From c739f437ddcf48af1fb5719f0034fe1ef45f31b0 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 25 Jan 2021 10:36:21 +0200
Subject: [PATCH 01/46] Update dependency vega to ^5.19.1 (#88984)

Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
 package.json |  2 +-
 yarn.lock    | 60 +++++++++++++++++++++++++++++++---------------------
 2 files changed, 37 insertions(+), 25 deletions(-)

diff --git a/package.json b/package.json
index 87e0f84695235..2fdc31820b9d4 100644
--- a/package.json
+++ b/package.json
@@ -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/yarn.lock b/yarn.lock
index cc32349b10860..828a3b630a838 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -28645,10 +28645,10 @@ vega-functions@^5.10.0:
     vega-time "^2.0.4"
     vega-util "^1.16.0"
 
-vega-functions@~5.11.0:
-  version "5.11.0"
-  resolved "https://registry.yarnpkg.com/vega-functions/-/vega-functions-5.11.0.tgz#a590d016f93c81730bdbc336b377231d7ae48569"
-  integrity sha512-/p0QIDiA3RaUZ7drxHuClpDQCrIScSHJlY0oo0+GFYGfp+lvb29Ox1T4a+wtqeCp6NRaTWry+EwDxojnshTZIQ==
+vega-functions@^5.12.0, vega-functions@~5.12.0:
+  version "5.12.0"
+  resolved "https://registry.yarnpkg.com/vega-functions/-/vega-functions-5.12.0.tgz#44bf08a7b20673dc8cf51d6781c8ea1399501668"
+  integrity sha512-3hljmGs+gR7TbO/yYuvAP9P5laKISf1GKk4yRHLNdM61fWgKm8pI3f6LY2Hvq9cHQFTiJ3/5/Bx2p1SX5R4quQ==
   dependencies:
     d3-array "^2.7.1"
     d3-color "^2.0.0"
@@ -28656,8 +28656,8 @@ vega-functions@~5.11.0:
     vega-dataflow "^5.7.3"
     vega-expression "^4.0.1"
     vega-scale "^7.1.1"
-    vega-scenegraph "^4.9.2"
-    vega-selections "^5.2.0"
+    vega-scenegraph "^4.9.3"
+    vega-selections "^5.3.0"
     vega-statistics "^1.7.9"
     vega-time "^2.0.4"
     vega-util "^1.16.0"
@@ -28724,16 +28724,16 @@ vega-loader@^4.3.2, vega-loader@^4.3.3, vega-loader@~4.4.0:
     vega-format "^1.0.4"
     vega-util "^1.16.0"
 
-vega-parser@~6.1.2:
-  version "6.1.2"
-  resolved "https://registry.yarnpkg.com/vega-parser/-/vega-parser-6.1.2.tgz#7f25751177e38c3239560a9c427ded8d2ba617bb"
-  integrity sha512-aGyZrNzPrBruEb/WhemKDuDjQsIkMDGIgnSJci0b+9ZVxjyAzMl7UfGbiYorPiJlnIercjUJbMoFD6fCIf4gqQ==
+vega-parser@~6.1.3:
+  version "6.1.3"
+  resolved "https://registry.yarnpkg.com/vega-parser/-/vega-parser-6.1.3.tgz#df72785e4b086eceb90ee6219a399210933b507b"
+  integrity sha512-8oiVhhW26GQ4GZBvolId8FVFvhn3s1KGgPlD7Z+4P2wkV+xe5Nqu0TEJ20F/cn3b88fd0Vj48X3BH3dlSeKNFg==
   dependencies:
     vega-dataflow "^5.7.3"
     vega-event-selector "^2.0.6"
-    vega-functions "^5.10.0"
+    vega-functions "^5.12.0"
     vega-scale "^7.1.1"
-    vega-util "^1.15.2"
+    vega-util "^1.16.0"
 
 vega-projection@^1.4.5, vega-projection@~1.4.5:
   version "1.4.5"
@@ -28772,7 +28772,7 @@ vega-scale@^7.0.3, vega-scale@^7.1.1, vega-scale@~7.1.1:
     vega-time "^2.0.4"
     vega-util "^1.15.2"
 
-vega-scenegraph@^4.9.2, vega-scenegraph@~4.9.2:
+vega-scenegraph@^4.9.2:
   version "4.9.2"
   resolved "https://registry.yarnpkg.com/vega-scenegraph/-/vega-scenegraph-4.9.2.tgz#83b1dbc34a9ab5595c74d547d6d95849d74451ed"
   integrity sha512-epm1CxcB8AucXQlSDeFnmzy0FCj+HV2k9R6ch2lfLRln5lPLEfgJWgFcFhVf5jyheY0FSeHH52Q5zQn1vYI1Ow==
@@ -28784,6 +28784,18 @@ vega-scenegraph@^4.9.2, vega-scenegraph@~4.9.2:
     vega-scale "^7.1.1"
     vega-util "^1.15.2"
 
+vega-scenegraph@^4.9.3, vega-scenegraph@~4.9.3:
+  version "4.9.3"
+  resolved "https://registry.yarnpkg.com/vega-scenegraph/-/vega-scenegraph-4.9.3.tgz#c4720550ea7ff5c8d9d0690f47fe2640547cfc6b"
+  integrity sha512-lBvqLbXqrqRCTGJmSgzZC/tLR/o+TXfakbdhDzNdpgTavTaQ65S/67Gpj5hPpi77DvsfZUIY9lCEeO37aJhy0Q==
+  dependencies:
+    d3-path "^2.0.0"
+    d3-shape "^2.0.0"
+    vega-canvas "^1.2.5"
+    vega-loader "^4.3.3"
+    vega-scale "^7.1.1"
+    vega-util "^1.15.2"
+
 vega-schema-url-parser@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/vega-schema-url-parser/-/vega-schema-url-parser-2.1.0.tgz#847f9cf9f1624f36f8a51abc1adb41ebc6673cb4"
@@ -28797,10 +28809,10 @@ vega-selections@^5.1.5:
     vega-expression "^4.0.0"
     vega-util "^1.15.2"
 
-vega-selections@^5.2.0:
-  version "5.2.0"
-  resolved "https://registry.yarnpkg.com/vega-selections/-/vega-selections-5.2.0.tgz#d85968d1bccc175fd92661c91d88151ffd5ade83"
-  integrity sha512-Xf3nTTJHRGw4tQMbt+0sBI/7WkEIzPG9E4HXkZk5Y9Q2HsGRVLmrAEXHSfpENrBLWTBZk/uvmP9rKDG7cbcTrg==
+vega-selections@^5.3.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/vega-selections/-/vega-selections-5.3.0.tgz#810f2e7b7642fa836cf98b2e5dcc151093b1f6a7"
+  integrity sha512-vC4NPsuN+IffruFXfH0L3i2A51RgG4PqpLv85TvrEAIYnSkyKDE4bf+wVraR3aPdnLLkc3+tYuMi6le5FmThIA==
   dependencies:
     vega-expression "^4.0.1"
     vega-util "^1.16.0"
@@ -28899,10 +28911,10 @@ vega-wordcloud@~4.1.3:
     vega-statistics "^1.7.9"
     vega-util "^1.15.2"
 
-vega@^5.18.0:
-  version "5.18.0"
-  resolved "https://registry.yarnpkg.com/vega/-/vega-5.18.0.tgz#98645e5d3bd5267d66ea3e701d99dcff63cfff8a"
-  integrity sha512-ysqouhboWNXSuQNN7W5IGOXsnEJNFVX5duCi0tTwRsFLc61FshpqVh4+4VoXg5pH0ZCxwpqbOwd2ULZWjJTx6g==
+vega@^5.19.1:
+  version "5.19.1"
+  resolved "https://registry.yarnpkg.com/vega/-/vega-5.19.1.tgz#64c8350740fe1a11d56cc6617ab3a76811fd704c"
+  integrity sha512-UE6/c9q9kzuz4HULFuU9HscBASoZa+zcXqGKdbQP545Nwmhd078QpcH+wZsq9lYfiTxmFtzLK/a0OH0zhkghvA==
   dependencies:
     vega-crossfilter "~4.0.5"
     vega-dataflow "~5.7.3"
@@ -28911,17 +28923,17 @@ vega@^5.18.0:
     vega-expression "~4.0.1"
     vega-force "~4.0.7"
     vega-format "~1.0.4"
-    vega-functions "~5.11.0"
+    vega-functions "~5.12.0"
     vega-geo "~4.3.8"
     vega-hierarchy "~4.0.9"
     vega-label "~1.0.0"
     vega-loader "~4.4.0"
-    vega-parser "~6.1.2"
+    vega-parser "~6.1.3"
     vega-projection "~1.4.5"
     vega-regression "~1.0.9"
     vega-runtime "~6.1.3"
     vega-scale "~7.1.1"
-    vega-scenegraph "~4.9.2"
+    vega-scenegraph "~4.9.3"
     vega-statistics "~1.7.9"
     vega-time "~2.0.4"
     vega-transforms "~4.9.3"

From 02f24de89994c34dcc75ba30c44c56731ffdbe13 Mon Sep 17 00:00:00 2001
From: Joe Reuter <johannes.reuter@elastic.co>
Date: Mon, 25 Jan 2021 11:25:46 +0100
Subject: [PATCH 02/46] [Lens] Add drag drop tests (#88660)

---
 .../functional/apps/lens/drag_and_drop.ts     | 153 +++--
 .../es_archives/lens/basic/data.json          | 577 ++++++++++++++++++
 .../test/functional/page_objects/lens_page.ts |   3 +
 3 files changed, 674 insertions(+), 59 deletions(-)
 create mode 100644 x-pack/test/functional/es_archives/lens/basic/data.json

diff --git a/x-pack/test/functional/apps/lens/drag_and_drop.ts b/x-pack/test/functional/apps/lens/drag_and_drop.ts
index e0130bc394271..57e5990a74012 100644
--- a/x-pack/test/functional/apps/lens/drag_and_drop.ts
+++ b/x-pack/test/functional/apps/lens/drag_and_drop.ts
@@ -11,73 +11,108 @@ export default function ({ getPageObjects }: FtrProviderContext) {
   const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']);
 
   describe('lens drag and drop tests', () => {
-    it('should construct the basic split xy chart', async () => {
-      await PageObjects.visualize.navigateToNewVisualization();
-      await PageObjects.visualize.clickVisType('lens');
-      await PageObjects.lens.goToTimeRange();
-      await PageObjects.header.waitUntilLoadingHasFinished();
-      await PageObjects.lens.dragFieldToWorkspace('@timestamp');
+    describe('basic drag and drop', () => {
+      it('should construct the basic split xy chart', async () => {
+        await PageObjects.visualize.navigateToNewVisualization();
+        await PageObjects.visualize.clickVisType('lens');
+        await PageObjects.lens.goToTimeRange();
+        await PageObjects.header.waitUntilLoadingHasFinished();
+        await PageObjects.lens.dragFieldToWorkspace('@timestamp');
 
-      expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_xDimensionPanel')).to.eql(
-        '@timestamp'
-      );
-    });
+        expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_xDimensionPanel')).to.eql(
+          '@timestamp'
+        );
+      });
 
-    it('should allow dropping fields to existing and empty dimension triggers', async () => {
-      await PageObjects.lens.switchToVisualization('lnsDatatable');
+      it('should allow dropping fields to existing and empty dimension triggers', async () => {
+        await PageObjects.lens.switchToVisualization('lnsDatatable');
 
-      await PageObjects.lens.dragFieldToDimensionTrigger(
-        'clientip',
-        'lnsDatatable_column > lns-dimensionTrigger'
-      );
-      expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_column')).to.eql(
-        'Top values of clientip'
-      );
+        await PageObjects.lens.dragFieldToDimensionTrigger(
+          'clientip',
+          'lnsDatatable_column > lns-dimensionTrigger'
+        );
+        expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_column')).to.eql(
+          'Top values of clientip'
+        );
 
-      await PageObjects.lens.dragFieldToDimensionTrigger(
-        'bytes',
-        'lnsDatatable_column > lns-empty-dimension'
-      );
-      expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_column', 1)).to.eql(
-        'bytes'
-      );
-      await PageObjects.lens.dragFieldToDimensionTrigger(
-        '@message.raw',
-        'lnsDatatable_column > lns-empty-dimension'
-      );
-      expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_column', 2)).to.eql(
-        'Top values of @message.raw'
-      );
-    });
+        await PageObjects.lens.dragFieldToDimensionTrigger(
+          'bytes',
+          'lnsDatatable_column > lns-empty-dimension'
+        );
+        expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_column', 1)).to.eql(
+          'bytes'
+        );
+        await PageObjects.lens.dragFieldToDimensionTrigger(
+          '@message.raw',
+          'lnsDatatable_column > lns-empty-dimension'
+        );
+        expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_column', 2)).to.eql(
+          'Top values of @message.raw'
+        );
+      });
 
-    it('should reorder the elements for the table', async () => {
-      await PageObjects.lens.reorderDimensions('lnsDatatable_column', 2, 0);
-      await PageObjects.header.waitUntilLoadingHasFinished();
-      expect(await PageObjects.lens.getDimensionTriggersTexts('lnsDatatable_column')).to.eql([
-        'Top values of @message.raw',
-        'Top values of clientip',
-        'bytes',
-      ]);
-    });
+      it('should reorder the elements for the table', async () => {
+        await PageObjects.lens.reorderDimensions('lnsDatatable_column', 2, 0);
+        await PageObjects.header.waitUntilLoadingHasFinished();
+        expect(await PageObjects.lens.getDimensionTriggersTexts('lnsDatatable_column')).to.eql([
+          'Top values of @message.raw',
+          'Top values of clientip',
+          'bytes',
+        ]);
+      });
 
-    it('should move the column to compatible dimension group', async () => {
-      await PageObjects.lens.switchToVisualization('bar');
-      expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_xDimensionPanel')).to.eql([
-        'Top values of @message.raw',
-      ]);
-      expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_splitDimensionPanel')).to.eql([
-        'Top values of clientip',
-      ]);
+      it('should move the column to compatible dimension group', async () => {
+        await PageObjects.lens.switchToVisualization('bar');
+        expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_xDimensionPanel')).to.eql([
+          'Top values of @message.raw',
+        ]);
+        expect(
+          await PageObjects.lens.getDimensionTriggersTexts('lnsXY_splitDimensionPanel')
+        ).to.eql(['Top values of clientip']);
+
+        await PageObjects.lens.dragDimensionToDimension(
+          'lnsXY_xDimensionPanel > lns-dimensionTrigger',
+          'lnsXY_splitDimensionPanel > lns-dimensionTrigger'
+        );
+
+        expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_xDimensionPanel')).to.eql(
+          []
+        );
+        expect(
+          await PageObjects.lens.getDimensionTriggersTexts('lnsXY_splitDimensionPanel')
+        ).to.eql(['Top values of @message.raw']);
+      });
+    });
 
-      await PageObjects.lens.dragDimensionToDimension(
-        'lnsXY_xDimensionPanel > lns-dimensionTrigger',
-        'lnsXY_splitDimensionPanel > lns-dimensionTrigger'
-      );
+    describe('workspace drop', () => {
+      it('should always nest time dimension in categorical dimension', async () => {
+        await PageObjects.visualize.navigateToNewVisualization();
+        await PageObjects.visualize.clickVisType('lens');
+        await PageObjects.lens.goToTimeRange();
+        await PageObjects.header.waitUntilLoadingHasFinished();
+        await PageObjects.lens.dragFieldToWorkspace('@timestamp');
+        await PageObjects.header.waitUntilLoadingHasFinished();
+        await PageObjects.lens.dragFieldToWorkspace('clientip');
+        await PageObjects.header.waitUntilLoadingHasFinished();
+        expect(
+          await PageObjects.lens.getDimensionTriggersTexts('lnsXY_splitDimensionPanel')
+        ).to.eql(['Top values of clientip']);
+        await PageObjects.lens.openDimensionEditor(
+          'lnsXY_splitDimensionPanel > lns-dimensionTrigger'
+        );
+        expect(await PageObjects.lens.isTopLevelAggregation()).to.be(true);
+        await PageObjects.lens.closeDimensionEditor();
+      });
 
-      expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_xDimensionPanel')).to.eql([]);
-      expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_splitDimensionPanel')).to.eql([
-        'Top values of @message.raw',
-      ]);
+      it('overwrite existing time dimension if one exists already', async () => {
+        await PageObjects.lens.dragFieldToWorkspace('utc_time');
+        await PageObjects.header.waitUntilLoadingHasFinished();
+        await PageObjects.lens.dragFieldToWorkspace('clientip');
+        await PageObjects.header.waitUntilLoadingHasFinished();
+        expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_xDimensionPanel')).to.eql([
+          'utc_time',
+        ]);
+      });
     });
   });
 }
diff --git a/x-pack/test/functional/es_archives/lens/basic/data.json b/x-pack/test/functional/es_archives/lens/basic/data.json
new file mode 100644
index 0000000000000..a985de882929d
--- /dev/null
+++ b/x-pack/test/functional/es_archives/lens/basic/data.json
@@ -0,0 +1,577 @@
+{
+  "type": "doc",
+  "value": {
+    "id": "space:default",
+    "index": ".kibana_1",
+    "source": {
+      "migrationVersion": {
+        "space": "6.6.0"
+      },
+      "references": [],
+      "space": {
+        "_reserved": true,
+        "description": "This is the default space!",
+        "disabledFeatures": [],
+        "name": "Default"
+      },
+      "type": "space"
+    },
+    "type": "_doc"
+  }
+}
+
+{
+  "type": "doc",
+  "value": {
+    "id": "index-pattern:logstash-*",
+    "index": ".kibana_1",
+    "source": {
+      "index-pattern": {
+        "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]",
+        "timeFieldName": "@timestamp",
+        "title": "logstash-*"
+      },
+      "migrationVersion": {
+        "index-pattern": "6.5.0"
+      },
+      "references": [],
+      "type": "index-pattern",
+      "updated_at": "2018-12-21T00:43:07.096Z"
+    },
+    "type": "_doc"
+  }
+}
+
+{
+  "type": "doc",
+  "value": {
+    "id": "index-pattern:log*",
+    "index": ".kibana_1",
+    "source": {
+      "index-pattern": {
+        "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]",
+        "timeFieldName": "@timestamp",
+        "title": "log*"
+      },
+      "migrationVersion": {
+        "index-pattern": "6.5.0"
+      },
+      "references": [],
+      "type": "index-pattern",
+      "updated_at": "2018-12-21T00:43:07.096Z"
+    },
+    "type": "_doc"
+  }
+}
+
+
+{
+  "type": "doc",
+  "value": {
+    "id": "custom_space:index-pattern:logstash-*",
+    "index": ".kibana_1",
+    "source": {
+      "index-pattern": {
+        "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]",
+        "timeFieldName": "@timestamp",
+        "title": "logstash-*"
+      },
+      "migrationVersion": {
+        "index-pattern": "6.5.0"
+      },
+      "namespace": "custom_space",
+      "references": [],
+      "type": "index-pattern",
+      "updated_at": "2018-12-21T00:43:07.096Z"
+    },
+    "type": "_doc"
+  }
+}
+
+{
+  "type": "doc",
+  "value": {
+    "id": "visualization:i-exist",
+    "index": ".kibana_1",
+    "source": {
+      "migrationVersion": {
+        "visualization": "7.3.1"
+      },
+      "references": [
+        {
+          "id": "logstash-*",
+          "name": "kibanaSavedObjectMeta.searchSourceJSON.index",
+          "type": "index-pattern"
+        }
+      ],
+      "type": "visualization",
+      "updated_at": "2019-01-22T19:32:31.206Z",
+      "visualization": {
+        "description": "",
+        "kibanaSavedObjectMeta": {
+          "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"
+        },
+        "title": "A Pie",
+        "uiStateJSON": "{}",
+        "version": 1,
+        "visState": "{\"title\":\"A Pie\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"dimensions\":{\"metric\":{\"accessor\":0,\"format\":{\"id\":\"number\"},\"params\":{},\"aggType\":\"count\"}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"
+      }
+    },
+    "type": "_doc"
+  }
+}
+
+{
+  "type": "doc",
+  "value": {
+    "id": "custom_space:visualization:i-exist",
+    "index": ".kibana_1",
+    "source": {
+      "migrationVersion": {
+        "visualization": "7.3.1"
+      },
+      "namespace": "custom_space",
+      "references": [
+        {
+          "id": "logstash-*",
+          "name": "kibanaSavedObjectMeta.searchSourceJSON.index",
+          "type": "index-pattern"
+        }
+      ],
+      "type": "visualization",
+      "updated_at": "2019-01-22T19:32:31.206Z",
+      "visualization": {
+        "description": "",
+        "kibanaSavedObjectMeta": {
+          "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"
+        },
+        "title": "A Pie",
+        "uiStateJSON": "{}",
+        "version": 1,
+        "visState": "{\"title\":\"A Pie\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"dimensions\":{\"metric\":{\"accessor\":0,\"format\":{\"id\":\"number\"},\"params\":{},\"aggType\":\"count\"}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"
+      }
+    },
+    "type": "_doc"
+  }
+}
+
+{
+  "type": "doc",
+  "value": {
+    "id": "query:okjpgs",
+    "index": ".kibana_1",
+    "source": {
+      "query": {
+        "description": "Ok responses for jpg files",
+        "filters": [
+          {
+            "$state": {
+              "store": "appState"
+            },
+            "meta": {
+              "alias": null,
+              "disabled": false,
+              "index": "b15b1d40-a8bb-11e9-98cf-2bb06ef63e0b",
+              "key": "extension.raw",
+              "negate": false,
+              "params": {
+                "query": "jpg"
+              },
+              "type": "phrase",
+              "value": "jpg"
+            },
+            "query": {
+              "match": {
+                "extension.raw": {
+                  "query": "jpg",
+                  "type": "phrase"
+                }
+              }
+            }
+          }
+        ],
+        "query": {
+          "language": "kuery",
+          "query": "response:200"
+        },
+        "title": "OKJpgs"
+      },
+      "references": [],
+      "type": "query",
+      "updated_at": "2019-07-17T17:54:26.378Z"
+    },
+    "type": "_doc"
+  }
+}
+
+{
+  "type": "doc",
+  "value": {
+    "id": "config:8.0.0",
+    "index": ".kibana_1",
+    "source": {
+      "config": {
+        "accessibility:disableAnimations": true,
+        "buildNum": 9007199254740991,
+        "dateFormat:tz": "UTC",
+        "defaultIndex": "logstash-*"
+      },
+      "references": [],
+      "type": "config",
+      "updated_at": "2019-09-04T18:47:24.761Z"
+    },
+    "type": "_doc"
+  }
+}
+
+{
+  "type": "doc",
+  "value": {
+    "id": "lens:76fc4200-cf44-11e9-b933-fd84270f3ac1",
+    "index": ".kibana_1",
+    "source": {
+      "lens": {
+        "expression": "kibana\n| kibana_context  query=\"{\\\"language\\\":\\\"kuery\\\",\\\"query\\\":\\\"\\\"}\" filters=\"[]\"\n| lens_merge_tables layerIds=\"c61a8afb-a185-4fae-a064-fb3846f6c451\" \n  tables={esaggs index=\"logstash-*\" metricsAtAllLevels=false partialRows=false includeFormatHints=true aggConfigs={lens_auto_date aggConfigs=\"[{\\\"id\\\":\\\"2cd09808-3915-49f4-b3b0-82767eba23f7\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"max\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"bytes\\\"}}]\"} | lens_rename_columns idMap=\"{\\\"col-0-2cd09808-3915-49f4-b3b0-82767eba23f7\\\":{\\\"dataType\\\":\\\"number\\\",\\\"isBucketed\\\":false,\\\"label\\\":\\\"Maximum of bytes\\\",\\\"operationType\\\":\\\"max\\\",\\\"scale\\\":\\\"ratio\\\",\\\"sourceField\\\":\\\"bytes\\\",\\\"id\\\":\\\"2cd09808-3915-49f4-b3b0-82767eba23f7\\\"}}\"}\n| lens_metric_chart title=\"Maximum of bytes\" accessor=\"2cd09808-3915-49f4-b3b0-82767eba23f7\" mode=\"full\"",
+        "state": {
+          "datasourceMetaData": {
+            "filterableIndexPatterns": [
+              {
+                "id": "logstash-*",
+                "title": "logstash-*"
+              }
+            ]
+          },
+          "datasourceStates": {
+            "indexpattern": {
+              "currentIndexPatternId": "logstash-*",
+              "layers": {
+                "c61a8afb-a185-4fae-a064-fb3846f6c451": {
+                  "columnOrder": [
+                    "2cd09808-3915-49f4-b3b0-82767eba23f7"
+                  ],
+                  "columns": {
+                    "2cd09808-3915-49f4-b3b0-82767eba23f7": {
+                      "dataType": "number",
+                      "isBucketed": false,
+                      "label": "Maximum of bytes",
+                      "operationType": "max",
+                      "scale": "ratio",
+                      "sourceField": "bytes"
+                    }
+                  },
+                  "indexPatternId": "logstash-*"
+                }
+              }
+            }
+          },
+          "filters": [],
+          "query": {
+            "language": "kuery",
+            "query": ""
+          },
+          "visualization": {
+            "accessor": "2cd09808-3915-49f4-b3b0-82767eba23f7",
+            "isHorizontal": false,
+            "layerId": "c61a8afb-a185-4fae-a064-fb3846f6c451",
+            "layers": [
+              {
+                "accessors": [
+                  "d3e62a7a-c259-4fff-a2fc-eebf20b7008a",
+                  "26ef70a9-c837-444c-886e-6bd905ee7335"
+                ],
+                "layerId": "c61a8afb-a185-4fae-a064-fb3846f6c451",
+                "seriesType": "area",
+                "splitAccessor": "54cd64ed-2a44-4591-af84-b2624504569a",
+                "xAccessor": "d6e40cea-6299-43b4-9c9d-b4ee305a2ce8"
+              }
+            ],
+            "legend": {
+              "isVisible": true,
+              "position": "right"
+            },
+            "preferredSeriesType": "area"
+          }
+        },
+        "title": "Artistpreviouslyknownaslens",
+        "visualizationType": "lnsMetric"
+      },
+      "references": [],
+      "type": "lens",
+      "updated_at": "2019-10-16T00:28:08.979Z"
+    },
+    "type": "_doc"
+  }
+}
+
+{
+  "type": "doc",
+  "value": {
+    "id": "lens:9536bed0-d57e-11ea-b169-e3a222a76b9c",
+    "index": ".kibana_1",
+    "source": {
+      "lens": {
+        "expression": "kibana\n| kibana_context  query=\"{\\\"language\\\":\\\"kuery\\\",\\\"query\\\":\\\"\\\"}\" filters=\"[]\"\n| lens_merge_tables layerIds=\"4ba1a1be-6e67-434b-b3a0-f30db8ea5395\" \n  tables={esaggs index=\"logstash-*\" metricsAtAllLevels=true partialRows=true includeFormatHints=true  aggConfigs=\"[{\\\"id\\\":\\\"bafe3009-1776-4227-a0fe-b0d6ccbb4961\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"terms\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"geo.dest\\\",\\\"orderBy\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\",\\\"order\\\":\\\"desc\\\",\\\"size\\\":7,\\\"otherBucket\\\":false,\\\"otherBucketLabel\\\":\\\"Other\\\",\\\"missingBucket\\\":false,\\\"missingBucketLabel\\\":\\\"Missing\\\"}},{\\\"id\\\":\\\"c1ebe4c9-f283-486c-ae95-6b3e99e83bd8\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"terms\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"geo.src\\\",\\\"orderBy\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\",\\\"order\\\":\\\"desc\\\",\\\"size\\\":3,\\\"otherBucket\\\":false,\\\"otherBucketLabel\\\":\\\"Other\\\",\\\"missingBucket\\\":false,\\\"missingBucketLabel\\\":\\\"Missing\\\"}},{\\\"id\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"bytes\\\",\\\"missing\\\":0}}]\" | lens_rename_columns idMap=\"{\\\"col-0-bafe3009-1776-4227-a0fe-b0d6ccbb4961\\\":{\\\"dataType\\\":\\\"string\\\",\\\"isBucketed\\\":true,\\\"label\\\":\\\"Top values of geo.dest\\\",\\\"operationType\\\":\\\"terms\\\",\\\"params\\\":{\\\"orderBy\\\":{\\\"columnId\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\",\\\"type\\\":\\\"column\\\"},\\\"orderDirection\\\":\\\"desc\\\",\\\"size\\\":7},\\\"scale\\\":\\\"ordinal\\\",\\\"sourceField\\\":\\\"geo.dest\\\",\\\"id\\\":\\\"bafe3009-1776-4227-a0fe-b0d6ccbb4961\\\"},\\\"col-2-c1ebe4c9-f283-486c-ae95-6b3e99e83bd8\\\":{\\\"label\\\":\\\"Top values of geo.src\\\",\\\"dataType\\\":\\\"string\\\",\\\"operationType\\\":\\\"terms\\\",\\\"scale\\\":\\\"ordinal\\\",\\\"sourceField\\\":\\\"geo.src\\\",\\\"isBucketed\\\":true,\\\"params\\\":{\\\"size\\\":3,\\\"orderBy\\\":{\\\"type\\\":\\\"column\\\",\\\"columnId\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\"},\\\"orderDirection\\\":\\\"desc\\\"},\\\"id\\\":\\\"c1ebe4c9-f283-486c-ae95-6b3e99e83bd8\\\"},\\\"col-3-3dc0bd55-2087-4e60-aea2-f9910714f7db\\\":{\\\"dataType\\\":\\\"number\\\",\\\"isBucketed\\\":false,\\\"label\\\":\\\"Average of bytes\\\",\\\"operationType\\\":\\\"avg\\\",\\\"scale\\\":\\\"ratio\\\",\\\"sourceField\\\":\\\"bytes\\\",\\\"id\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\"}}\"}\n| lens_pie shape=\"pie\" hideLabels=false groups=\"bafe3009-1776-4227-a0fe-b0d6ccbb4961\"\n  groups=\"c1ebe4c9-f283-486c-ae95-6b3e99e83bd8\" metric=\"3dc0bd55-2087-4e60-aea2-f9910714f7db\" numberDisplay=\"percent\" categoryDisplay=\"default\" legendDisplay=\"default\" legendPosition=\"right\" percentDecimals=3 nestedLegend=false",
+        "state": {
+          "datasourceMetaData": {
+            "filterableIndexPatterns": [
+              {
+                "id": "logstash-*",
+                "title": "logstash-*"
+              }
+            ]
+          },
+          "datasourceStates": {
+            "indexpattern": {
+              "currentIndexPatternId": "logstash-*",
+              "layers": {
+                "4ba1a1be-6e67-434b-b3a0-f30db8ea5395": {
+                  "columnOrder": [
+                    "bafe3009-1776-4227-a0fe-b0d6ccbb4961",
+                    "c1ebe4c9-f283-486c-ae95-6b3e99e83bd8",
+                    "3dc0bd55-2087-4e60-aea2-f9910714f7db"
+                  ],
+                  "columns": {
+                    "3dc0bd55-2087-4e60-aea2-f9910714f7db": {
+                      "dataType": "number",
+                      "isBucketed": false,
+                      "label": "Average of bytes",
+                      "operationType": "avg",
+                      "scale": "ratio",
+                      "sourceField": "bytes"
+                    },
+                    "5bd1c078-e1dd-465b-8d25-7a6404befa88": {
+                      "dataType": "date",
+                      "isBucketed": true,
+                      "label": "@timestamp",
+                      "operationType": "date_histogram",
+                      "params": {
+                        "interval": "auto"
+                      },
+                      "scale": "interval",
+                      "sourceField": "@timestamp"
+                    },
+                    "65340cf3-8402-4494-96f2-293701c59571": {
+                      "dataType": "number",
+                      "isBucketed": true,
+                      "label": "Top values of bytes",
+                      "operationType": "terms",
+                      "params": {
+                        "orderBy": {
+                          "columnId": "3dc0bd55-2087-4e60-aea2-f9910714f7db",
+                          "type": "column"
+                        },
+                        "orderDirection": "desc",
+                        "size": 3
+                      },
+                      "scale": "ordinal",
+                      "sourceField": "bytes"
+                    },
+                    "87554e1d-3dbf-4c1c-a358-4c9d40424cfa": {
+                      "dataType": "string",
+                      "isBucketed": true,
+                      "label": "Top values of type",
+                      "operationType": "terms",
+                      "params": {
+                        "orderBy": {
+                          "columnId": "3dc0bd55-2087-4e60-aea2-f9910714f7db",
+                          "type": "column"
+                        },
+                        "orderDirection": "desc",
+                        "size": 3
+                      },
+                      "scale": "ordinal",
+                      "sourceField": "type"
+                    },
+                    "bafe3009-1776-4227-a0fe-b0d6ccbb4961": {
+                      "dataType": "string",
+                      "isBucketed": true,
+                      "label": "Top values of geo.dest",
+                      "operationType": "terms",
+                      "params": {
+                        "orderBy": {
+                          "columnId": "3dc0bd55-2087-4e60-aea2-f9910714f7db",
+                          "type": "column"
+                        },
+                        "orderDirection": "desc",
+                        "size": 7
+                      },
+                      "scale": "ordinal",
+                      "sourceField": "geo.dest"
+                    },
+                    "c1ebe4c9-f283-486c-ae95-6b3e99e83bd8": {
+                      "dataType": "string",
+                      "isBucketed": true,
+                      "label": "Top values of geo.src",
+                      "operationType": "terms",
+                      "params": {
+                        "orderBy": {
+                          "columnId": "3dc0bd55-2087-4e60-aea2-f9910714f7db",
+                          "type": "column"
+                        },
+                        "orderDirection": "desc",
+                        "size": 3
+                      },
+                      "scale": "ordinal",
+                      "sourceField": "geo.src"
+                    }
+                  },
+                  "indexPatternId": "logstash-*"
+                }
+              }
+            }
+          },
+          "filters": [],
+          "query": {
+            "language": "kuery",
+            "query": ""
+          },
+          "visualization": {
+            "layers": [
+              {
+                "categoryDisplay": "default",
+                "groups": [
+                  "bafe3009-1776-4227-a0fe-b0d6ccbb4961",
+                  "c1ebe4c9-f283-486c-ae95-6b3e99e83bd8"
+                ],
+                "layerId": "4ba1a1be-6e67-434b-b3a0-f30db8ea5395",
+                "legendDisplay": "default",
+                "metric": "3dc0bd55-2087-4e60-aea2-f9910714f7db",
+                "nestedLegend": false,
+                "numberDisplay": "percent"
+              }
+            ],
+            "shape": "pie"
+          }
+        },
+        "title": "lnsPieVis",
+        "visualizationType": "lnsPie"
+      },
+      "references": [],
+      "type": "lens",
+      "updated_at": "2020-08-03T11:43:43.421Z"
+    },
+    "type": "_doc"
+  }
+}
+
+{
+  "type": "doc",
+  "value": {
+    "id": "lens:76fc4200-cf44-11e9-b933-fd84270f3ac2",
+    "index": ".kibana_1",
+    "source": {
+      "lens": {
+        "expression": "kibana\n| kibana_context  query=\"{\\\"query\\\":\\\"\\\",\\\"language\\\":\\\"kuery\\\"}\" filters=\"[]\"\n| lens_merge_tables layerIds=\"4ba1a1be-6e67-434b-b3a0-f30db8ea5395\" \n  tables={esaggs index=\"logstash-*\" metricsAtAllLevels=false partialRows=false includeFormatHints=true aggConfigs={lens_auto_date aggConfigs=\"[{\\\"id\\\":\\\"7a5d833b-ca6f-4e48-a924-d2a28d365dc3\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"terms\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"ip\\\",\\\"orderBy\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\",\\\"order\\\":\\\"desc\\\",\\\"size\\\":3,\\\"otherBucket\\\":false,\\\"otherBucketLabel\\\":\\\"Other\\\",\\\"missingBucket\\\":false,\\\"missingBucketLabel\\\":\\\"Missing\\\"}},{\\\"id\\\":\\\"3cf18f28-3495-4d45-a55f-d97f88022099\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"date_histogram\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"@timestamp\\\",\\\"useNormalizedEsInterval\\\":true,\\\"interval\\\":\\\"auto\\\",\\\"drop_partials\\\":false,\\\"min_doc_count\\\":0,\\\"extended_bounds\\\":{}}},{\\\"id\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"bytes\\\",\\\"missing\\\":0}}]\"} | lens_rename_columns idMap=\"{\\\"col-0-7a5d833b-ca6f-4e48-a924-d2a28d365dc3\\\":{\\\"label\\\":\\\"Top values of ip\\\",\\\"dataType\\\":\\\"ip\\\",\\\"operationType\\\":\\\"terms\\\",\\\"scale\\\":\\\"ordinal\\\",\\\"suggestedPriority\\\":0,\\\"sourceField\\\":\\\"ip\\\",\\\"isBucketed\\\":true,\\\"params\\\":{\\\"size\\\":3,\\\"orderBy\\\":{\\\"type\\\":\\\"column\\\",\\\"columnId\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\"},\\\"orderDirection\\\":\\\"desc\\\"},\\\"id\\\":\\\"7a5d833b-ca6f-4e48-a924-d2a28d365dc3\\\"},\\\"col-1-3cf18f28-3495-4d45-a55f-d97f88022099\\\":{\\\"label\\\":\\\"@timestamp\\\",\\\"dataType\\\":\\\"date\\\",\\\"operationType\\\":\\\"date_histogram\\\",\\\"suggestedPriority\\\":1,\\\"sourceField\\\":\\\"@timestamp\\\",\\\"isBucketed\\\":true,\\\"scale\\\":\\\"interval\\\",\\\"params\\\":{\\\"interval\\\":\\\"auto\\\"},\\\"id\\\":\\\"3cf18f28-3495-4d45-a55f-d97f88022099\\\"},\\\"col-2-3dc0bd55-2087-4e60-aea2-f9910714f7db\\\":{\\\"label\\\":\\\"Average of bytes\\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"bytes\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"id\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\"}}\"}\n| lens_xy_chart xTitle=\"@timestamp\" yTitle=\"Average of bytes\" legend={lens_xy_legendConfig isVisible=true position=\"right\"} \n  layers={lens_xy_layer layerId=\"4ba1a1be-6e67-434b-b3a0-f30db8ea5395\" hide=false xAccessor=\"3cf18f28-3495-4d45-a55f-d97f88022099\" yScaleType=\"linear\" xScaleType=\"time\" isHistogram=true splitAccessor=\"7a5d833b-ca6f-4e48-a924-d2a28d365dc3\" seriesType=\"bar_stacked\" accessors=\"3dc0bd55-2087-4e60-aea2-f9910714f7db\" columnToLabel=\"{\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\":\\\"Average of bytes\\\",\\\"7a5d833b-ca6f-4e48-a924-d2a28d365dc3\\\":\\\"Top values of ip\\\"}\"}",
+        "state": {
+          "datasourceMetaData": {
+            "filterableIndexPatterns": [
+              {
+                "id": "logstash-*",
+                "title": "logstash-*"
+              }
+            ]
+          },
+          "datasourceStates": {
+            "indexpattern": {
+              "currentIndexPatternId": "logstash-*",
+              "layers": {
+                "4ba1a1be-6e67-434b-b3a0-f30db8ea5395": {
+                  "columnOrder": [
+                    "7a5d833b-ca6f-4e48-a924-d2a28d365dc3",
+                    "3cf18f28-3495-4d45-a55f-d97f88022099",
+                    "3dc0bd55-2087-4e60-aea2-f9910714f7db"
+                  ],
+                  "columns": {
+                    "3cf18f28-3495-4d45-a55f-d97f88022099": {
+                      "dataType": "date",
+                      "isBucketed": true,
+                      "label": "@timestamp",
+                      "operationType": "date_histogram",
+                      "params": {
+                        "interval": "auto"
+                      },
+                      "scale": "interval",
+                      "sourceField": "@timestamp",
+                      "suggestedPriority": 1
+                    },
+                    "3dc0bd55-2087-4e60-aea2-f9910714f7db": {
+                      "dataType": "number",
+                      "isBucketed": false,
+                      "label": "Average of bytes",
+                      "operationType": "avg",
+                      "scale": "ratio",
+                      "sourceField": "bytes"
+                    },
+                    "7a5d833b-ca6f-4e48-a924-d2a28d365dc3": {
+                      "dataType": "ip",
+                      "isBucketed": true,
+                      "label": "Top values of ip",
+                      "operationType": "terms",
+                      "params": {
+                        "orderBy": {
+                          "columnId": "3dc0bd55-2087-4e60-aea2-f9910714f7db",
+                          "type": "column"
+                        },
+                        "orderDirection": "desc",
+                        "size": 3
+                      },
+                      "scale": "ordinal",
+                      "sourceField": "ip",
+                      "suggestedPriority": 0
+                    }
+                  },
+                  "indexPatternId": "logstash-*"
+                }
+              }
+            }
+          },
+          "filters": [],
+          "query": {
+            "language": "kuery",
+            "query": ""
+          },
+          "visualization": {
+            "layers": [
+              {
+                "accessors": [
+                  "3dc0bd55-2087-4e60-aea2-f9910714f7db"
+                ],
+                "layerId": "4ba1a1be-6e67-434b-b3a0-f30db8ea5395",
+                "seriesType": "bar_stacked",
+                "splitAccessor": "7a5d833b-ca6f-4e48-a924-d2a28d365dc3",
+                "xAccessor": "3cf18f28-3495-4d45-a55f-d97f88022099"
+              }
+            ],
+            "legend": {
+              "isVisible": true,
+              "position": "right"
+            },
+            "preferredSeriesType": "bar_stacked"
+          }
+        },
+        "title": "lnsXYvis",
+        "visualizationType": "lnsXY"
+      },
+      "references": [],
+      "type": "lens",
+      "updated_at": "2019-10-16T00:28:08.979Z"
+    },
+    "type": "_doc"
+  }
+}
+
+{
+  "type": "doc",
+  "value": {
+    "id": "ui-metric:DashboardPanelVersionInUrl:8.0.0",
+    "index": ".kibana_1",
+    "source": {
+      "type": "ui-metric",
+      "ui-metric": {
+        "count": 1
+      },
+      "updated_at": "2019-10-16T00:28:24.399Z"
+    },
+    "type": "_doc"
+  }
+}
diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts
index 04c660847bcee..31a4d6e29fc35 100644
--- a/x-pack/test/functional/page_objects/lens_page.ts
+++ b/x-pack/test/functional/page_objects/lens_page.ts
@@ -241,6 +241,9 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
       });
     },
 
+    async isTopLevelAggregation() {
+      return await testSubjects.isEuiSwitchChecked('indexPattern-nesting-switch');
+    },
     /**
      * Removes the dimension matching a specific test subject
      */

From 9d7cac76f5cfb72b9a937165ba53752e1e14c0c5 Mon Sep 17 00:00:00 2001
From: Joe Reuter <johannes.reuter@elastic.co>
Date: Mon, 25 Jan 2021 11:26:30 +0100
Subject: [PATCH 03/46] increase timeout on graph test for cloud (#88612)

---
 x-pack/test/functional/page_objects/graph_page.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/x-pack/test/functional/page_objects/graph_page.ts b/x-pack/test/functional/page_objects/graph_page.ts
index 9ce1f87b5bf3d..8dda82e4e08bb 100644
--- a/x-pack/test/functional/page_objects/graph_page.ts
+++ b/x-pack/test/functional/page_objects/graph_page.ts
@@ -196,7 +196,7 @@ export function GraphPageProvider({ getService, getPageObjects }: FtrProviderCon
       await testSubjects.click('confirmSaveSavedObjectButton');
 
       // Confirm that the Graph has been saved.
-      return await testSubjects.exists('saveGraphSuccess');
+      return await testSubjects.exists('saveGraphSuccess', { timeout: 10000 });
     }
 
     async getSearchFilter() {

From 403021fcabac0bf4416e3c2dd0735cd565068ec0 Mon Sep 17 00:00:00 2001
From: Anton Dosov <anton.dosov@elastic.co>
Date: Mon, 25 Jan 2021 11:28:10 +0100
Subject: [PATCH 04/46] [Search Sessions] omit searchSessionId from the
 initialState, explicitly pause refreshInterval in restoreState (#88650)

---
 .../lib/session_restoration.test.ts           | 60 +++++++++++++++++++
 .../application/lib/session_restoration.ts    | 21 ++++---
 .../angular/discover_state.test.ts            | 29 ++++++++-
 .../application/angular/discover_state.ts     | 15 ++---
 4 files changed, 106 insertions(+), 19 deletions(-)
 create mode 100644 src/plugins/dashboard/public/application/lib/session_restoration.test.ts

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/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<AppState>;
   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,

From 2ff523556d6b1cc38943369889dbbc3a5f2b6e7a Mon Sep 17 00:00:00 2001
From: Stratoula Kalafateli <efstratia.kalafateli@elastic.co>
Date: Mon, 25 Jan 2021 12:30:49 +0200
Subject: [PATCH 05/46] [XY Axis] Fix bug on percentiles and percentiles ranks
 (#88576)

* [XY Axis] Fix bug on percentiles and percentiles ranks

* Add unit tests to renderAllSeries

* make it simpler

* Minor change on test

* Fix license headers

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
 .../utils/render_all_series.test.mocks.ts     | 386 ++++++++++++++++++
 .../public/utils/render_all_series.test.tsx   | 132 ++++++
 .../public/utils/render_all_series.tsx        |  18 +-
 .../vis_type_xy/public/vis_component.tsx      |  45 +-
 4 files changed, 560 insertions(+), 21 deletions(-)
 create mode 100644 src/plugins/vis_type_xy/public/utils/render_all_series.test.mocks.ts
 create mode 100644 src/plugins/vis_type_xy/public/utils/render_all_series.test.tsx

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(<div>{renderSeries}</div>);
+    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(<div>{renderBarSeries}</div>);
+    expect(wrapper.find(AreaSeries).length).toBe(0);
+    expect(wrapper.find(BarSeries).length).toBe(1);
+  });
+
+  it('renders the correct yAccessors for not percentile aggs', () => {
+    const renderSeries = getAllSeries(getVisConfig(), defaultSeriesParams, defaultData);
+    const wrapper = shallow(<div>{renderSeries}</div>);
+    expect(wrapper.find(AreaSeries).prop('yAccessors')).toEqual(['col-1-3']);
+  });
+
+  it('renders the correct yAccessors for percentile aggs', () => {
+    const percentilesConfig = getVisConfigPercentiles();
+    const percentilesData = getPercentilesData();
+    const renderPercentileSeries = renderAllSeries(
+      percentilesConfig,
+      defaultSeriesParams as SeriesParam[],
+      percentilesData,
+      jest.fn(),
+      jest.fn(),
+      'Europe/Athens',
+      'col-0-2',
+      []
+    );
+    const wrapper = shallow(<div>{renderPercentileSeries}</div>);
+    expect(wrapper.find(AreaSeries).prop('yAccessors')).toEqual([
+      'col-1-3.1',
+      'col-2-3.5',
+      'col-3-3.25',
+      'col-4-3.50',
+      'col-5-3.75',
+      'col-6-3.95',
+      'col-7-3.99',
+    ]);
+  });
+
+  it('defaults the CurveType to linear', () => {
+    const renderSeries = getAllSeries(getVisConfig(), defaultSeriesParams, defaultData);
+    const wrapper = shallow(<div>{renderSeries}</div>);
+    expect(wrapper.find(AreaSeries).prop('curve')).toEqual(CurveType.LINEAR);
+  });
+});
diff --git a/src/plugins/vis_type_xy/public/utils/render_all_series.tsx b/src/plugins/vis_type_xy/public/utils/render_all_series.tsx
index 264fa539c1980..fb884bb235971 100644
--- a/src/plugins/vis_type_xy/public/utils/render_all_series.tsx
+++ b/src/plugins/vis_type_xy/public/utils/render_all_series.tsx
@@ -71,13 +71,15 @@ export const renderAllSeries = (
       interpolate,
       type,
     }) => {
-      const yAspect = aspects.y.find(({ aggId }) => aggId === paramId);
-
-      if (!show || !yAspect || yAspect.accessor === null) {
+      const yAspects = aspects.y.filter(
+        ({ aggId, accessor }) => aggId?.includes(paramId) && accessor !== null
+      );
+      if (!show || !yAspects.length) {
         return null;
       }
+      const yAccessors = yAspects.map((aspect) => aspect.accessor) as string[];
 
-      const id = `${type}-${yAspect.accessor}`;
+      const id = `${type}-${yAccessors[0]}`;
       const yAxisScale = yAxes.find(({ groupId: axisGroupId }) => axisGroupId === groupId)?.scale;
       const isStacked = mode === 'stacked' || yAxisScale?.mode === 'percentage';
       const stackMode = yAxisScale?.mode === 'normal' ? undefined : yAxisScale?.mode;
@@ -94,13 +96,13 @@ export const renderAllSeries = (
               id={id}
               name={getSeriesName}
               color={getSeriesColor}
-              tickFormat={yAspect.formatter}
+              tickFormat={yAspects[0].formatter}
               groupId={pseudoGroupId}
               useDefaultGroupDomain={useDefaultGroupDomain}
               xScaleType={xAxis.scale.type}
               yScaleType={yAxisScale?.type}
               xAccessor={xAccessor}
-              yAccessors={[yAspect.accessor]}
+              yAccessors={yAccessors}
               splitSeriesAccessors={splitSeriesAccessors}
               data={data}
               timeZone={timeZone}
@@ -125,7 +127,7 @@ export const renderAllSeries = (
               id={id}
               fit={fittingFunction}
               color={getSeriesColor}
-              tickFormat={yAspect.formatter}
+              tickFormat={yAspects[0].formatter}
               name={getSeriesName}
               curve={getCurveType(interpolate)}
               groupId={pseudoGroupId}
@@ -133,7 +135,7 @@ export const renderAllSeries = (
               xScaleType={xAxis.scale.type}
               yScaleType={yAxisScale?.type}
               xAccessor={xAccessor}
-              yAccessors={[yAspect.accessor]}
+              yAccessors={yAccessors}
               markSizeAccessor={markSizeAccessor}
               markFormat={aspects.z?.formatter}
               splitSeriesAccessors={splitSeriesAccessors}
diff --git a/src/plugins/vis_type_xy/public/vis_component.tsx b/src/plugins/vis_type_xy/public/vis_component.tsx
index 0cdabd2fa409e..6f994707cbb72 100644
--- a/src/plugins/vis_type_xy/public/vis_component.tsx
+++ b/src/plugins/vis_type_xy/public/vis_component.tsx
@@ -296,10 +296,38 @@ const VisComponent = (props: VisComponentProps) => {
     ]
   );
   const xAccessor = getXAccessor(config.aspects.x);
-  const splitSeriesAccessors = config.aspects.series
-    ? compact(config.aspects.series.map(getComplexAccessor(COMPLEX_SPLIT_ACCESSOR)))
-    : [];
 
+  const splitSeriesAccessors = useMemo(
+    () =>
+      config.aspects.series
+        ? compact(config.aspects.series.map(getComplexAccessor(COMPLEX_SPLIT_ACCESSOR)))
+        : [],
+    [config.aspects.series]
+  );
+
+  const renderSeries = useMemo(
+    () =>
+      renderAllSeries(
+        config,
+        visParams.seriesParams,
+        visData.rows,
+        getSeriesName,
+        getSeriesColor,
+        timeZone,
+        xAccessor,
+        splitSeriesAccessors
+      ),
+    [
+      config,
+      getSeriesColor,
+      getSeriesName,
+      splitSeriesAccessors,
+      timeZone,
+      visData.rows,
+      visParams.seriesParams,
+      xAccessor,
+    ]
+  );
   return (
     <div className="xyChart__container" data-test-subj="visTypeXyChart">
       <LegendToggle
@@ -343,16 +371,7 @@ const VisComponent = (props: VisComponentProps) => {
         {config.yAxes.map((axisProps) => (
           <XYAxis key={axisProps.id} {...axisProps} />
         ))}
-        {renderAllSeries(
-          config,
-          visParams.seriesParams,
-          visData.rows,
-          getSeriesName,
-          getSeriesColor,
-          timeZone,
-          xAccessor,
-          splitSeriesAccessors
-        )}
+        {renderSeries}
       </Chart>
     </div>
   );

From 72ef3b105a38ae66045849054adc7408daee163c Mon Sep 17 00:00:00 2001
From: Daniil <daniil_suleiman@epam.com>
Date: Mon, 25 Jan 2021 14:11:59 +0300
Subject: [PATCH 06/46] [Data table] Add telemetry for table vis split mode
 (#88604)

* Add telemetry for table vis

* Update telemetry schema

* Add unit tests

* Update license

* Use soClient instead of esClient, update tests

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
 src/plugins/telemetry/schema/oss_plugins.json | 30 +++++++
 src/plugins/vis_type_table/common/index.ts    |  9 ++
 src/plugins/vis_type_table/common/types.ts    | 28 ++++++
 src/plugins/vis_type_table/jest.config.js     |  1 +
 .../public/components/table_vis_options.tsx   |  2 +-
 .../components/table_vis_options_lazy.tsx     |  2 +-
 .../vis_type_table/public/components/utils.ts |  2 +-
 .../public/legacy/table_vis_legacy_fn.ts      |  5 +-
 .../public/legacy/table_vis_legacy_type.ts    |  4 +-
 .../vis_type_table/public/table_vis_fn.ts     |  5 +-
 .../vis_type_table/public/table_vis_type.ts   |  4 +-
 .../vis_type_table/public/to_ast.test.ts      |  2 +-
 src/plugins/vis_type_table/public/to_ast.ts   |  3 +-
 src/plugins/vis_type_table/public/types.ts    | 19 +----
 .../public/utils/use/use_formatted_columns.ts |  3 +-
 .../public/utils/use/use_pagination.ts        |  2 +-
 src/plugins/vis_type_table/server/index.ts    | 10 ++-
 .../server/usage_collector/get_stats.test.ts  | 67 +++++++++++++++
 .../server/usage_collector/get_stats.ts       | 85 +++++++++++++++++++
 .../server/usage_collector/index.ts           |  9 ++
 .../register_usage_collector.test.ts          | 56 ++++++++++++
 .../register_usage_collector.ts               | 32 +++++++
 src/plugins/vis_type_table/tsconfig.json      |  1 +
 src/plugins/visualizations/common/index.ts    | 10 +++
 src/plugins/visualizations/common/types.ts    | 32 +++++++
 src/plugins/visualizations/public/index.ts    |  5 +-
 .../public/legacy/vis_update_state.d.ts       |  2 +-
 .../saved_visualization_references.test.ts    |  3 +-
 src/plugins/visualizations/public/types.ts    | 16 +---
 src/plugins/visualizations/public/vis.ts      |  5 +-
 30 files changed, 398 insertions(+), 56 deletions(-)
 create mode 100644 src/plugins/vis_type_table/common/index.ts
 create mode 100644 src/plugins/vis_type_table/common/types.ts
 create mode 100644 src/plugins/vis_type_table/server/usage_collector/get_stats.test.ts
 create mode 100644 src/plugins/vis_type_table/server/usage_collector/get_stats.ts
 create mode 100644 src/plugins/vis_type_table/server/usage_collector/index.ts
 create mode 100644 src/plugins/vis_type_table/server/usage_collector/register_usage_collector.test.ts
 create mode 100644 src/plugins/vis_type_table/server/usage_collector/register_usage_collector.ts
 create mode 100644 src/plugins/visualizations/common/index.ts
 create mode 100644 src/plugins/visualizations/common/types.ts

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: ['<rootDir>/src/plugins/vis_type_table'],
   testRunner: 'jasmine2',
+  collectCoverageFrom: ['<rootDir>/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<TableVisParams> = {
-  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<TableVisParams> = {
-  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<typeof getVisSchemas>,
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<ConfigSchema> = {
   exposeToBrowser: {
@@ -21,6 +23,10 @@ export const config: PluginConfigDescriptor<ConfigSchema> = {
 };
 
 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<VisTypeTableUsage | undefined> {
+  const visualizations = await soClient.find<VisualizationSavedObjectAttributes>({
+    type: 'visualization',
+    perPage: 10000,
+  });
+
+  const tableVisualizations = visualizations.saved_objects
+    .map<SavedVisState<TableVisParams>>(({ 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<VisTypeTableUsage | undefined>({
+    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/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<TVisParams = VisParams> {
+  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<VisualizeEmbeddableFactory>;
 export type VisualizeEmbeddableContract = PublicContract<VisualizeEmbeddable>;
 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) {

From 601d9fd018ffc833d41f9a344271839b209ddb3a Mon Sep 17 00:00:00 2001
From: Shahzad <shahzad.muhammad@elastic.co>
Date: Mon, 25 Jan 2021 12:33:26 +0100
Subject: [PATCH 07/46] [Uptime] waterfall view reduce opacity for
 blocking/waiting color (#88611)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
 .../step_detail/waterfall/data_formatting.test.ts  | 14 +++++++-------
 .../step_detail/waterfall/data_formatting.ts       |  2 +-
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts
index 5c0b36874004a..3967e0404a76f 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts
@@ -11,7 +11,7 @@ import { WaterfallDataEntry } from '../../waterfall/types';
 describe('Palettes', () => {
   it('A colour palette comprising timing and mime type colours is correctly generated', () => {
     expect(colourPalette).toEqual({
-      blocked: '#b9a888',
+      blocked: '#dcd4c4',
       connect: '#da8b45',
       dns: '#54b399',
       font: '#aa6556',
@@ -173,10 +173,10 @@ describe('getSeriesAndDomain', () => {
         "series": Array [
           Object {
             "config": Object {
-              "colour": "#b9a888",
+              "colour": "#dcd4c4",
               "showTooltip": true,
               "tooltipProps": Object {
-                "colour": "#b9a888",
+                "colour": "#dcd4c4",
                 "value": "Queued / Blocked: 0.854ms",
               },
             },
@@ -264,10 +264,10 @@ describe('getSeriesAndDomain', () => {
           },
           Object {
             "config": Object {
-              "colour": "#b9a888",
+              "colour": "#dcd4c4",
               "showTooltip": true,
               "tooltipProps": Object {
-                "colour": "#b9a888",
+                "colour": "#dcd4c4",
                 "value": "Queued / Blocked: 84.546ms",
               },
             },
@@ -330,10 +330,10 @@ describe('getSeriesAndDomain', () => {
         "series": Array [
           Object {
             "config": Object {
-              "colour": "#b9a888",
+              "colour": "#dcd4c4",
               "showTooltip": true,
               "tooltipProps": Object {
-                "colour": "#b9a888",
+                "colour": "#dcd4c4",
                 "value": "Queued / Blocked: 0.854ms",
               },
             },
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts
index 5e59026fd65f8..3cc0497bda8ec 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts
@@ -197,7 +197,7 @@ const buildTimingPalette = (): TimingColourPalette => {
   const palette = Object.values(Timings).reduce<Partial<TimingColourPalette>>((acc, value) => {
     switch (value) {
       case Timings.Blocked:
-        acc[value] = SAFE_PALETTE[6];
+        acc[value] = SAFE_PALETTE[16];
         break;
       case Timings.Dns:
         acc[value] = SAFE_PALETTE[0];

From f15a1e685cc70844b01e3814f5d264fd1ad43586 Mon Sep 17 00:00:00 2001
From: James Gowdy <jgowdy@elastic.co>
Date: Mon, 25 Jan 2021 11:36:48 +0000
Subject: [PATCH 08/46] [ML] Adding jobs stats to functions shared in setup
 contract (#88673)

* [ML] Adding jobs stats to functions shared in setup contract

* updating types

* adding datafeeds

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
 .../providers/anomaly_detectors.ts            | 46 ++++++++++++++++++-
 1 file changed, 45 insertions(+), 1 deletion(-)

diff --git a/x-pack/plugins/ml/server/shared_services/providers/anomaly_detectors.ts b/x-pack/plugins/ml/server/shared_services/providers/anomaly_detectors.ts
index 3719f4d97779c..42f6eb1f8f9c8 100644
--- a/x-pack/plugins/ml/server/shared_services/providers/anomaly_detectors.ts
+++ b/x-pack/plugins/ml/server/shared_services/providers/anomaly_detectors.ts
@@ -5,7 +5,12 @@
  */
 
 import { KibanaRequest, SavedObjectsClientContract } from 'kibana/server';
-import { Job } from '../../../common/types/anomaly_detection_jobs';
+import {
+  Job,
+  JobStats,
+  Datafeed,
+  DatafeedStats,
+} from '../../../common/types/anomaly_detection_jobs';
 import { GetGuards } from '../shared_services';
 
 export interface AnomalyDetectorsProvider {
@@ -14,6 +19,9 @@ export interface AnomalyDetectorsProvider {
     savedObjectsClient: SavedObjectsClientContract
   ): {
     jobs(jobId?: string): Promise<{ count: number; jobs: Job[] }>;
+    jobStats(jobId?: string): Promise<{ count: number; jobs: JobStats[] }>;
+    datafeeds(datafeedId?: string): Promise<{ count: number; datafeeds: Datafeed[] }>;
+    datafeedStats(datafeedId?: string): Promise<{ count: number; datafeeds: DatafeedStats[] }>;
   };
 }
 
@@ -36,6 +44,42 @@ export function getAnomalyDetectorsProvider(getGuards: GetGuards): AnomalyDetect
               return body;
             });
         },
+        async jobStats(jobId?: string) {
+          return await getGuards(request, savedObjectsClient)
+            .isFullLicense()
+            .hasMlCapabilities(['canGetJobs'])
+            .ok(async ({ mlClient }) => {
+              const { body } = await mlClient.getJobStats<{
+                count: number;
+                jobs: JobStats[];
+              }>(jobId !== undefined ? { job_id: jobId } : undefined);
+              return body;
+            });
+        },
+        async datafeeds(datafeedId?: string) {
+          return await getGuards(request, savedObjectsClient)
+            .isFullLicense()
+            .hasMlCapabilities(['canGetDatafeeds'])
+            .ok(async ({ mlClient }) => {
+              const { body } = await mlClient.getDatafeeds<{
+                count: number;
+                datafeeds: Datafeed[];
+              }>(datafeedId !== undefined ? { datafeed_id: datafeedId } : undefined);
+              return body;
+            });
+        },
+        async datafeedStats(datafeedId?: string) {
+          return await getGuards(request, savedObjectsClient)
+            .isFullLicense()
+            .hasMlCapabilities(['canGetDatafeeds'])
+            .ok(async ({ mlClient }) => {
+              const { body } = await mlClient.getDatafeedStats<{
+                count: number;
+                datafeeds: DatafeedStats[];
+              }>(datafeedId !== undefined ? { datafeed_id: datafeedId } : undefined);
+              return body;
+            });
+        },
       };
     },
   };

From 349fd5fcade9e6c80bbe3be1b13e50fa6efbc24a Mon Sep 17 00:00:00 2001
From: Diana Derevyankina
 <54894989+DziyanaDzeraviankina@users.noreply.github.com>
Date: Mon, 25 Jan 2021 15:26:53 +0300
Subject: [PATCH 09/46] Advanced JSON input functional test (#88609)

* Advanced JSON input functional test

* Replace string check with object property check

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
 test/functional/apps/visualize/_inspector.ts | 31 ++++++++++++++++++++
 1 file changed, 31 insertions(+)

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();

From 191ad14ac2ec5e275750b9d93db72c71a1d7ef98 Mon Sep 17 00:00:00 2001
From: Diana Derevyankina
 <54894989+DziyanaDzeraviankina@users.noreply.github.com>
Date: Mon, 25 Jan 2021 15:47:03 +0300
Subject: [PATCH 10/46] Small multiples in vis_type_xy plugin (#86880)

* Small multiples in vis_type_xy plugin

* Fix tooltip and formatted split chart values

* update advanced settings wording

* Remove React import in files with no JSX and change the extension to .ts

* Simplify conditions

* fix bar interval on split charts in vislib

* Fix charts not splitting for terms boolean fields

* fix filtering for small multiples

* Change tests interval values from 100 to 1000000

* Revert "Change tests interval values from 100 to 1000000"

This reverts commit 92f9d1b4b9e0b1f3a6c6d1058c36c657d2c5c68f.

* Fix tests for interval issue in vislib

(cherry picked from commit ef45b63c47da403399f76f00b49329531d445f31)

* Revert axis_scale changes related to interval

* Enable _line_chart_split_chart test for new charts library

* Move chart splitter id to const

Co-authored-by: nickofthyme <nick.ryan.partridge@gmail.com>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
 docs/management/advanced-options.asciidoc     |  2 +-
 .../static/utils/transform_click_event.ts     | 62 ++++++++++++++++---
 .../public/vislib/lib/axis/axis_scale.js      | 10 +--
 .../vis_type_xy/public/chart_splitter.tsx     | 45 ++++++++++++++
 .../public/components/detailed_tooltip.tsx    | 34 +++++++---
 .../vis_type_xy/public/components/index.ts    |  1 -
 .../public/components/split_chart_warning.tsx | 44 -------------
 .../vis_type_xy/public/config/get_aspects.ts  |  7 ++-
 .../vis_type_xy/public/types/config.ts        |  2 +
 .../vis_type_xy/public/utils/accessors.tsx    | 15 +++--
 .../vis_type_xy/public/vis_component.tsx      | 40 ++++++++++--
 .../vis_type_xy/public/vis_renderer.tsx       | 25 +++-----
 .../public/vis_types/{area.tsx => area.ts}    |  9 ---
 .../vis_types/{histogram.tsx => histogram.ts} |  9 ---
 .../{horizontal_bar.tsx => horizontal_bar.ts} |  9 ---
 .../public/vis_types/{line.tsx => line.ts}    |  9 ---
 .../public/vis_types/split_tooltip.tsx        | 20 ------
 src/plugins/vis_type_xy/server/plugin.ts      |  3 +-
 .../apps/visualize/_line_chart_split_chart.ts | 28 ++++++---
 test/functional/apps/visualize/index.ts       |  1 +
 20 files changed, 213 insertions(+), 162 deletions(-)
 create mode 100644 src/plugins/vis_type_xy/public/chart_splitter.tsx
 delete mode 100644 src/plugins/vis_type_xy/public/components/split_chart_warning.tsx
 rename src/plugins/vis_type_xy/public/vis_types/{area.tsx => area.ts} (94%)
 rename src/plugins/vis_type_xy/public/vis_types/{histogram.tsx => histogram.ts} (94%)
 rename src/plugins/vis_type_xy/public/vis_types/{horizontal_bar.tsx => horizontal_bar.ts} (94%)
 rename src/plugins/vis_type_xy/public/vis_types/{line.tsx => line.ts} (94%)
 delete mode 100644 src/plugins/vis_type_xy/public/vis_types/split_tooltip.tsx

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/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<XYChartSeriesIdentifier, 'smHorizontalAccessorValue' | 'smVerticalAccessorValue'>):
+  | 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<string | number, AccessorFn>,
+  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<Array<[index: number, id: string]>>(
-      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<string | number, AccessorFn>,
+  splitChartAccessor?: Accessor | AccessorFn,
   negate = false
 ): ClickTriggerEvent => {
+  const splitChartValue = getSplitChartValue(rest);
   const allSplitAccessors = getAllSplitAccessors(splitAccessors, splitSeriesAccessorFnMap);
   const columns = table.columns.reduce<Array<[index: number, id: string]>>(
-    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/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 ? (
+    <>
+      <GroupBy
+        id={CHART_SPLITTER_ID}
+        by={(spec, datum) => {
+          const splitTypeAccessor = splitColumnAccessor || splitRowAccessor;
+          if (splitTypeAccessor) {
+            return typeof splitTypeAccessor === 'function'
+              ? splitTypeAccessor(datum)
+              : datum[splitTypeAccessor];
+          }
+          return spec.id;
+        }}
+        sort={sort || 'dataIndex'}
+      />
+      <SmallMultiples
+        splitVertically={splitRowAccessor ? CHART_SPLITTER_ID : undefined}
+        splitHorizontally={splitColumnAccessor ? CHART_SPLITTER_ID : undefined}
+      />
+    </>
+  ) : 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 (
-    <EuiCallOut
-      title={i18n.translate('visTypeXy.splitChartWarning.title', {
-        defaultMessage: 'Warning',
-      })}
-      color="warning"
-      iconType="help"
-    >
-      <FormattedMessage
-        id="visTypeXy.splitChartWarning.content"
-        defaultMessage="The new charts library does not support split chart aggregation. Please enable the {link} advanced setting to use split chart aggregation."
-        values={{
-          link: (
-            <EuiLink href={advancedSettingsLink} target="_blank" external>
-              <FormattedMessage
-                id="visTypeXy.splitChartWarning.link"
-                defaultMessage="Legacy charts library"
-              />
-            </EuiLink>
-          ),
-        }}
-      />
-    </EuiCallOut>
-  );
-};
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/vis_component.tsx b/src/plugins/vis_type_xy/public/vis_component.tsx
index 6f994707cbb72..871fb408d4da0 100644
--- a/src/plugins/vis_type_xy/public/vis_component.tsx
+++ b/src/plugins/vis_type_xy/public/vis_component.tsx
@@ -65,6 +65,7 @@ import {
   getComplexAccessor,
   getSplitSeriesAccessorFnMap,
 } from './utils/accessors';
+import { ChartSplitter } from './chart_splitter';
 
 export interface VisComponentProps {
   visParams: VisParams;
@@ -117,7 +118,8 @@ const VisComponent = (props: VisComponentProps) => {
     (
       visData: Datatable,
       xAccessor: Accessor | AccessorFn,
-      splitSeriesAccessors: Array<Accessor | AccessorFn>
+      splitSeriesAccessors: Array<Accessor | AccessorFn>,
+      splitChartAccessor?: Accessor | AccessorFn
     ): ElementClickListener => {
       const splitSeriesAccessorFnMap = getSplitSeriesAccessorFnMap(splitSeriesAccessors);
       return (elements) => {
@@ -125,7 +127,8 @@ const VisComponent = (props: VisComponentProps) => {
           const event = getFilterFromChartClickEventFn(
             visData,
             xAccessor,
-            splitSeriesAccessorFnMap
+            splitSeriesAccessorFnMap,
+            splitChartAccessor
           )(elements as XYChartElementEvent[]);
           props.fireEvent(event);
         }
@@ -154,12 +157,17 @@ const VisComponent = (props: VisComponentProps) => {
     (
       visData: Datatable,
       xAccessor: Accessor | AccessorFn,
-      splitSeriesAccessors: Array<Accessor | AccessorFn>
+      splitSeriesAccessors: Array<Accessor | AccessorFn>,
+      splitChartAccessor?: Accessor | AccessorFn
     ) => {
       const splitSeriesAccessorFnMap = getSplitSeriesAccessorFnMap(splitSeriesAccessors);
       return (series: XYChartSeriesIdentifier): ClickTriggerEvent | null => {
         if (xAccessor !== null) {
-          return getFilterFromSeriesFn(visData)(series, splitSeriesAccessorFnMap);
+          return getFilterFromSeriesFn(visData)(
+            series,
+            splitSeriesAccessorFnMap,
+            splitChartAccessor
+          );
         }
 
         return null;
@@ -304,6 +312,12 @@ const VisComponent = (props: VisComponentProps) => {
         : [],
     [config.aspects.series]
   );
+  const splitChartColumnAccessor = config.aspects.splitColumn
+    ? getComplexAccessor(COMPLEX_SPLIT_ACCESSOR, true)(config.aspects.splitColumn)
+    : undefined;
+  const splitChartRowAccessor = config.aspects.splitRow
+    ? getComplexAccessor(COMPLEX_SPLIT_ACCESSOR, true)(config.aspects.splitRow)
+    : undefined;
 
   const renderSeries = useMemo(
     () =>
@@ -336,6 +350,10 @@ const VisComponent = (props: VisComponentProps) => {
         legendPosition={legendPosition}
       />
       <Chart size="100%">
+        <ChartSplitter
+          splitColumnAccessor={splitChartColumnAccessor}
+          splitRowAccessor={splitChartRowAccessor}
+        />
         <XYSettings
           {...config}
           showLegend={showLegend}
@@ -343,14 +361,24 @@ const VisComponent = (props: VisComponentProps) => {
           xDomain={xDomain}
           adjustedXDomain={adjustedXDomain}
           legendColorPicker={useColorPicker(legendPosition, setColor, getSeriesName)}
-          onElementClick={handleFilterClick(visData, xAccessor, splitSeriesAccessors)}
+          onElementClick={handleFilterClick(
+            visData,
+            xAccessor,
+            splitSeriesAccessors,
+            splitChartColumnAccessor ?? splitChartRowAccessor
+          )}
           onBrushEnd={handleBrush(visData, xAccessor, 'interval' in config.aspects.x.params)}
           onRenderChange={onRenderChange}
           legendAction={
             config.aspects.series && (config.aspects.series?.length ?? 0) > 0
               ? getLegendActions(
                   canFilter,
-                  getFilterEventData(visData, xAccessor, splitSeriesAccessors),
+                  getFilterEventData(
+                    visData,
+                    xAccessor,
+                    splitSeriesAccessors,
+                    splitChartColumnAccessor ?? splitChartRowAccessor
+                  ),
                   handleFilterAction,
                   getSeriesName
                 )
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<RenderValue> = {
   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(
       <I18nProvider>
-        <>
-          {isSplitChart && <SplitChartWarning />}
-          <VisualizationContainer handlers={handlers} showNoResult={showNoResult || isSplitChart}>
-            <VisComponent
-              visParams={visConfig}
-              visData={visData}
-              renderComplete={handlers.done}
-              fireEvent={handlers.event}
-              uiState={handlers.uiState as PersistedState}
-              syncColors={syncColors}
-            />
-          </VisualizationContainer>
-        </>
+        <VisualizationContainer handlers={handlers} showNoResult={showNoResult}>
+          <VisComponent
+            visParams={visConfig}
+            visData={visData}
+            renderComplete={handlers.done}
+            fireEvent={handlers.event}
+            uiState={handlers.uiState as PersistedState}
+            syncColors={syncColors}
+          />
+        </VisualizationContainer>
       </I18nProvider>,
       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: <SplitTooltip />,
-        }),
       },
     ],
   },
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: <SplitTooltip />,
-        }),
       },
     ],
   },
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: <SplitTooltip />,
-        }),
       },
     ],
   },
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: <SplitTooltip />,
-        }),
       },
     ],
   },
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 (
-    <FormattedMessage
-      id="visTypeXy.splitTitle.tooltip"
-      defaultMessage="Split chart aggregation is not supported with the new charts library. Please enable the legacy charts library advanced setting to use split chart aggregation."
-    />
-  );
-}
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<string, UiSettingsParams<boolean>> = {
     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/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'));

From 16657028f1b188d959a1384a82e4557f0a168e09 Mon Sep 17 00:00:00 2001
From: Daniil <daniil_suleiman@epam.com>
Date: Mon, 25 Jan 2021 16:16:25 +0300
Subject: [PATCH 11/46] [Saved Objects] Fix saved object view path (#89057)

* Fix saved object view path

* Add additional check
---
 .../public/lib/create_field_list.ts           |  3 ++-
 .../object_view/components/form.tsx           |  5 ++--
 .../object_view/saved_object_view.tsx         | 18 +++++++------
 .../saved_objects_edition_page.tsx            | 26 +++++++++++--------
 4 files changed, 30 insertions(+), 22 deletions(-)

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<string, any>).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<any>;
+  object?: SavedObjectWithMetadata<any>;
 }
 
 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 (
-    <SavedObjectEdition
-      id={id}
-      serviceName={serviceName}
-      serviceRegistry={serviceRegistry}
-      savedObjectsClient={coreStart.savedObjects.client}
-      overlays={coreStart.overlays}
-      notifications={coreStart.notifications}
-      capabilities={capabilities}
-      notFoundType={query.notFound as string}
-      history={history}
-    />
+    <RedirectAppLinks application={coreStart.application}>
+      <SavedObjectEdition
+        id={id}
+        http={coreStart.http}
+        serviceName={serviceName}
+        serviceRegistry={serviceRegistry}
+        savedObjectsClient={coreStart.savedObjects.client}
+        overlays={coreStart.overlays}
+        notifications={coreStart.notifications}
+        capabilities={capabilities}
+        notFoundType={query.notFound as string}
+        history={history}
+      />
+    </RedirectAppLinks>
   );
 };
 

From 50d8c69ea800f778c8530a1c56036ea590c7569d Mon Sep 17 00:00:00 2001
From: Tiago Costa <tiagoffcc@hotmail.com>
Date: Mon, 25 Jan 2021 13:36:38 +0000
Subject: [PATCH 12/46] skip flaky suite (#88639)

---
 .../functional/tests/visualize_integration.ts                  | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts b/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts
index e92ba226f3959..51f4bf8883521 100644
--- a/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts
+++ b/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts
@@ -151,7 +151,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
       });
     });
 
-    describe('editing', () => {
+    // FLAKY: https://github.com/elastic/kibana/issues/88639
+    describe.skip('editing', () => {
       beforeEach(async () => {
         await PageObjects.visualize.gotoVisualizationLandingPage();
         await listingTable.waitUntilTableIsLoaded();

From 99ffbbe357b5f80f10925f71e7d5862403f0a009 Mon Sep 17 00:00:00 2001
From: Marco Liberati <dej611@users.noreply.github.com>
Date: Mon, 25 Jan 2021 14:46:23 +0100
Subject: [PATCH 13/46] [Lens] Restore a11y flacky tests (#88954)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
 x-pack/test/accessibility/apps/lens.ts | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/x-pack/test/accessibility/apps/lens.ts b/x-pack/test/accessibility/apps/lens.ts
index a7cacd0ad1cbb..71673d49c0c08 100644
--- a/x-pack/test/accessibility/apps/lens.ts
+++ b/x-pack/test/accessibility/apps/lens.ts
@@ -7,15 +7,12 @@
 import { FtrProviderContext } from '../ftr_provider_context';
 
 export default function ({ getService, getPageObjects }: FtrProviderContext) {
-  const PageObjects = getPageObjects(['common', 'visualize', 'header', 'home', 'settings', 'lens']);
+  const PageObjects = getPageObjects(['common', 'visualize', 'timePicker', 'home', 'lens']);
   const a11y = getService('a11y');
   const testSubjects = getService('testSubjects');
   const listingTable = getService('listingTable');
 
-  // FLAKY: https://github.com/elastic/kibana/issues/88926
-  // FLAKY: https://github.com/elastic/kibana/issues/88927
-  // FLAKY: https://github.com/elastic/kibana/issues/88929
-  describe.skip('Lens', () => {
+  describe('Lens', () => {
     const lensChartName = 'MyLensChart';
     before(async () => {
       await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', {
@@ -35,12 +32,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
     it('lens', async () => {
       await PageObjects.visualize.navigateToNewVisualization();
       await PageObjects.visualize.clickVisType('lens');
+      await PageObjects.timePicker.ensureHiddenNoDataPopover();
       await a11y.testAppSnapshot();
     });
 
     it('lens XY chart', async () => {
       await PageObjects.visualize.navigateToNewVisualization();
       await PageObjects.visualize.clickVisType('lens');
+      await PageObjects.timePicker.ensureHiddenNoDataPopover();
 
       await PageObjects.lens.configureDimension({
         dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension',
@@ -75,6 +74,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
     it('dimension configuration panel', async () => {
       await PageObjects.visualize.navigateToNewVisualization();
       await PageObjects.visualize.clickVisType('lens');
+      await PageObjects.timePicker.ensureHiddenNoDataPopover();
 
       await PageObjects.lens.openDimensionEditor('lnsXY_xDimensionPanel > lns-empty-dimension');
       await a11y.testAppSnapshot();

From a5bb86482d346c905d2946e667055cc0889d320a Mon Sep 17 00:00:00 2001
From: Tiago Costa <tiagoffcc@hotmail.com>
Date: Mon, 25 Jan 2021 13:49:20 +0000
Subject: [PATCH 14/46] skip flaky suite (#89069)

---
 .../apps/management/search_sessions/sessions_management.ts     | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/sessions_management.ts b/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/sessions_management.ts
index f06e8eba0bf68..e3797550984aa 100644
--- a/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/sessions_management.ts
+++ b/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/sessions_management.ts
@@ -20,7 +20,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const retry = getService('retry');
 
-  describe('Search search sessions Management UI', () => {
+  // FLAKY: https://github.com/elastic/kibana/issues/89069
+  describe.skip('Search search sessions Management UI', () => {
     describe('New search sessions', () => {
       before(async () => {
         await PageObjects.common.navigateToApp('dashboard');

From aeb6df30d5b987579fcef03805a17e3c5b7da224 Mon Sep 17 00:00:00 2001
From: Thom Heymann <190132+thomheymann@users.noreply.github.com>
Date: Mon, 25 Jan 2021 13:51:57 +0000
Subject: [PATCH 15/46] Update user management page (#87133)

* Update user management page

* Fixed i18n errors

* Fix linting errors

* Add ids required for accessability

* Added suggestions from code review

* Fix test errors

* Fix types in fleet

* fix translations

* Fix i18n

* Added suggestions from code review

* Fix i18n errors

* Fix linting errors

* Update messaging

* Updated unit tests

* Updated functional tests

* Fixed functional tests

* Fix linting errors

* Fix React warnings

* Added suggestions from code review

* Added tests and renamed routes

* Fix functional tests

* Simplified API integration tests

* Updated copy based on writing suggestions

* Fixed unit tests

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
 package.json                                  |   2 +-
 .../components/agent_logs/agent_logs.tsx      |   2 +-
 x-pack/plugins/security/common/constants.ts   |  13 +
 x-pack/plugins/security/common/model/index.ts |   2 +
 x-pack/plugins/security/common/model/role.ts  |  22 +-
 .../security/public/components/breadcrumb.tsx | 137 +++
 .../public/components/confirm_modal.tsx       |  94 ++
 .../security/public/components/doc_link.tsx   |  60 ++
 .../public/components/form_flyout.tsx         |  97 ++
 .../public/components/use_current_user.ts     |  24 +
 .../security/public/components/use_form.ts    | 205 +++++
 .../security/public/components/use_html_id.ts |  27 +
 .../role_combo_box/role_combo_box.test.tsx    | 266 +++---
 .../role_combo_box/role_combo_box.tsx         | 128 ++-
 .../role_combo_box_option.test.tsx            |  57 --
 .../role_combo_box/role_combo_box_option.tsx  |  31 -
 .../roles/edit_role/edit_role_page.tsx        |   2 +-
 .../roles/edit_role/validate_role.test.ts     |  28 +-
 .../roles/edit_role/validate_role.ts          |  22 +-
 .../edit_user/change_password_flyout.tsx      | 286 ++++++
 .../users/edit_user/confirm_delete_users.tsx  |  96 ++
 .../users/edit_user/confirm_disable_users.tsx | 119 +++
 .../users/edit_user/confirm_enable_users.tsx  |  89 ++
 .../users/edit_user/create_user_page.test.tsx | 108 +++
 .../users/edit_user/create_user_page.tsx      |  44 +
 .../users/edit_user/edit_user_page.scss       |   6 -
 .../users/edit_user/edit_user_page.test.tsx   | 566 ++++++++----
 .../users/edit_user/edit_user_page.tsx        | 836 ++++++------------
 .../management/users/edit_user/index.ts       |   1 +
 .../management/users/edit_user/user_form.tsx  | 466 ++++++++++
 .../users/edit_user/validate_user.test.ts     | 128 ---
 .../users/edit_user/validate_user.ts          | 142 ---
 .../management/users/user_api_client.mock.ts  |   2 +
 .../management/users/user_api_client.ts       |  10 +-
 .../users/users_grid/users_grid_page.tsx      |   2 +-
 .../users/users_management_app.test.tsx       | 129 +--
 .../management/users/users_management_app.tsx | 158 ++--
 .../server/routes/users/create_or_update.ts   |   7 +-
 .../security/server/routes/users/disable.ts   |  32 +
 .../security/server/routes/users/enable.ts    |  32 +
 .../security/server/routes/users/index.ts     |   4 +
 .../translations/translations/ja-JP.json      |  34 +-
 .../translations/translations/zh-CN.json      |  34 +-
 x-pack/test/accessibility/apps/users.ts       |  47 +-
 .../api_integration/apis/security/index.ts    |   1 +
 .../apis/security/security_basic.ts           |   1 +
 .../api_integration/apis/security/users.ts    |  47 +
 .../dashboard_mode/dashboard_view_mode.js     |  58 +-
 .../apps/security/doc_level_security_roles.js |   8 +-
 .../apps/security/field_level_security.js     |  16 +-
 .../functional/apps/security/rbac_phase1.js   |  14 +-
 .../functional/apps/security/role_mappings.ts |   5 +-
 .../apps/security/secure_roles_perm.js        |   8 +-
 .../functional/apps/security/user_email.js    |   7 +-
 x-pack/test/functional/apps/security/users.js |  12 +-
 .../functional/page_objects/security_page.ts  | 108 ++-
 yarn.lock                                     |  40 +-
 57 files changed, 3242 insertions(+), 1680 deletions(-)
 create mode 100644 x-pack/plugins/security/public/components/breadcrumb.tsx
 create mode 100644 x-pack/plugins/security/public/components/confirm_modal.tsx
 create mode 100644 x-pack/plugins/security/public/components/doc_link.tsx
 create mode 100644 x-pack/plugins/security/public/components/form_flyout.tsx
 create mode 100644 x-pack/plugins/security/public/components/use_current_user.ts
 create mode 100644 x-pack/plugins/security/public/components/use_form.ts
 create mode 100644 x-pack/plugins/security/public/components/use_html_id.ts
 delete mode 100644 x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.test.tsx
 delete mode 100644 x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.tsx
 create mode 100644 x-pack/plugins/security/public/management/users/edit_user/change_password_flyout.tsx
 create mode 100644 x-pack/plugins/security/public/management/users/edit_user/confirm_delete_users.tsx
 create mode 100644 x-pack/plugins/security/public/management/users/edit_user/confirm_disable_users.tsx
 create mode 100644 x-pack/plugins/security/public/management/users/edit_user/confirm_enable_users.tsx
 create mode 100644 x-pack/plugins/security/public/management/users/edit_user/create_user_page.test.tsx
 create mode 100644 x-pack/plugins/security/public/management/users/edit_user/create_user_page.tsx
 delete mode 100644 x-pack/plugins/security/public/management/users/edit_user/edit_user_page.scss
 create mode 100644 x-pack/plugins/security/public/management/users/edit_user/user_form.tsx
 delete mode 100644 x-pack/plugins/security/public/management/users/edit_user/validate_user.test.ts
 delete mode 100644 x-pack/plugins/security/public/management/users/edit_user/validate_user.ts
 create mode 100644 x-pack/plugins/security/server/routes/users/disable.ts
 create mode 100644 x-pack/plugins/security/server/routes/users/enable.ts
 create mode 100644 x-pack/test/api_integration/apis/security/users.ts

diff --git a/package.json b/package.json
index 2fdc31820b9d4..24297011ccc63 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx
index 7326d2efb8565..8a4cf1d8566a0 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx
@@ -183,7 +183,7 @@ export const AgentLogsUI: React.FunctionComponent<AgentLogsProps> = memo(({ agen
     [http.basePath, state.start, state.end, logStreamQuery]
   );
 
-  const [logsPanelRef, { height: logPanelHeight }] = useMeasure();
+  const [logsPanelRef, { height: logPanelHeight }] = useMeasure<HTMLDivElement>();
 
   const agentVersion = agent.local_metadata?.elastic?.agent?.version;
   const isLogFeatureAvailable = useMemo(() => {
diff --git a/x-pack/plugins/security/common/constants.ts b/x-pack/plugins/security/common/constants.ts
index f53b5ca6d56ca..c235c296bcbae 100644
--- a/x-pack/plugins/security/common/constants.ts
+++ b/x-pack/plugins/security/common/constants.ts
@@ -22,3 +22,16 @@ export const AUTH_PROVIDER_HINT_QUERY_STRING_PARAMETER = 'auth_provider_hint';
 export const LOGOUT_PROVIDER_QUERY_STRING_PARAMETER = 'provider';
 export const LOGOUT_REASON_QUERY_STRING_PARAMETER = 'msg';
 export const NEXT_URL_QUERY_STRING_PARAMETER = 'next';
+
+/**
+ * Matches valid usernames and role names.
+ *
+ * - Must contain only letters, numbers, spaces, punctuation and printable symbols.
+ * - Must not contain leading or trailing spaces.
+ */
+export const NAME_REGEX = /^(?! )[a-zA-Z0-9 !"#$%&'()*+,\-./\\:;<=>?@\[\]^_`{|}~]+(?<! )$/;
+
+/**
+ * Maximum length of usernames and role names.
+ */
+export const MAX_NAME_LENGTH = 1024;
diff --git a/x-pack/plugins/security/common/model/index.ts b/x-pack/plugins/security/common/model/index.ts
index ee1dcffd4a794..df55ffd27a22f 100644
--- a/x-pack/plugins/security/common/model/index.ts
+++ b/x-pack/plugins/security/common/model/index.ts
@@ -19,6 +19,8 @@ export {
   isRoleDeprecated,
   isRoleReadOnly,
   isRoleReserved,
+  isRoleSystem,
+  isRoleAdmin,
   isRoleEnabled,
   prepareRoleClone,
   getExtendedRoleDeprecationNotice,
diff --git a/x-pack/plugins/security/common/model/role.ts b/x-pack/plugins/security/common/model/role.ts
index 4cc7271eaca13..15213ece49395 100644
--- a/x-pack/plugins/security/common/model/role.ts
+++ b/x-pack/plugins/security/common/model/role.ts
@@ -68,7 +68,27 @@ export function isRoleReserved(role: Partial<Role>) {
  * @param {role} the Role as returned by roles API
  */
 export function isRoleDeprecated(role: Partial<Role>) {
-  return role.metadata?._deprecated ?? false;
+  return (role.metadata?._deprecated as boolean) ?? false;
+}
+
+/**
+ * Returns whether given role is a system role or not.
+ *
+ * @param {role} the Role as returned by roles API
+ */
+export function isRoleSystem(role: Partial<Role>) {
+  return (isRoleReserved(role) && role.name?.endsWith('_system')) ?? false;
+}
+
+/**
+ * Returns whether given role is an admin role or not.
+ *
+ * @param {role} the Role as returned by roles API
+ */
+export function isRoleAdmin(role: Partial<Role>) {
+  return (
+    (isRoleReserved(role) && (role.name?.endsWith('_admin') || role.name === 'superuser')) ?? false
+  );
 }
 
 /**
diff --git a/x-pack/plugins/security/public/components/breadcrumb.tsx b/x-pack/plugins/security/public/components/breadcrumb.tsx
new file mode 100644
index 0000000000000..7246e37b33da9
--- /dev/null
+++ b/x-pack/plugins/security/public/components/breadcrumb.tsx
@@ -0,0 +1,137 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { createContext, useEffect, useRef, useContext, FunctionComponent } from 'react';
+import { EuiBreadcrumb } from '@elastic/eui';
+import { useKibana } from '../../../../../src/plugins/kibana_react/public';
+
+interface BreadcrumbsContext {
+  parents: BreadcrumbProps[];
+  onMount(breadcrumbs: BreadcrumbProps[]): void;
+  onUnmount(breadcrumbs: BreadcrumbProps[]): void;
+}
+
+const BreadcrumbsContext = createContext<BreadcrumbsContext | undefined>(undefined);
+
+export interface BreadcrumbProps extends EuiBreadcrumb {
+  text: string;
+}
+
+/**
+ * Component that automatically sets breadcrumbs and document title based on the render tree.
+ *
+ * @example
+ * // Breadcrumbs will be set to: "Users > Create"
+ * // Document title will be set to: "Create - Users"
+ *
+ * ```typescript
+ * <Breadcrumb text="Users">
+ *   <Table />
+ *   {showForm && (
+ *     <Breadcrumb text="Create">
+ *       <Form />
+ *     </Breadcrumb>
+ *   )}
+ * </Breadcrumb>
+ * ```
+ */
+export const Breadcrumb: FunctionComponent<BreadcrumbProps> = ({ children, ...breadcrumb }) => {
+  const context = useContext(BreadcrumbsContext);
+  const component = <InnerBreadcrumb breadcrumb={breadcrumb}>{children}</InnerBreadcrumb>;
+
+  if (context) {
+    return component;
+  }
+
+  return <BreadcrumbsProvider>{component}</BreadcrumbsProvider>;
+};
+
+export interface BreadcrumbsProviderProps {
+  onChange?: BreadcrumbsChangeHandler;
+}
+
+export type BreadcrumbsChangeHandler = (breadcrumbs: BreadcrumbProps[]) => void;
+
+/**
+ * Component that can be used to define any side effects that should occur when breadcrumbs change.
+ *
+ * By default the breadcrumbs in application chrome are set and the document title is updated.
+ *
+ * @example
+ * ```typescript
+ * <Breadcrumbs onChange={(breadcrumbs) => setBreadcrumbs(breadcrumbs)}>
+ *   <Breadcrumb text="Users" />
+ * </Breadcrumbs>
+ * ```
+ */
+export const BreadcrumbsProvider: FunctionComponent<BreadcrumbsProviderProps> = ({
+  children,
+  onChange,
+}) => {
+  const { services } = useKibana();
+  const breadcrumbsRef = useRef<BreadcrumbProps[]>([]);
+
+  const handleChange = (breadcrumbs: BreadcrumbProps[]) => {
+    if (onChange) {
+      onChange(breadcrumbs);
+    } else if (services.chrome) {
+      services.chrome.setBreadcrumbs(breadcrumbs);
+      services.chrome.docTitle.change(getDocTitle(breadcrumbs));
+    }
+  };
+
+  return (
+    <BreadcrumbsContext.Provider
+      value={{
+        parents: [],
+        onMount: (breadcrumbs) => {
+          if (breadcrumbs.length > breadcrumbsRef.current.length) {
+            breadcrumbsRef.current = breadcrumbs;
+            handleChange(breadcrumbs);
+          }
+        },
+        onUnmount: (breadcrumbs) => {
+          if (breadcrumbs.length < breadcrumbsRef.current.length) {
+            breadcrumbsRef.current = breadcrumbs;
+            handleChange(breadcrumbs);
+          }
+        },
+      }}
+    >
+      {children}
+    </BreadcrumbsContext.Provider>
+  );
+};
+
+export interface InnerBreadcrumbProps {
+  breadcrumb: BreadcrumbProps;
+}
+
+export const InnerBreadcrumb: FunctionComponent<InnerBreadcrumbProps> = ({
+  breadcrumb,
+  children,
+}) => {
+  const { parents, onMount, onUnmount } = useContext(BreadcrumbsContext)!;
+  const nextParents = [...parents, breadcrumb];
+
+  useEffect(() => {
+    onMount(nextParents);
+    return () => onUnmount(parents);
+  }, [breadcrumb.text, breadcrumb.href]); // eslint-disable-line react-hooks/exhaustive-deps
+
+  return (
+    <BreadcrumbsContext.Provider value={{ parents: nextParents, onMount, onUnmount }}>
+      {children}
+    </BreadcrumbsContext.Provider>
+  );
+};
+
+export function getDocTitle(breadcrumbs: BreadcrumbProps[], maxBreadcrumbs = 2) {
+  return breadcrumbs
+    .slice(0, maxBreadcrumbs)
+    .reverse()
+    .map(({ text }) => text);
+}
diff --git a/x-pack/plugins/security/public/components/confirm_modal.tsx b/x-pack/plugins/security/public/components/confirm_modal.tsx
new file mode 100644
index 0000000000000..8dfbf9e3f0649
--- /dev/null
+++ b/x-pack/plugins/security/public/components/confirm_modal.tsx
@@ -0,0 +1,94 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FunctionComponent } from 'react';
+import {
+  EuiButton,
+  EuiButtonProps,
+  EuiButtonEmpty,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiModal,
+  EuiModalBody,
+  EuiModalFooter,
+  EuiModalHeader,
+  EuiModalHeaderTitle,
+  EuiModalProps,
+  EuiOverlayMask,
+} from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+
+export interface ConfirmModalProps extends Omit<EuiModalProps, 'onClose' | 'initialFocus'> {
+  confirmButtonText: string;
+  confirmButtonColor?: EuiButtonProps['color'];
+  isLoading?: EuiButtonProps['isLoading'];
+  isDisabled?: EuiButtonProps['isDisabled'];
+  onCancel(): void;
+  onConfirm(): void;
+  ownFocus?: boolean;
+}
+
+/**
+ * Component that renders a confirmation modal similar to `EuiConfirmModal`, except that
+ * it adds `isLoading` prop, which renders a loading spinner and disables action buttons,
+ * and `ownFocus` prop to render overlay mask.
+ */
+export const ConfirmModal: FunctionComponent<ConfirmModalProps> = ({
+  children,
+  confirmButtonColor: buttonColor,
+  confirmButtonText,
+  isLoading,
+  isDisabled,
+  onCancel,
+  onConfirm,
+  ownFocus = true,
+  title,
+  ...rest
+}) => {
+  const modal = (
+    <EuiModal role="dialog" title={title} onClose={onCancel} {...rest}>
+      <EuiModalHeader>
+        <EuiModalHeaderTitle>{title}</EuiModalHeaderTitle>
+      </EuiModalHeader>
+      <EuiModalBody data-test-subj="confirmModalBodyText">{children}</EuiModalBody>
+      <EuiModalFooter>
+        <EuiFlexGroup justifyContent="flexEnd">
+          <EuiFlexItem grow={false}>
+            <EuiButtonEmpty
+              data-test-subj="confirmModalCancelButton"
+              flush="right"
+              isDisabled={isLoading}
+              onClick={onCancel}
+            >
+              <FormattedMessage
+                id="xpack.security.confirmModal.cancelButton"
+                defaultMessage="Cancel"
+              />
+            </EuiButtonEmpty>
+          </EuiFlexItem>
+          <EuiFlexItem grow={false}>
+            <EuiButton
+              data-test-subj="confirmModalConfirmButton"
+              color={buttonColor}
+              fill
+              isLoading={isLoading}
+              isDisabled={isDisabled}
+              onClick={onConfirm}
+            >
+              {confirmButtonText}
+            </EuiButton>
+          </EuiFlexItem>
+        </EuiFlexGroup>
+      </EuiModalFooter>
+    </EuiModal>
+  );
+
+  return ownFocus ? (
+    <EuiOverlayMask onClick={!isLoading ? onCancel : undefined}>{modal}</EuiOverlayMask>
+  ) : (
+    modal
+  );
+};
diff --git a/x-pack/plugins/security/public/components/doc_link.tsx b/x-pack/plugins/security/public/components/doc_link.tsx
new file mode 100644
index 0000000000000..50a93b8ee5090
--- /dev/null
+++ b/x-pack/plugins/security/public/components/doc_link.tsx
@@ -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;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useCallback, FunctionComponent } from 'react';
+import { EuiLink } from '@elastic/eui';
+import { useKibana } from '../../../../../src/plugins/kibana_react/public';
+import { CoreStart } from '../../../../../src/core/public';
+
+export type DocLinks = CoreStart['docLinks']['links'];
+export type GetDocLinkFunction = (app: string, doc: string) => string;
+
+/**
+ * Creates links to the documentation.
+ *
+ * @see {@link DocLink} for a component that creates a link to the docs.
+ *
+ * @example
+ * ```typescript
+ * <DocLink app="elasticsearch" doc="built-in-roles.html">
+ *   Learn what privileges individual roles grant.
+ * </DocLink>
+ * ```
+ *
+ * @example
+ * ```typescript
+ * const [docs] = useDocLinks();
+ *
+ * <EuiLink href={docs.dashboard.guide} target="_blank" external>
+ *   Learn how to get started with dashboards.
+ * </EuiLink>
+ * ```
+ */
+export function useDocLinks(): [DocLinks, GetDocLinkFunction] {
+  const { services } = useKibana();
+  const { links, ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = services.docLinks!;
+  const getDocLink = useCallback<GetDocLinkFunction>(
+    (app, doc) => {
+      return `${ELASTIC_WEBSITE_URL}guide/en/${app}/reference/${DOC_LINK_VERSION}/${doc}`;
+    },
+    [ELASTIC_WEBSITE_URL, DOC_LINK_VERSION]
+  );
+  return [links, getDocLink];
+}
+
+export interface DocLinkProps {
+  app: string;
+  doc: string;
+}
+
+export const DocLink: FunctionComponent<DocLinkProps> = ({ app, doc, children }) => {
+  const [, getDocLink] = useDocLinks();
+  return (
+    <EuiLink href={getDocLink(app, doc)} target="_blank" external>
+      {children}
+    </EuiLink>
+  );
+};
diff --git a/x-pack/plugins/security/public/components/form_flyout.tsx b/x-pack/plugins/security/public/components/form_flyout.tsx
new file mode 100644
index 0000000000000..a0d397f81751e
--- /dev/null
+++ b/x-pack/plugins/security/public/components/form_flyout.tsx
@@ -0,0 +1,97 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useEffect, FunctionComponent, RefObject } from 'react';
+import { FormattedMessage } from '@kbn/i18n/react';
+import {
+  EuiTitle,
+  EuiFlyout,
+  EuiFlyoutProps,
+  EuiFlyoutHeader,
+  EuiFlyoutBody,
+  EuiFlyoutFooter,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiButton,
+  EuiButtonProps,
+  EuiButtonEmpty,
+  EuiPortal,
+} from '@elastic/eui';
+import { useHtmlId } from './use_html_id';
+
+export interface FormFlyoutProps extends Omit<EuiFlyoutProps, 'onClose'> {
+  title: string;
+  initialFocus?: RefObject<HTMLElement>;
+  onCancel(): void;
+  onSubmit(): void;
+  submitButtonText: string;
+  submitButtonColor?: EuiButtonProps['color'];
+  isLoading?: EuiButtonProps['isLoading'];
+  isDisabled?: EuiButtonProps['isDisabled'];
+}
+
+export const FormFlyout: FunctionComponent<FormFlyoutProps> = ({
+  title,
+  submitButtonText,
+  submitButtonColor,
+  onCancel,
+  onSubmit,
+  isLoading,
+  isDisabled,
+  children,
+  initialFocus,
+  ...rest
+}) => {
+  useEffect(() => {
+    if (initialFocus && initialFocus.current) {
+      initialFocus.current.focus();
+    }
+  }, [initialFocus]);
+
+  const titleId = useHtmlId('formFlyout', 'title');
+
+  return (
+    <EuiPortal>
+      <EuiFlyout onClose={onCancel} aria-labelledby={titleId} {...rest}>
+        <EuiFlyoutHeader hasBorder>
+          <EuiTitle size="m">
+            <h2 id={titleId}>{title}</h2>
+          </EuiTitle>
+        </EuiFlyoutHeader>
+        <EuiFlyoutBody>{children}</EuiFlyoutBody>
+        <EuiFlyoutFooter>
+          <EuiFlexGroup justifyContent="spaceBetween">
+            <EuiFlexItem grow={false}>
+              <EuiButtonEmpty
+                data-test-subj="formFlyoutCancelButton"
+                flush="right"
+                isDisabled={isLoading}
+                onClick={onCancel}
+              >
+                <FormattedMessage
+                  id="xpack.security.formFlyout.cancelButton"
+                  defaultMessage="Cancel"
+                />
+              </EuiButtonEmpty>
+            </EuiFlexItem>
+            <EuiFlexItem grow={false}>
+              <EuiButton
+                data-test-subj="formFlyoutSubmitButton"
+                isLoading={isLoading}
+                isDisabled={isDisabled}
+                color={submitButtonColor}
+                fill
+                onClick={onSubmit}
+              >
+                {submitButtonText}
+              </EuiButton>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </EuiFlyoutFooter>
+      </EuiFlyout>
+    </EuiPortal>
+  );
+};
diff --git a/x-pack/plugins/security/public/components/use_current_user.ts b/x-pack/plugins/security/public/components/use_current_user.ts
new file mode 100644
index 0000000000000..b686e0ae9d778
--- /dev/null
+++ b/x-pack/plugins/security/public/components/use_current_user.ts
@@ -0,0 +1,24 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import useAsync from 'react-use/lib/useAsync';
+import constate from 'constate';
+import { AuthenticationServiceSetup } from '../authentication';
+
+export interface AuthenticationProviderProps {
+  authc: AuthenticationServiceSetup;
+}
+
+const [AuthenticationProvider, useAuthentication] = constate(
+  ({ authc }: AuthenticationProviderProps) => authc
+);
+
+export { AuthenticationProvider, useAuthentication };
+
+export function useCurrentUser() {
+  const authc = useAuthentication();
+  return useAsync(authc.getCurrentUser, [authc]);
+}
diff --git a/x-pack/plugins/security/public/components/use_form.ts b/x-pack/plugins/security/public/components/use_form.ts
new file mode 100644
index 0000000000000..33c7e184ec171
--- /dev/null
+++ b/x-pack/plugins/security/public/components/use_form.ts
@@ -0,0 +1,205 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { ChangeEventHandler, FocusEventHandler, ReactEventHandler, useState } from 'react';
+import { get, set, cloneDeep, cloneDeepWith } from 'lodash';
+import useAsyncFn from 'react-use/lib/useAsyncFn';
+
+export type FormReturnTuple<Values, Result> = [FormState<Values, Result>, FormProps];
+
+export interface FormProps {
+  onSubmit: ReactEventHandler;
+  onChange: ChangeEventHandler<HTMLFormElement & HTMLInputElement>;
+  onBlur: FocusEventHandler<HTMLFormElement & HTMLInputElement>;
+}
+
+export interface FormOptions<Values, Result> {
+  onSubmit: SubmitCallback<Values, Result>;
+  validate: ValidateCallback<Values>;
+  defaultValues: Values;
+}
+
+/**
+ * Returns state and {@link HTMLFormElement} event handlers useful for creating
+ * forms with inline validation.
+ *
+ * @see {@link useFormState} if you don't want to use {@link HTMLFormElement}.
+ *
+ * @example
+ * ```typescript
+ * const [form, eventHandlers] = useForm({
+ *   onSubmit: (values) => apiClient.create(values),
+ *   validate: (values) => !values.email ? { email: 'Required' } : {}
+ * });
+ *
+ * <EuiForm component="form" {...eventHandlers}>
+ *   <EuiFieldText name="email" isInvalid={form.touched.email && form.errors.email} />
+ *   <EuiButton type="submit">Submit</EuiButton>
+ * <EuiForm>
+ * ```
+ */
+export function useForm<Values extends FormValues, Result>(
+  options: FormOptions<Values, Result>
+): FormReturnTuple<Values, Result> {
+  const form = useFormState(options);
+
+  const eventHandlers: FormProps = {
+    onSubmit: (event) => {
+      event.preventDefault();
+      form.submit();
+    },
+    onChange: (event) => {
+      const { name, type, checked, value } = event.target;
+      if (name) {
+        form.setValue(name, type === 'checkbox' ? checked : value);
+      }
+    },
+    onBlur: (event) => {
+      const { name } = event.target;
+      if (name) {
+        form.setTouched(event.target.name);
+      }
+    },
+  };
+
+  return [form, eventHandlers];
+}
+
+export type FormValues = Record<string, any>;
+export type SubmitCallback<Values, Result> = (values: Values) => Promise<Result>;
+export type ValidateCallback<Values> = (
+  values: Values
+) => ValidationErrors<Values> | Promise<ValidationErrors<Values>>;
+export type ValidationErrors<Values> = DeepMap<Values, string>;
+export type TouchedFields<Values> = DeepMap<Values, boolean>;
+
+export interface FormState<Values, Result> {
+  setValue(name: string, value: any): Promise<void>;
+  setError(name: string, message: string): void;
+  setTouched(name: string): Promise<void>;
+  reset(values: Values): void;
+  submit(): Promise<Result | undefined>;
+  values: Values;
+  errors: ValidationErrors<Values>;
+  touched: TouchedFields<Values>;
+  isValidating: boolean;
+  isSubmitting: boolean;
+  submitError: Error | undefined;
+  isInvalid: boolean;
+  isSubmitted: boolean;
+}
+
+/**
+ * Returns state useful for creating forms with inline validation.
+ *
+ * @example
+ * ```typescript
+ * const form = useFormState({
+ *   onSubmit: (values) => apiClient.create(values),
+ *   validate: (values) => !values.toggle ? { toggle: 'Required' } : {}
+ * });
+ *
+ * <EuiSwitch
+ *   checked={form.values.toggle}
+ *   onChange={(e) => form.setValue('toggle', e.target.checked)}
+ *   onBlur={() => form.setTouched('toggle')}
+ *   isInvalid={!!form.errors.toggle}
+ * />
+ * <EuiButton onClick={form.submit}>
+ *   Submit
+ * </EuiButton>
+ * ```
+ */
+export function useFormState<Values extends FormValues, Result>({
+  onSubmit,
+  validate,
+  defaultValues,
+}: FormOptions<Values, Result>): FormState<Values, Result> {
+  const [values, setValues] = useState<Values>(defaultValues);
+  const [errors, setErrors] = useState<ValidationErrors<Values>>({});
+  const [touched, setTouched] = useState<TouchedFields<Values>>({});
+  const [submitCount, setSubmitCount] = useState(0);
+
+  const [validationState, validateForm] = useAsyncFn(
+    async (formValues: Values) => {
+      const nextErrors = await validate(formValues);
+      setErrors(nextErrors);
+      if (Object.keys(nextErrors).length === 0) {
+        setSubmitCount(0);
+      }
+      return nextErrors;
+    },
+    [validate]
+  );
+
+  const [submitState, submitForm] = useAsyncFn(
+    async (formValues: Values) => {
+      const nextErrors = await validateForm(formValues);
+      setTouched(mapDeep(formValues, true));
+      setSubmitCount(submitCount + 1);
+      if (Object.keys(nextErrors).length === 0) {
+        return onSubmit(formValues);
+      }
+    },
+    [validateForm, onSubmit]
+  );
+
+  return {
+    setValue: async (name, value) => {
+      const nextValues = setDeep(values, name, value);
+      setValues(nextValues);
+      await validateForm(nextValues);
+    },
+    setTouched: async (name, value = true) => {
+      setTouched(setDeep(touched, name, value));
+      await validateForm(values);
+    },
+    setError: (name, message) => {
+      setErrors(setDeep(errors, name, message));
+      setTouched(setDeep(touched, name, true));
+    },
+    reset: (nextValues) => {
+      setValues(nextValues);
+      setErrors({});
+      setTouched({});
+      setSubmitCount(0);
+    },
+    submit: () => submitForm(values),
+    values,
+    errors,
+    touched,
+    isValidating: validationState.loading,
+    isSubmitting: submitState.loading,
+    submitError: submitState.error,
+    isInvalid: Object.keys(errors).length > 0,
+    isSubmitted: submitCount > 0,
+  };
+}
+
+type DeepMap<T, TValue> = {
+  [K in keyof T]?: T[K] extends any[]
+    ? T[K][number] extends object
+      ? Array<DeepMap<T[K][number], TValue>>
+      : TValue
+    : T[K] extends object
+    ? DeepMap<T[K], TValue>
+    : TValue;
+};
+
+function mapDeep<T, V>(values: T, value: V): DeepMap<T, V> {
+  return cloneDeepWith(values, (v) => {
+    if (typeof v !== 'object' && v !== null) {
+      return value;
+    }
+  });
+}
+
+function setDeep<T extends object, V>(values: T, name: string, value: V): T {
+  if (get(values, name) !== value) {
+    return set(cloneDeep(values), name, value);
+  }
+  return values;
+}
diff --git a/x-pack/plugins/security/public/components/use_html_id.ts b/x-pack/plugins/security/public/components/use_html_id.ts
new file mode 100644
index 0000000000000..23666e83cbf23
--- /dev/null
+++ b/x-pack/plugins/security/public/components/use_html_id.ts
@@ -0,0 +1,27 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { useMemo } from 'react';
+import { htmlIdGenerator } from '@elastic/eui';
+
+/**
+ * Generates an ID that can be used for HTML elements.
+ *
+ * @param prefix Prefix of the id to be generated
+ * @param suffix Suffix of the id to be generated
+ *
+ * @example
+ * ```typescript
+ * const titleId = useHtmlId('changePasswordForm', 'title');
+ *
+ * <EuiForm aria-labelledby={titleId}>
+ *   <h2 id={titleId}>Change password</h2>
+ * </EuiForm>
+ * ```
+ */
+export function useHtmlId(prefix?: string, suffix?: string) {
+  return useMemo(() => htmlIdGenerator(prefix)(suffix), [prefix, suffix]);
+}
diff --git a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.test.tsx b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.test.tsx
index c5582d3526242..b7808ffb30e74 100644
--- a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.test.tsx
+++ b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.test.tsx
@@ -5,141 +5,153 @@
  */
 
 import React from 'react';
-import { mountWithIntl } from '@kbn/test/jest';
-
+import { shallowWithIntl } from '@kbn/test/jest';
 import { RoleComboBox } from '.';
-import { EuiComboBox } from '@elastic/eui';
-import { findTestSubject } from '@kbn/test/jest';
 
 describe('RoleComboBox', () => {
-  it('renders the provided list of roles via EuiComboBox options', () => {
-    const availableRoles = [
-      {
-        name: 'role-1',
-        elasticsearch: { cluster: [], indices: [], run_as: [] },
-        kibana: [],
-        metadata: {},
-      },
-      {
-        name: 'role-2',
-        elasticsearch: { cluster: [], indices: [], run_as: [] },
-        kibana: [],
-        metadata: {},
-      },
-    ];
-    const wrapper = mountWithIntl(
-      <RoleComboBox availableRoles={availableRoles} selectedRoleNames={[]} onChange={jest.fn()} />
-    );
-
-    expect(wrapper.find(EuiComboBox).props().options).toMatchInlineSnapshot(`
-      Array [
-        Object {
-          "color": "default",
-          "data-test-subj": "roleOption-role-1",
-          "label": "role-1",
-          "value": Object {
-            "isDeprecated": false,
+  it('renders roles grouped by custom, user, admin, system and deprecated roles with correct color', () => {
+    const wrapper = shallowWithIntl(
+      <RoleComboBox
+        availableRoles={[
+          {
+            name: 'custom_role',
+            elasticsearch: { cluster: [], indices: [], run_as: [] },
+            kibana: [],
+            metadata: {},
           },
-        },
-        Object {
-          "color": "default",
-          "data-test-subj": "roleOption-role-2",
-          "label": "role-2",
-          "value": Object {
-            "isDeprecated": false,
+          {
+            name: 'reserved_role',
+            elasticsearch: { cluster: [], indices: [], run_as: [] },
+            kibana: [],
+            metadata: { _reserved: true, _deprecated: false },
           },
-        },
-      ]
-    `);
-  });
-
-  it('renders deprecated roles as such', () => {
-    const availableRoles = [
-      {
-        name: 'role-1',
-        elasticsearch: { cluster: [], indices: [], run_as: [] },
-        kibana: [],
-        metadata: { _deprecated: true },
-      },
-    ];
-    const wrapper = mountWithIntl(
-      <RoleComboBox availableRoles={availableRoles} selectedRoleNames={[]} onChange={jest.fn()} />
-    );
-
-    expect(wrapper.find(EuiComboBox).props().options).toMatchInlineSnapshot(`
-      Array [
-        Object {
-          "color": "warning",
-          "data-test-subj": "roleOption-role-1",
-          "label": "role-1",
-          "value": Object {
-            "isDeprecated": true,
+          {
+            name: 'some_admin',
+            elasticsearch: { cluster: [], indices: [], run_as: [] },
+            kibana: [],
+            metadata: { _reserved: true, _deprecated: false },
           },
-        },
-      ]
-    `);
-  });
-
-  it('renders the selected role names in the expanded list, coded according to deprecated status', () => {
-    const availableRoles = [
-      {
-        name: 'role-1',
-        elasticsearch: { cluster: [], indices: [], run_as: [] },
-        kibana: [],
-        metadata: {},
-      },
-      {
-        name: 'role-2',
-        elasticsearch: { cluster: [], indices: [], run_as: [] },
-        kibana: [],
-        metadata: {},
-      },
-    ];
-    const wrapper = mountWithIntl(
-      <div>
-        <RoleComboBox availableRoles={availableRoles} selectedRoleNames={[]} onChange={jest.fn()} />
-      </div>
+          {
+            name: 'some_system',
+            elasticsearch: { cluster: [], indices: [], run_as: [] },
+            kibana: [],
+            metadata: { _reserved: true, _deprecated: false },
+          },
+          {
+            name: 'deprecated_role',
+            elasticsearch: { cluster: [], indices: [], run_as: [] },
+            kibana: [],
+            metadata: { _reserved: true, _deprecated: true },
+          },
+        ]}
+        selectedRoleNames={[]}
+        onChange={jest.fn()}
+      />
     );
 
-    findTestSubject(wrapper, 'comboBoxToggleListButton').simulate('click');
-
-    wrapper.find(EuiComboBox).setState({ isListOpen: true });
-
-    expect(findTestSubject(wrapper, 'rolesDropdown-renderOption')).toMatchInlineSnapshot(`
-      Array [
-        <div
-          className="euiText euiText--medium"
-          data-test-subj="rolesDropdown-renderOption"
-        >
-          <EuiTextColor
-            color="default"
-            component="div"
-          >
-            <div
-              className="euiTextColor euiTextColor--default"
-            >
-              role-1
-               
-            </div>
-          </EuiTextColor>
-        </div>,
-        <div
-          className="euiText euiText--medium"
-          data-test-subj="rolesDropdown-renderOption"
-        >
-          <EuiTextColor
-            color="default"
-            component="div"
-          >
-            <div
-              className="euiTextColor euiTextColor--default"
-            >
-              role-2
-               
-            </div>
-          </EuiTextColor>
-        </div>,
-      ]
+    expect(wrapper).toMatchInlineSnapshot(`
+      <EuiComboBox
+        async={false}
+        compressed={false}
+        data-test-subj="rolesDropdown"
+        fullWidth={false}
+        isClearable={true}
+        onChange={[Function]}
+        options={
+          Array [
+            Object {
+              "label": "Custom roles",
+              "options": Array [
+                Object {
+                  "color": undefined,
+                  "data-test-subj": "roleOption-custom_role",
+                  "label": "custom_role",
+                  "value": Object {
+                    "deprecatedReason": undefined,
+                    "isAdmin": false,
+                    "isDeprecated": false,
+                    "isReserved": false,
+                    "isSystem": false,
+                  },
+                },
+              ],
+            },
+            Object {
+              "label": "User roles",
+              "options": Array [
+                Object {
+                  "color": "primary",
+                  "data-test-subj": "roleOption-reserved_role",
+                  "label": "reserved_role",
+                  "value": Object {
+                    "deprecatedReason": undefined,
+                    "isAdmin": false,
+                    "isDeprecated": false,
+                    "isReserved": true,
+                    "isSystem": false,
+                  },
+                },
+              ],
+            },
+            Object {
+              "label": "Admin roles",
+              "options": Array [
+                Object {
+                  "color": "primary",
+                  "data-test-subj": "roleOption-some_admin",
+                  "label": "some_admin",
+                  "value": Object {
+                    "deprecatedReason": undefined,
+                    "isAdmin": true,
+                    "isDeprecated": false,
+                    "isReserved": true,
+                    "isSystem": false,
+                  },
+                },
+              ],
+            },
+            Object {
+              "label": "System roles",
+              "options": Array [
+                Object {
+                  "color": "primary",
+                  "data-test-subj": "roleOption-some_system",
+                  "label": "some_system",
+                  "value": Object {
+                    "deprecatedReason": undefined,
+                    "isAdmin": false,
+                    "isDeprecated": false,
+                    "isReserved": true,
+                    "isSystem": true,
+                  },
+                },
+              ],
+            },
+            Object {
+              "label": "Deprecated roles",
+              "options": Array [
+                Object {
+                  "color": "warning",
+                  "data-test-subj": "roleOption-deprecated_role",
+                  "label": "deprecated_role",
+                  "value": Object {
+                    "deprecatedReason": undefined,
+                    "isAdmin": false,
+                    "isDeprecated": true,
+                    "isReserved": true,
+                    "isSystem": false,
+                  },
+                },
+              ],
+            },
+          ]
+        }
+        placeholder="Select roles"
+        renderOption={[Function]}
+        selectedOptions={Array []}
+        singleSelection={false}
+        sortMatchesBy="none"
+      />
     `);
   });
 });
diff --git a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.tsx b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.tsx
index 5b24b296b299f..91d953c4aa29a 100644
--- a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.tsx
+++ b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.tsx
@@ -6,11 +6,28 @@
 
 import React from 'react';
 import { i18n } from '@kbn/i18n';
-import { EuiComboBox } from '@elastic/eui';
-import { Role, isRoleDeprecated } from '../../../common/model';
-import { RoleComboBoxOption } from './role_combo_box_option';
+import { FormattedMessage } from '@kbn/i18n/react';
+import {
+  EuiBadge,
+  EuiComboBox,
+  EuiComboBoxProps,
+  EuiComboBoxOptionOption,
+  EuiFlexGroup,
+  EuiFlexItem,
+} from '@elastic/eui';
+import {
+  Role,
+  isRoleSystem,
+  isRoleAdmin,
+  isRoleReserved,
+  isRoleDeprecated,
+} from '../../../common/model';
 
-interface Props {
+interface Props
+  extends Omit<
+    EuiComboBoxProps<string>,
+    'onChange' | 'options' | 'selectedOptions' | 'renderOption'
+  > {
   availableRoles: Role[];
   selectedRoleNames: readonly string[];
   onChange: (selectedRoleNames: string[]) => void;
@@ -19,43 +36,132 @@ interface Props {
   isDisabled?: boolean;
 }
 
+type Option = EuiComboBoxOptionOption<{
+  isReserved: boolean;
+  isDeprecated: boolean;
+  isSystem: boolean;
+  isAdmin: boolean;
+  deprecatedReason?: string;
+}>;
+
 export const RoleComboBox = (props: Props) => {
   const onRolesChange = (selectedItems: Array<{ label: string }>) => {
     props.onChange(selectedItems.map((item) => item.label));
   };
 
-  const roleNameToOption = (roleName: string) => {
+  const roleNameToOption = (roleName: string): Option => {
     const roleDefinition = props.availableRoles.find((role) => role.name === roleName);
+    const isReserved: boolean = (roleDefinition && isRoleReserved(roleDefinition)) ?? false;
     const isDeprecated: boolean = (roleDefinition && isRoleDeprecated(roleDefinition)) ?? false;
+    const isSystem: boolean = (roleDefinition && isRoleSystem(roleDefinition)) ?? false;
+    const isAdmin: boolean = (roleDefinition && isRoleAdmin(roleDefinition)) ?? false;
     return {
-      color: isDeprecated ? 'warning' : 'default',
+      color: isDeprecated ? 'warning' : isReserved ? 'primary' : undefined,
       'data-test-subj': `roleOption-${roleName}`,
       label: roleName,
       value: {
+        isReserved,
         isDeprecated,
+        isSystem,
+        isAdmin,
+        deprecatedReason: roleDefinition?.metadata?._deprecated_reason,
       },
     };
   };
 
   const options = props.availableRoles.map((role) => roleNameToOption(role.name));
-
   const selectedOptions = props.selectedRoleNames.map(roleNameToOption);
+  const groupedOptions = options.reduce<Record<string, typeof options>>((acc, option) => {
+    const type = option.value?.isDeprecated
+      ? 'deprecated'
+      : option.value?.isSystem
+      ? 'system'
+      : option.value?.isAdmin
+      ? 'admin'
+      : option.value?.isReserved
+      ? 'user'
+      : 'custom';
+    if (!acc[type]) {
+      acc[type] = [];
+    }
+    acc[type].push(option);
+    return acc;
+  }, {});
 
   return (
     <EuiComboBox
       data-test-subj="rolesDropdown"
+      id={props.id}
       placeholder={
         props.placeholder ||
-        i18n.translate('xpack.security.management.users.editUser.addRolesPlaceholder', {
-          defaultMessage: 'Add roles',
+        i18n.translate('xpack.security.management.users.roleComboBox.placeholder', {
+          defaultMessage: 'Select roles',
         })
       }
       onChange={onRolesChange}
       isLoading={props.isLoading}
       isDisabled={props.isDisabled}
-      options={options}
+      options={[
+        {
+          label: i18n.translate('xpack.security.management.users.roleComboBox.customRoles', {
+            defaultMessage: 'Custom roles',
+          }),
+          options: groupedOptions.custom ?? [],
+        },
+        {
+          label: i18n.translate('xpack.security.management.users.roleComboBox.userRoles', {
+            defaultMessage: 'User roles',
+          }),
+          options: groupedOptions.user ?? [],
+        },
+        {
+          label: i18n.translate('xpack.security.management.users.roleComboBox.AdminRoles', {
+            defaultMessage: 'Admin roles',
+          }),
+          options: groupedOptions.admin ?? [],
+        },
+        {
+          label: i18n.translate('xpack.security.management.users.roleComboBox.systemRoles', {
+            defaultMessage: 'System roles',
+          }),
+          options: groupedOptions.system ?? [],
+        },
+        {
+          label: i18n.translate('xpack.security.management.users.roleComboBox.deprecatedRoles', {
+            defaultMessage: 'Deprecated roles',
+          }),
+          options: groupedOptions.deprecated ?? [],
+        },
+      ]}
       selectedOptions={selectedOptions}
-      renderOption={(option) => <RoleComboBoxOption option={option} />}
+      renderOption={renderOption}
     />
   );
 };
+
+function renderOption(option: Option) {
+  return (
+    <EuiFlexGroup justifyContent="spaceBetween" alignItems="center" responsive={false}>
+      <EuiFlexItem>{option.label}</EuiFlexItem>
+      {option.value?.isDeprecated ? (
+        <EuiFlexItem grow={false}>
+          <EuiBadge color={option.color}>
+            <FormattedMessage
+              id="xpack.security.management.users.roleComboBox.deprecatedBadge"
+              defaultMessage="deprecated"
+            />
+          </EuiBadge>
+        </EuiFlexItem>
+      ) : option.value?.isReserved ? (
+        <EuiFlexItem grow={false}>
+          <EuiBadge color={option.color}>
+            <FormattedMessage
+              id="xpack.security.management.users.roleComboBox.reservedBadge"
+              defaultMessage="built in"
+            />
+          </EuiBadge>
+        </EuiFlexItem>
+      ) : undefined}
+    </EuiFlexGroup>
+  );
+}
diff --git a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.test.tsx b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.test.tsx
deleted file mode 100644
index b24a48145b461..0000000000000
--- a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.test.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { shallowWithIntl } from '@kbn/test/jest';
-import { RoleComboBoxOption } from './role_combo_box_option';
-
-describe('RoleComboBoxOption', () => {
-  it('renders a regular role correctly', () => {
-    const wrapper = shallowWithIntl(
-      <RoleComboBoxOption
-        option={{
-          color: 'default',
-          label: 'role-1',
-        }}
-      />
-    );
-
-    expect(wrapper).toMatchInlineSnapshot(`
-      <EuiText
-        color="default"
-        data-test-subj="rolesDropdown-renderOption"
-      >
-        role-1
-         
-      </EuiText>
-    `);
-  });
-
-  it('renders a deprecated role correctly', () => {
-    const wrapper = shallowWithIntl(
-      <RoleComboBoxOption
-        option={{
-          color: 'warning',
-          label: 'role-1',
-          value: {
-            isDeprecated: true,
-          },
-        }}
-      />
-    );
-
-    expect(wrapper).toMatchInlineSnapshot(`
-      <EuiText
-        color="warning"
-        data-test-subj="rolesDropdown-renderOption"
-      >
-        role-1
-         
-        (deprecated)
-      </EuiText>
-    `);
-  });
-});
diff --git a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.tsx b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.tsx
deleted file mode 100644
index ae9b79c796275..0000000000000
--- a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-
-import { i18n } from '@kbn/i18n';
-
-import { EuiComboBoxOptionOption, EuiText } from '@elastic/eui';
-
-interface Props {
-  option: EuiComboBoxOptionOption<{ isDeprecated: boolean }>;
-}
-
-export const RoleComboBoxOption = ({ option }: Props) => {
-  const isDeprecated = option.value?.isDeprecated ?? false;
-  const deprecatedLabel = i18n.translate(
-    'xpack.security.management.users.editUser.deprecatedRoleText',
-    {
-      defaultMessage: '(deprecated)',
-    }
-  );
-
-  return (
-    <EuiText color={option.color as any} data-test-subj="rolesDropdown-renderOption">
-      {option.label} {isDeprecated ? deprecatedLabel : ''}
-    </EuiText>
-  );
-};
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx
index c750ec373b9f7..df5e5c8be9025 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx
@@ -407,7 +407,7 @@ export const EditRolePage: FunctionComponent<Props> = ({
   const onNameChange = (e: ChangeEvent<HTMLInputElement>) =>
     setRole({
       ...role,
-      name: e.target.value.replace(/\s/g, '_'),
+      name: e.target.value,
     });
 
   const getElasticsearchPrivileges = () => {
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.test.ts b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.test.ts
index 868674aec6f86..e6b9b19022f31 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.test.ts
+++ b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.test.ts
@@ -40,7 +40,7 @@ describe('validateRoleName', () => {
 
     expect(validator.validateRoleName(role)).toEqual({
       isInvalid: true,
-      error: `Please provide a role name`,
+      error: `Please provide a role name.`,
     });
   });
 
@@ -57,13 +57,30 @@ describe('validateRoleName', () => {
 
     expect(validator.validateRoleName(role)).toEqual({
       isInvalid: true,
-      error: `Name must not exceed 1024 characters`,
+      error: `Name must not exceed 1024 characters.`,
+    });
+  });
+
+  test('it cannot start with whitespace character', () => {
+    const role = {
+      name: ' role-name',
+      elasticsearch: {
+        cluster: [],
+        indices: [],
+        run_as: [],
+      },
+      kibana: [],
+    };
+
+    expect(validator.validateRoleName(role)).toEqual({
+      isInvalid: true,
+      error: `Name must not contain leading or trailing spaces.`,
     });
   });
 
   const charList = `!#%^&*()+=[]{}\|';:"/,<>?`.split('');
   charList.forEach((element) => {
-    test(`it cannot support the "${element}" character`, () => {
+    test(`it allows the "${element}" character`, () => {
       const role = {
         name: `role-${element}`,
         elasticsearch: {
@@ -74,10 +91,7 @@ describe('validateRoleName', () => {
         kibana: [],
       };
 
-      expect(validator.validateRoleName(role)).toEqual({
-        isInvalid: true,
-        error: `Name must begin with a letter or underscore and contain only letters, underscores, and numbers.`,
-      });
+      expect(validator.validateRoleName(role)).toEqual({ isInvalid: false });
     });
   });
 });
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts
index 89b16b1467776..e0459bbd3dd0d 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts
+++ b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts
@@ -6,6 +6,7 @@
 
 import { i18n } from '@kbn/i18n';
 import { Role, RoleIndexPrivilege } from '../../../../common/model';
+import { NAME_REGEX, MAX_NAME_LENGTH } from '../../../../common/constants';
 
 interface RoleValidatorOptions {
   shouldValidate?: boolean;
@@ -41,25 +42,36 @@ export class RoleValidator {
         i18n.translate(
           'xpack.security.management.editRole.validateRole.provideRoleNameWarningMessage',
           {
-            defaultMessage: 'Please provide a role name',
+            defaultMessage: 'Please provide a role name.',
           }
         )
       );
     }
-    if (role.name.length > 1024) {
+    if (role.name.length > MAX_NAME_LENGTH) {
       return invalid(
         i18n.translate('xpack.security.management.editRole.validateRole.nameLengthWarningMessage', {
-          defaultMessage: 'Name must not exceed 1024 characters',
+          defaultMessage: 'Name must not exceed {maxLength} characters.',
+          values: { maxLength: MAX_NAME_LENGTH },
         })
       );
     }
-    if (!role.name.match(/^[a-zA-Z_][a-zA-Z0-9_@\-\$\.]*$/)) {
+    if (role.name.trim() !== role.name) {
+      return invalid(
+        i18n.translate(
+          'xpack.security.management.editRole.validateRole.nameWhitespaceWarningMessage',
+          {
+            defaultMessage: `Name must not contain leading or trailing spaces.`,
+          }
+        )
+      );
+    }
+    if (!role.name.match(NAME_REGEX)) {
       return invalid(
         i18n.translate(
           'xpack.security.management.editRole.validateRole.nameAllowedCharactersWarningMessage',
           {
             defaultMessage:
-              'Name must begin with a letter or underscore and contain only letters, underscores, and numbers.',
+              'Name must contain only letters, numbers, spaces, punctuation and printable symbols.',
           }
         )
       );
diff --git a/x-pack/plugins/security/public/management/users/edit_user/change_password_flyout.tsx b/x-pack/plugins/security/public/management/users/edit_user/change_password_flyout.tsx
new file mode 100644
index 0000000000000..2586b7c24bf4c
--- /dev/null
+++ b/x-pack/plugins/security/public/management/users/edit_user/change_password_flyout.tsx
@@ -0,0 +1,286 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FunctionComponent } from 'react';
+import {
+  EuiCallOut,
+  EuiFieldPassword,
+  EuiFlexGroup,
+  EuiForm,
+  EuiFormRow,
+  EuiIcon,
+  EuiLoadingContent,
+  EuiSpacer,
+  EuiText,
+  EuiFlexItem,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
+import { useForm, ValidationErrors } from '../../../components/use_form';
+import { useCurrentUser } from '../../../components/use_current_user';
+import { FormFlyout } from '../../../components/form_flyout';
+import { UserAPIClient } from '..';
+
+export interface ChangePasswordFormValues {
+  current_password?: string;
+  password: string;
+  confirm_password: string;
+}
+
+export interface ChangePasswordFlyoutProps {
+  username: string;
+  defaultValues?: ChangePasswordFormValues;
+  onCancel(): void;
+  onSuccess?(): void;
+}
+
+export const ChangePasswordFlyout: FunctionComponent<ChangePasswordFlyoutProps> = ({
+  username,
+  defaultValues = {
+    current_password: '',
+    password: '',
+    confirm_password: '',
+  },
+  onSuccess,
+  onCancel,
+}) => {
+  const { services } = useKibana();
+  const { value: currentUser, loading: isLoading } = useCurrentUser();
+  const isCurrentUser = currentUser?.username === username;
+  const isSystemUser = username === 'kibana' || username === 'kibana_system';
+
+  const [form, eventHandlers] = useForm({
+    onSubmit: async (values) => {
+      try {
+        await new UserAPIClient(services.http!).changePassword(
+          username,
+          values.password,
+          values.current_password
+        );
+        services.notifications!.toasts.addSuccess(
+          i18n.translate('xpack.security.management.users.changePasswordFlyout.successMessage', {
+            defaultMessage: "Password changed for '{username}'.",
+            values: { username },
+          })
+        );
+        onSuccess?.();
+      } catch (error) {
+        if ((error as any).body?.message === 'security_exception') {
+          form.setError(
+            'current_password',
+            i18n.translate(
+              'xpack.security.management.users.changePasswordFlyout.currentPasswordInvalidError',
+              {
+                defaultMessage: 'Invalid password.',
+              }
+            )
+          );
+        } else {
+          services.notifications!.toasts.addDanger({
+            title: i18n.translate(
+              'xpack.security.management.users.changePasswordFlyout.errorMessage',
+              {
+                defaultMessage: 'Could not change password',
+              }
+            ),
+            text: (error as any).body?.message || error.message,
+          });
+          throw error;
+        }
+      }
+    },
+    validate: async (values) => {
+      const errors: ValidationErrors<typeof values> = {};
+
+      if (isCurrentUser) {
+        if (!values.current_password) {
+          errors.current_password = i18n.translate(
+            'xpack.security.management.users.changePasswordFlyout.currentPasswordRequiredError',
+            {
+              defaultMessage: 'Enter your current password.',
+            }
+          );
+        }
+      }
+
+      if (!values.password) {
+        errors.password = i18n.translate(
+          'xpack.security.management.users.changePasswordFlyout.passwordRequiredError',
+          {
+            defaultMessage: 'Enter a new password.',
+          }
+        );
+      } else if (values.password.length < 6) {
+        errors.password = i18n.translate(
+          'xpack.security.management.users.changePasswordFlyout.passwordInvalidError',
+          {
+            defaultMessage: 'Password must be at least 6 characters.',
+          }
+        );
+      } else if (!values.confirm_password) {
+        errors.confirm_password = i18n.translate(
+          'xpack.security.management.users.changePasswordFlyout.confirmPasswordRequiredError',
+          {
+            defaultMessage: 'Passwords do not match.',
+          }
+        );
+      } else if (values.password !== values.confirm_password) {
+        errors.confirm_password = i18n.translate(
+          'xpack.security.management.users.changePasswordFlyout.confirmPasswordInvalidError',
+          {
+            defaultMessage: 'Passwords do not match.',
+          }
+        );
+      }
+
+      return errors;
+    },
+    defaultValues,
+  });
+
+  return (
+    <FormFlyout
+      title={i18n.translate('xpack.security.management.users.changePasswordFlyout.title', {
+        defaultMessage: 'Change password',
+      })}
+      onCancel={onCancel}
+      onSubmit={form.submit}
+      submitButtonText={
+        isSystemUser
+          ? i18n.translate(
+              'xpack.security.management.users.changePasswordFlyout.confirmSystemPasswordButton',
+              {
+                defaultMessage:
+                  '{isSubmitting, select, true{Changing password…} other{Change password}}',
+                values: { isSubmitting: form.isSubmitting },
+              }
+            )
+          : i18n.translate('xpack.security.management.users.changePasswordFlyout.confirmButton', {
+              defaultMessage:
+                '{isSubmitting, select, true{Changing password…} other{Change password}}',
+              values: { isSubmitting: form.isSubmitting },
+            })
+      }
+      submitButtonColor={isSystemUser ? 'danger' : undefined}
+      isLoading={form.isSubmitting}
+      isDisabled={isLoading || (form.isSubmitted && form.isInvalid)}
+      size="s"
+      ownFocus
+    >
+      {isLoading ? (
+        <EuiLoadingContent />
+      ) : (
+        <EuiForm component="form" noValidate {...eventHandlers}>
+          {isSystemUser ? (
+            <>
+              <EuiCallOut
+                title={i18n.translate(
+                  'xpack.security.management.users.changePasswordFlyout.systemUserTitle',
+                  { defaultMessage: 'This is extremely important!' }
+                )}
+                color="danger"
+                iconType="alert"
+              >
+                <p>
+                  <FormattedMessage
+                    id="xpack.security.management.users.changePasswordFlyout.systemUserWarning"
+                    defaultMessage="Changing this password will prevent Kibana from communicating with Elasticsearch."
+                  />
+                </p>
+                <p>
+                  <FormattedMessage
+                    id="xpack.security.management.users.changePasswordFlyout.systemUserDescription"
+                    defaultMessage="Once changed, you must manually update your config file with the new password and restart Kibana."
+                  />
+                </p>
+              </EuiCallOut>
+              <EuiSpacer />
+            </>
+          ) : undefined}
+
+          <EuiFormRow
+            label={i18n.translate(
+              'xpack.security.management.users.changePasswordFlyout.userLabel',
+              {
+                defaultMessage: 'User',
+              }
+            )}
+          >
+            <EuiFlexGroup alignItems="center" gutterSize="s" responsive={false}>
+              <EuiFlexItem grow={false}>
+                <EuiIcon type="user" />
+              </EuiFlexItem>
+              <EuiFlexItem>
+                <EuiSpacer size="xs" />
+                <EuiText>{username}</EuiText>
+                <EuiSpacer size="xs" />
+              </EuiFlexItem>
+            </EuiFlexGroup>
+          </EuiFormRow>
+
+          {isCurrentUser ? (
+            <EuiFormRow
+              label={i18n.translate(
+                'xpack.security.management.users.changePasswordFlyout.currentPasswordLabel',
+                { defaultMessage: 'Current password' }
+              )}
+              error={form.errors.current_password}
+              isInvalid={form.touched.current_password && !!form.errors.current_password}
+            >
+              <EuiFieldPassword
+                name="current_password"
+                type="dual"
+                defaultValue={form.values.current_password}
+                isInvalid={form.touched.current_password && !!form.errors.current_password}
+                autoComplete="current-password"
+              />
+            </EuiFormRow>
+          ) : null}
+
+          <EuiFormRow
+            label={i18n.translate(
+              'xpack.security.management.users.changePasswordFlyout.passwordLabel',
+              {
+                defaultMessage: 'New password',
+              }
+            )}
+            helpText="Password must be at least 6 characters."
+            error={form.errors.password}
+            isInvalid={form.touched.password && !!form.errors.password}
+          >
+            <EuiFieldPassword
+              name="password"
+              type="dual"
+              defaultValue={form.values.password}
+              isInvalid={form.touched.password && !!form.errors.password}
+              autoComplete="new-password"
+            />
+          </EuiFormRow>
+          <EuiFormRow
+            label={i18n.translate(
+              'xpack.security.management.users.changePasswordFlyout.confirmPasswordLabel',
+              { defaultMessage: 'Confirm password' }
+            )}
+            error={form.errors.confirm_password}
+            isInvalid={form.touched.confirm_password && !!form.errors.confirm_password}
+          >
+            <EuiFieldPassword
+              name="confirm_password"
+              type="dual"
+              defaultValue={form.values.confirm_password}
+              isInvalid={form.touched.confirm_password && !!form.errors.confirm_password}
+              autoComplete="new-password"
+            />
+          </EuiFormRow>
+
+          {/* Hidden submit button is required for enter key to trigger form submission */}
+          <input type="submit" hidden />
+        </EuiForm>
+      )}
+    </FormFlyout>
+  );
+};
diff --git a/x-pack/plugins/security/public/management/users/edit_user/confirm_delete_users.tsx b/x-pack/plugins/security/public/management/users/edit_user/confirm_delete_users.tsx
new file mode 100644
index 0000000000000..18be46ebefed0
--- /dev/null
+++ b/x-pack/plugins/security/public/management/users/edit_user/confirm_delete_users.tsx
@@ -0,0 +1,96 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FunctionComponent } from 'react';
+import { EuiText } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import useAsyncFn from 'react-use/lib/useAsyncFn';
+import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
+import { ConfirmModal } from '../../../components/confirm_modal';
+import { UserAPIClient } from '..';
+
+export interface ConfirmDeleteUsersProps {
+  usernames: string[];
+  onCancel(): void;
+  onSuccess?(): void;
+}
+
+export const ConfirmDeleteUsers: FunctionComponent<ConfirmDeleteUsersProps> = ({
+  usernames,
+  onCancel,
+  onSuccess,
+}) => {
+  const { services } = useKibana();
+
+  const [state, deleteUsers] = useAsyncFn(async () => {
+    for (const username of usernames) {
+      try {
+        await new UserAPIClient(services.http!).deleteUser(username);
+        services.notifications!.toasts.addSuccess(
+          i18n.translate('xpack.security.management.users.confirmDeleteUsers.successMessage', {
+            defaultMessage: "Deleted user '{username}'",
+            values: { username },
+          })
+        );
+        onSuccess?.();
+      } catch (error) {
+        services.notifications!.toasts.addDanger({
+          title: i18n.translate('xpack.security.management.users.confirmDeleteUsers.errorMessage', {
+            defaultMessage: "Could not delete user '{username}'",
+            values: { username },
+          }),
+          text: (error as any).body?.message || error.message,
+        });
+      }
+    }
+  }, [services.http]);
+
+  return (
+    <ConfirmModal
+      title={i18n.translate('xpack.security.management.users.confirmDeleteUsers.title', {
+        defaultMessage: "Delete {count, plural, one{user '{username}'} other{{count} users}}?",
+        values: { count: usernames.length, username: usernames[0] },
+      })}
+      onCancel={onCancel}
+      onConfirm={deleteUsers}
+      confirmButtonText={i18n.translate(
+        'xpack.security.management.users.confirmDeleteUsers.confirmButton',
+        {
+          defaultMessage:
+            '{isLoading, select, true{Deleting {count, plural, one{user} other{users}}…} other{Delete {count, plural, one{user} other{users}}}}',
+          values: { count: usernames.length, isLoading: state.loading },
+        }
+      )}
+      confirmButtonColor="danger"
+      isLoading={state.loading}
+      ownFocus
+    >
+      <EuiText>
+        <p>
+          <FormattedMessage
+            id="xpack.security.management.users.confirmDeleteUsers.description"
+            defaultMessage="{count, plural, one{This user} other{These users}} will be permanently deleted and access to Elastic removed{count, plural, one{.} other{:}}"
+            values={{ count: usernames.length }}
+          />
+        </p>
+        {usernames.length > 1 && (
+          <ul>
+            {usernames.map((username) => (
+              <li key={username}>{username}</li>
+            ))}
+          </ul>
+        )}
+        <p>
+          <FormattedMessage
+            id="xpack.security.management.users.confirmDelete.cannotUndoWarning"
+            defaultMessage="You can't recover deleted users."
+          />
+        </p>
+      </EuiText>
+    </ConfirmModal>
+  );
+};
diff --git a/x-pack/plugins/security/public/management/users/edit_user/confirm_disable_users.tsx b/x-pack/plugins/security/public/management/users/edit_user/confirm_disable_users.tsx
new file mode 100644
index 0000000000000..b0a9e875c2089
--- /dev/null
+++ b/x-pack/plugins/security/public/management/users/edit_user/confirm_disable_users.tsx
@@ -0,0 +1,119 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FunctionComponent } from 'react';
+import { EuiText } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import useAsyncFn from 'react-use/lib/useAsyncFn';
+import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
+import { ConfirmModal } from '../../../components/confirm_modal';
+import { UserAPIClient } from '..';
+
+export interface ConfirmDisableUsersProps {
+  usernames: string[];
+  onCancel(): void;
+  onSuccess?(): void;
+}
+
+export const ConfirmDisableUsers: FunctionComponent<ConfirmDisableUsersProps> = ({
+  usernames,
+  onCancel,
+  onSuccess,
+}) => {
+  const { services } = useKibana();
+  const isSystemUser = usernames[0] === 'kibana' || usernames[0] === 'kibana_system';
+
+  const [state, disableUsers] = useAsyncFn(async () => {
+    for (const username of usernames) {
+      try {
+        await new UserAPIClient(services.http!).disableUser(username);
+        services.notifications!.toasts.addSuccess(
+          i18n.translate('xpack.security.management.users.confirmDisableUsers.successMessage', {
+            defaultMessage: "Deactivated user '{username}'",
+            values: { username },
+          })
+        );
+        onSuccess?.();
+      } catch (error) {
+        services.notifications!.toasts.addDanger({
+          title: i18n.translate(
+            'xpack.security.management.users.confirmDisableUsers.errorMessage',
+            {
+              defaultMessage: "Could not deactivate user '{username}'",
+              values: { username },
+            }
+          ),
+          text: (error as any).body?.message || error.message,
+        });
+      }
+    }
+  }, [services.http]);
+
+  return (
+    <ConfirmModal
+      title={i18n.translate('xpack.security.management.users.confirmDisableUsers.title', {
+        defaultMessage: "Deactivate {count, plural, one{user '{username}'} other{{count} users}}?",
+        values: { count: usernames.length, username: usernames[0] },
+      })}
+      onCancel={onCancel}
+      onConfirm={disableUsers}
+      confirmButtonText={
+        isSystemUser
+          ? i18n.translate(
+              'xpack.security.management.users.confirmDisableUsers.confirmSystemPasswordButton',
+              {
+                defaultMessage:
+                  '{isLoading, select, true{Deactivating user…} other{I understand, deactivate this user}}',
+                values: { isLoading: state.loading },
+              }
+            )
+          : i18n.translate('xpack.security.management.users.confirmDisableUsers.confirmButton', {
+              defaultMessage:
+                '{isLoading, select, true{Deactivating {count, plural, one{user} other{users}}…} other{Deactivate {count, plural, one{user} other{users}}}}',
+              values: { count: usernames.length, isLoading: state.loading },
+            })
+      }
+      confirmButtonColor={isSystemUser ? 'danger' : undefined}
+      isLoading={state.loading}
+      ownFocus
+    >
+      {isSystemUser ? (
+        <EuiText>
+          <p>
+            <FormattedMessage
+              id="xpack.security.management.users.confirmDisableUsers.systemUserWarning"
+              defaultMessage="Deactivating the system user will prevent Kibana from communicating with Elasticsearch."
+            />
+          </p>
+          <p>
+            <FormattedMessage
+              id="xpack.security.management.users.confirmDisableUsers.systemUserDescription"
+              defaultMessage="Once deactivated, you must manually update your config file with different user details and restart Kibana."
+            />
+          </p>
+        </EuiText>
+      ) : (
+        <EuiText>
+          <p>
+            <FormattedMessage
+              id="xpack.security.management.users.confirmDisableUsers.description"
+              defaultMessage="{count, plural, one{This user} other{These users}} will no longer be able to access Elastic{count, plural, one{.} other{:}}"
+              values={{ count: usernames.length }}
+            />
+          </p>
+          {usernames.length > 1 && (
+            <ul>
+              {usernames.map((username) => (
+                <li key={username}>{username}</li>
+              ))}
+            </ul>
+          )}
+        </EuiText>
+      )}
+    </ConfirmModal>
+  );
+};
diff --git a/x-pack/plugins/security/public/management/users/edit_user/confirm_enable_users.tsx b/x-pack/plugins/security/public/management/users/edit_user/confirm_enable_users.tsx
new file mode 100644
index 0000000000000..c9589cfa17da2
--- /dev/null
+++ b/x-pack/plugins/security/public/management/users/edit_user/confirm_enable_users.tsx
@@ -0,0 +1,89 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FunctionComponent } from 'react';
+import { EuiText } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import useAsyncFn from 'react-use/lib/useAsyncFn';
+import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
+import { ConfirmModal } from '../../../components/confirm_modal';
+import { UserAPIClient } from '..';
+
+export interface ConfirmEnableUsersProps {
+  usernames: string[];
+  onCancel(): void;
+  onSuccess?(): void;
+}
+
+export const ConfirmEnableUsers: FunctionComponent<ConfirmEnableUsersProps> = ({
+  usernames,
+  onCancel,
+  onSuccess,
+}) => {
+  const { services } = useKibana();
+
+  const [state, enableUsers] = useAsyncFn(async () => {
+    for (const username of usernames) {
+      try {
+        await new UserAPIClient(services.http!).enableUser(username);
+        services.notifications!.toasts.addSuccess(
+          i18n.translate('xpack.security.management.users.confirmEnableUsers.successMessage', {
+            defaultMessage: "Activated user '{username}'",
+            values: { username },
+          })
+        );
+        onSuccess?.();
+      } catch (error) {
+        services.notifications!.toasts.addDanger({
+          title: i18n.translate('xpack.security.management.users.confirmEnableUsers.errorMessage', {
+            defaultMessage: "Could not activate user '{username}'",
+            values: { username },
+          }),
+          text: (error as any).body?.message || error.message,
+        });
+      }
+    }
+  }, [services.http]);
+
+  return (
+    <ConfirmModal
+      title={i18n.translate('xpack.security.management.users.confirmEnableUsers.title', {
+        defaultMessage: "Activate {count, plural, one{user '{username}'} other{{count} users}}?",
+        values: { count: usernames.length, username: usernames[0] },
+      })}
+      onCancel={onCancel}
+      onConfirm={enableUsers}
+      confirmButtonText={i18n.translate(
+        'xpack.security.management.users.confirmEnableUsers.confirmButton',
+        {
+          defaultMessage:
+            '{isLoading, select, true{Activating {count, plural, one{user} other{users}}…} other{Activate {count, plural, one{user} other{users}}}}',
+          values: { count: usernames.length, isLoading: state.loading },
+        }
+      )}
+      isLoading={state.loading}
+      ownFocus
+    >
+      <EuiText>
+        <p>
+          <FormattedMessage
+            id="xpack.security.management.users.confirmEnableUsers.description"
+            defaultMessage="{count, plural, one{This user} other{These users}} will be able to access Elastic{count, plural, one{.} other{:}}"
+            values={{ count: usernames.length }}
+          />
+        </p>
+        {usernames.length > 1 && (
+          <ul>
+            {usernames.map((username) => (
+              <li key={username}>{username}</li>
+            ))}
+          </ul>
+        )}
+      </EuiText>
+    </ConfirmModal>
+  );
+};
diff --git a/x-pack/plugins/security/public/management/users/edit_user/create_user_page.test.tsx b/x-pack/plugins/security/public/management/users/edit_user/create_user_page.test.tsx
new file mode 100644
index 0000000000000..e7e3e1164ae14
--- /dev/null
+++ b/x-pack/plugins/security/public/management/users/edit_user/create_user_page.test.tsx
@@ -0,0 +1,108 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { render, fireEvent, waitFor, within } from '@testing-library/react';
+import { createMemoryHistory } from 'history';
+import { coreMock } from '../../../../../../../src/core/public/mocks';
+import { securityMock } from '../../../mocks';
+import { Providers } from '../users_management_app';
+import { CreateUserPage } from './create_user_page';
+
+jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({
+  htmlIdGenerator: () => () => `id-${Math.random()}`,
+}));
+
+describe('CreateUserPage', () => {
+  it('creates user when submitting form and redirects back', async () => {
+    const coreStart = coreMock.createStart();
+    const history = createMemoryHistory({ initialEntries: ['/create'] });
+    const authc = securityMock.createSetup().authc;
+    coreStart.http.post.mockResolvedValue({});
+
+    const { findByRole, findByLabelText } = render(
+      <Providers services={coreStart} authc={authc} history={history}>
+        <CreateUserPage />
+      </Providers>
+    );
+
+    fireEvent.change(await findByLabelText('Username'), { target: { value: 'jdoe' } });
+    fireEvent.change(await findByLabelText('Password'), { target: { value: 'changeme' } });
+    fireEvent.change(await findByLabelText('Confirm password'), {
+      target: { value: 'changeme' },
+    });
+    fireEvent.click(await findByRole('button', { name: 'Create user' }));
+
+    await waitFor(() => {
+      expect(coreStart.http.post).toHaveBeenLastCalledWith('/internal/security/users/jdoe', {
+        body: JSON.stringify({
+          password: 'changeme',
+          username: 'jdoe',
+          full_name: '',
+          email: '',
+          roles: [],
+        }),
+      });
+      expect(history.location.pathname).toBe('/');
+    });
+  });
+
+  it('validates form', async () => {
+    const coreStart = coreMock.createStart();
+    const history = createMemoryHistory({ initialEntries: ['/create'] });
+    const authc = securityMock.createSetup().authc;
+
+    coreStart.http.get.mockResolvedValueOnce([]);
+    coreStart.http.get.mockResolvedValueOnce([
+      {
+        username: 'existing_username',
+        full_name: '',
+        email: '',
+        enabled: true,
+        roles: ['superuser'],
+      },
+    ]);
+
+    const { findAllByText, findByRole, findByLabelText } = render(
+      <Providers services={coreStart} authc={authc} history={history}>
+        <CreateUserPage />
+      </Providers>
+    );
+
+    fireEvent.click(await findByRole('button', { name: 'Create user' }));
+
+    const alert = await findByRole('alert');
+    within(alert).getByText(/Enter a username/i);
+    within(alert).getByText(/Enter a password/i);
+
+    fireEvent.change(await findByLabelText('Username'), { target: { value: 'existing_username' } });
+
+    await findAllByText(/User 'existing_username' already exists/i);
+
+    fireEvent.change(await findByLabelText('Username'), {
+      target: { value: ' username_with_leading_space' },
+    });
+
+    await findAllByText(/Username must not contain leading or trailing spaces/i);
+
+    fireEvent.change(await findByLabelText('Username'), {
+      target: { value: '€' },
+    });
+
+    await findAllByText(
+      /Username must contain only letters, numbers, spaces, punctuation, and symbols/i
+    );
+
+    fireEvent.change(await findByLabelText('Password'), { target: { value: '111' } });
+
+    await findAllByText(/Password must be at least 6 characters/i);
+
+    fireEvent.change(await findByLabelText('Password'), { target: { value: '123456' } });
+    fireEvent.change(await findByLabelText('Confirm password'), { target: { value: '111' } });
+
+    await findAllByText(/Passwords do not match/i);
+  });
+});
diff --git a/x-pack/plugins/security/public/management/users/edit_user/create_user_page.tsx b/x-pack/plugins/security/public/management/users/edit_user/create_user_page.tsx
new file mode 100644
index 0000000000000..6842ddb774bda
--- /dev/null
+++ b/x-pack/plugins/security/public/management/users/edit_user/create_user_page.tsx
@@ -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;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FunctionComponent } from 'react';
+import {
+  EuiHorizontalRule,
+  EuiPageContent,
+  EuiPageContentBody,
+  EuiPageContentHeader,
+  EuiPageContentHeaderSection,
+  EuiTitle,
+} from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { useHistory } from 'react-router-dom';
+import { UserForm } from './user_form';
+
+export const CreateUserPage: FunctionComponent = () => {
+  const history = useHistory();
+  const backToUsers = () => history.push('/');
+
+  return (
+    <EuiPageContent>
+      <EuiPageContentHeader>
+        <EuiPageContentHeaderSection>
+          <EuiTitle>
+            <h1>
+              <FormattedMessage
+                id="xpack.security.management.users.createUserPage.title"
+                defaultMessage="Create user"
+              />
+            </h1>
+          </EuiTitle>
+        </EuiPageContentHeaderSection>
+      </EuiPageContentHeader>
+      <EuiPageContentBody>
+        <EuiHorizontalRule />
+        <UserForm isNewUser onCancel={backToUsers} onSuccess={backToUsers} />
+      </EuiPageContentBody>
+    </EuiPageContent>
+  );
+};
diff --git a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.scss b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.scss
deleted file mode 100644
index 727fac4782752..0000000000000
--- a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-.secUsersEditPage__content {
-  max-width: 460px;
-  margin-left: auto;
-  margin-right: auto;
-  flex-grow: 0;
-}
diff --git a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx
index 5e8c9f2d14a4c..f065c45d7080c 100644
--- a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx
+++ b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx
@@ -4,228 +4,440 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { act } from '@testing-library/react';
-import { mountWithIntl, nextTick } from '@kbn/test/jest';
-import { EditUserPage } from './edit_user_page';
 import React from 'react';
-import { User, Role } from '../../../../common/model';
-import { ReactWrapper } from 'enzyme';
-import { coreMock, scopedHistoryMock } from '../../../../../../../src/core/public/mocks';
+import {
+  fireEvent,
+  render,
+  waitFor,
+  waitForElementToBeRemoved,
+  within,
+} from '@testing-library/react';
+import { createMemoryHistory } from 'history';
+import { coreMock } from '../../../../../../../src/core/public/mocks';
 import { mockAuthenticatedUser } from '../../../../common/model/authenticated_user.mock';
 import { securityMock } from '../../../mocks';
-import { rolesAPIClientMock } from '../../roles/index.mock';
-import { userAPIClientMock } from '../index.mock';
-import { findTestSubject } from '@kbn/test/jest';
-
-const createUser = (username: string, roles = ['idk', 'something']) => {
-  const user: User = {
-    username,
-    full_name: 'my full name',
-    email: 'foo@bar.com',
-    roles,
-    enabled: true,
-  };
-
-  if (username === 'reserved_user') {
-    user.metadata = {
-      _reserved: true,
-    };
-  }
-
-  if (username === 'deprecated_user') {
-    user.metadata = {
-      _reserved: true,
-      _deprecated: true,
-      _deprecated_reason: 'beacuse I said so.',
-    };
-  }
-
-  return user;
+import { Providers } from '../users_management_app';
+import { EditUserPage } from './edit_user_page';
+
+jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({
+  htmlIdGenerator: () => () => `id-${Math.random()}`,
+}));
+
+const userMock = {
+  username: 'jdoe',
+  full_name: '',
+  email: '',
+  enabled: true,
+  roles: ['superuser'],
 };
 
-const buildClients = (user: User) => {
-  const apiClient = userAPIClientMock.create();
-  apiClient.getUser.mockResolvedValue(user);
+describe('EditUserPage', () => {
+  it('warns when viewing deactivated user', async () => {
+    const coreStart = coreMock.createStart();
+    const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] });
+    const authc = securityMock.createSetup().authc;
+
+    coreStart.http.get.mockResolvedValueOnce({
+      ...userMock,
+      enabled: false,
+    });
+    coreStart.http.get.mockResolvedValueOnce([]);
+
+    const { findByText } = render(
+      <Providers services={coreStart} authc={authc} history={history}>
+        <EditUserPage username={userMock.username} />
+      </Providers>
+    );
 
-  const rolesAPIClient = rolesAPIClientMock.create();
-  rolesAPIClient.getRoles.mockImplementation(() => {
-    return Promise.resolve([
-      {
-        name: 'role 1',
-        elasticsearch: {
-          cluster: ['all'],
-          indices: [],
-          run_as: [],
-        },
-        kibana: [],
-      },
-      {
-        name: 'role 2',
-        elasticsearch: {
-          cluster: [],
-          indices: [],
-          run_as: ['bar'],
-        },
-        kibana: [],
+    await findByText(/User has been deactivated/i);
+  });
+
+  it('warns when viewing deprecated user', async () => {
+    const coreStart = coreMock.createStart();
+    const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] });
+    const authc = securityMock.createSetup().authc;
+
+    coreStart.http.get.mockResolvedValueOnce({
+      ...userMock,
+      metadata: {
+        _reserved: true,
+        _deprecated: true,
+        _deprecated_reason: 'Use [new_user] instead.',
       },
+    });
+    coreStart.http.get.mockResolvedValueOnce([]);
+
+    const { findByRole, findByText } = render(
+      <Providers services={coreStart} authc={authc} history={history}>
+        <EditUserPage username={userMock.username} />
+      </Providers>
+    );
+
+    await findByText(/User is deprecated/i);
+    await findByText(/Use .new_user. instead/i);
+
+    fireEvent.click(await findByRole('button', { name: 'Back to users' }));
+
+    await waitFor(() => expect(history.location.pathname).toBe('/'));
+  });
+
+  it('warns when viewing built-in user', async () => {
+    const coreStart = coreMock.createStart();
+    const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] });
+    const authc = securityMock.createSetup().authc;
+
+    coreStart.http.get.mockResolvedValueOnce({
+      ...userMock,
+      metadata: { _reserved: true, _deprecated: false },
+    });
+    coreStart.http.get.mockResolvedValueOnce([]);
+
+    const { findByRole, findByText } = render(
+      <Providers services={coreStart} authc={authc} history={history}>
+        <EditUserPage username={userMock.username} />
+      </Providers>
+    );
+
+    await findByText(/User is built in/i);
+
+    fireEvent.click(await findByRole('button', { name: 'Back to users' }));
+
+    await waitFor(() => expect(history.location.pathname).toBe('/'));
+  });
+
+  it('warns when selecting deprecated role', async () => {
+    const coreStart = coreMock.createStart();
+    const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] });
+    const authc = securityMock.createSetup().authc;
+
+    coreStart.http.get.mockResolvedValueOnce({
+      ...userMock,
+      enabled: false,
+      roles: ['deprecated_role'],
+    });
+    coreStart.http.get.mockResolvedValueOnce([
       {
-        name: 'deprecated-role',
-        elasticsearch: {
-          cluster: [],
-          indices: [],
-          run_as: ['bar'],
-        },
-        kibana: [],
+        name: 'deprecated_role',
         metadata: {
+          _reserved: true,
           _deprecated: true,
+          _deprecated_reason: 'Use [new_role] instead.',
         },
       },
-    ] as Role[]);
+    ]);
+
+    const { findByText } = render(
+      <Providers services={coreStart} authc={authc} history={history}>
+        <EditUserPage username={userMock.username} />
+      </Providers>
+    );
+
+    await findByText(/Role .deprecated_role. is deprecated. Use .new_role. instead/i);
   });
 
-  return { apiClient, rolesAPIClient };
-};
+  it('updates user when submitting form and redirects back', async () => {
+    const coreStart = coreMock.createStart();
+    const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] });
+    const authc = securityMock.createSetup().authc;
 
-function buildSecuritySetup() {
-  const securitySetupMock = securityMock.createSetup();
-  securitySetupMock.authc.getCurrentUser.mockResolvedValue(
-    mockAuthenticatedUser(createUser('current_user'))
-  );
-  return securitySetupMock;
-}
+    coreStart.http.get.mockResolvedValueOnce(userMock);
+    coreStart.http.get.mockResolvedValueOnce([]);
+    coreStart.http.post.mockResolvedValueOnce({});
 
-function expectSaveButton(wrapper: ReactWrapper<any, any>) {
-  expect(wrapper.find('EuiButton[data-test-subj="userFormSaveButton"]')).toHaveLength(1);
-}
+    const { findByRole, findByLabelText } = render(
+      <Providers services={coreStart} authc={authc} history={history}>
+        <EditUserPage username={userMock.username} />
+      </Providers>
+    );
 
-function expectMissingSaveButton(wrapper: ReactWrapper<any, any>) {
-  expect(wrapper.find('EuiButton[data-test-subj="userFormSaveButton"]')).toHaveLength(0);
-}
+    fireEvent.change(await findByLabelText('Full name'), { target: { value: 'John Doe' } });
+    fireEvent.change(await findByLabelText('Email address'), {
+      target: { value: 'jdoe@elastic.co' },
+    });
+    fireEvent.click(await findByRole('button', { name: 'Update user' }));
+
+    await waitFor(() => {
+      expect(coreStart.http.get).toHaveBeenCalledWith('/internal/security/users/jdoe');
+      expect(coreStart.http.get).toHaveBeenCalledWith('/api/security/role');
+      expect(coreStart.http.post).toHaveBeenLastCalledWith('/internal/security/users/jdoe', {
+        body: JSON.stringify({
+          ...userMock,
+          full_name: 'John Doe',
+          email: 'jdoe@elastic.co',
+        }),
+      });
+      expect(history.location.pathname).toBe('/');
+    });
+  });
 
-describe('EditUserPage', () => {
-  const history = scopedHistoryMock.create();
-
-  it('allows reserved users to be viewed', async () => {
-    const user = createUser('reserved_user');
-    const { apiClient, rolesAPIClient } = buildClients(user);
-    const securitySetup = buildSecuritySetup();
-    const wrapper = mountWithIntl(
-      <EditUserPage
-        username={user.username}
-        userAPIClient={apiClient}
-        rolesAPIClient={rolesAPIClient}
-        authc={securitySetup.authc}
-        notifications={coreMock.createStart().notifications}
-        history={history}
-      />
+  it('warns when user form submission fails', async () => {
+    const coreStart = coreMock.createStart();
+    const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] });
+    const authc = securityMock.createSetup().authc;
+
+    coreStart.http.get.mockResolvedValueOnce(userMock);
+    coreStart.http.get.mockResolvedValueOnce([]);
+    coreStart.http.post.mockRejectedValueOnce(new Error('Error message'));
+
+    const { findByRole, findByLabelText } = render(
+      <Providers services={coreStart} authc={authc} history={history}>
+        <EditUserPage username={userMock.username} />
+      </Providers>
     );
 
-    await waitForRender(wrapper);
+    fireEvent.change(await findByLabelText('Full name'), { target: { value: 'John Doe' } });
+    fireEvent.change(await findByLabelText('Email address'), {
+      target: { value: 'jdoe@elastic.co' },
+    });
+    fireEvent.click(await findByRole('button', { name: 'Update user' }));
+
+    await waitFor(() => {
+      expect(coreStart.http.get).toHaveBeenCalledWith('/internal/security/users/jdoe');
+      expect(coreStart.http.get).toHaveBeenCalledWith('/api/security/role');
+      expect(coreStart.http.post).toHaveBeenLastCalledWith('/internal/security/users/jdoe', {
+        body: JSON.stringify({
+          ...userMock,
+          full_name: 'John Doe',
+          email: 'jdoe@elastic.co',
+        }),
+      });
+      expect(coreStart.notifications.toasts.addDanger).toHaveBeenCalledWith({
+        text: 'Error message',
+        title: "Could not update user 'jdoe'",
+      });
+      expect(history.location.pathname).toBe('/edit/jdoe');
+    });
+  });
 
-    expect(apiClient.getUser).toBeCalledTimes(1);
-    expect(securitySetup.authc.getCurrentUser).toBeCalledTimes(1);
+  it('changes password of other user when submitting form and closes dialog', async () => {
+    const coreStart = coreMock.createStart();
+    const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] });
+    const authc = securityMock.createSetup().authc;
+
+    coreStart.http.get.mockResolvedValueOnce(userMock);
+    coreStart.http.get.mockResolvedValueOnce([]);
+    authc.getCurrentUser.mockResolvedValueOnce(
+      mockAuthenticatedUser({ ...userMock, username: 'elastic' })
+    );
+    coreStart.http.post.mockResolvedValueOnce({});
 
-    expectMissingSaveButton(wrapper);
+    const { getByRole, findByRole } = render(
+      <Providers services={coreStart} authc={authc} history={history}>
+        <EditUserPage username={userMock.username} />
+      </Providers>
+    );
+
+    fireEvent.click(await findByRole('button', { name: 'Change password' }));
+
+    const dialog = getByRole('dialog');
+    fireEvent.change(await within(dialog).findByLabelText('New password'), {
+      target: { value: 'changeme' },
+    });
+    fireEvent.change(within(dialog).getByLabelText('Confirm password'), {
+      target: { value: 'changeme' },
+    });
+    fireEvent.click(within(dialog).getByRole('button', { name: 'Change password' }));
+
+    await waitForElementToBeRemoved(() => getByRole('dialog'));
+    expect(coreStart.http.post).toHaveBeenLastCalledWith('/internal/security/users/jdoe/password', {
+      body: JSON.stringify({
+        newPassword: 'changeme',
+      }),
+    });
   });
 
-  it('allows new users to be created', async () => {
-    const user = createUser('');
-    const { apiClient, rolesAPIClient } = buildClients(user);
-    const securitySetup = buildSecuritySetup();
-    const wrapper = mountWithIntl(
-      <EditUserPage
-        username={user.username}
-        userAPIClient={apiClient}
-        rolesAPIClient={rolesAPIClient}
-        authc={securitySetup.authc}
-        notifications={coreMock.createStart().notifications}
-        history={history}
-      />
+  it('changes password of current user when submitting form and closes dialog', async () => {
+    const coreStart = coreMock.createStart();
+    const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] });
+    const authc = securityMock.createSetup().authc;
+
+    coreStart.http.get.mockResolvedValueOnce(userMock);
+    coreStart.http.get.mockResolvedValueOnce([]);
+    authc.getCurrentUser.mockResolvedValueOnce(mockAuthenticatedUser(userMock));
+    coreStart.http.post.mockResolvedValueOnce({});
+
+    const { getByRole, findByRole } = render(
+      <Providers services={coreStart} authc={authc} history={history}>
+        <EditUserPage username={userMock.username} />
+      </Providers>
     );
 
-    await waitForRender(wrapper);
+    fireEvent.click(await findByRole('button', { name: 'Change password' }));
+
+    const dialog = await findByRole('dialog');
+    fireEvent.change(await within(dialog).findByLabelText('Current password'), {
+      target: { value: '123456' },
+    });
+    fireEvent.change(await within(dialog).findByLabelText('New password'), {
+      target: { value: 'changeme' },
+    });
+    fireEvent.change(await within(dialog).findByLabelText('Confirm password'), {
+      target: { value: 'changeme' },
+    });
+    fireEvent.click(await within(dialog).findByRole('button', { name: 'Change password' }));
+
+    await waitForElementToBeRemoved(() => getByRole('dialog'));
+    expect(coreStart.http.post).toHaveBeenLastCalledWith('/internal/security/users/jdoe/password', {
+      body: JSON.stringify({
+        newPassword: 'changeme',
+        password: '123456',
+      }),
+    });
+  });
+
+  it('warns when change password form submission fails', async () => {
+    const coreStart = coreMock.createStart();
+    const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] });
+    const authc = securityMock.createSetup().authc;
+
+    coreStart.http.get.mockResolvedValueOnce(userMock);
+    coreStart.http.get.mockResolvedValueOnce([]);
+    authc.getCurrentUser.mockResolvedValueOnce(
+      mockAuthenticatedUser({ ...userMock, username: 'elastic' })
+    );
+    coreStart.http.post.mockRejectedValueOnce(new Error('Error message'));
 
-    expect(apiClient.getUser).toBeCalledTimes(0);
-    expect(securitySetup.authc.getCurrentUser).toBeCalledTimes(0);
+    const { findByRole } = render(
+      <Providers services={coreStart} authc={authc} history={history}>
+        <EditUserPage username={userMock.username} />
+      </Providers>
+    );
 
-    expectSaveButton(wrapper);
+    fireEvent.click(await findByRole('button', { name: 'Change password' }));
+
+    const dialog = await findByRole('dialog');
+    fireEvent.change(await within(dialog).findByLabelText('New password'), {
+      target: { value: 'changeme' },
+    });
+    fireEvent.change(await within(dialog).findByLabelText('Confirm password'), {
+      target: { value: 'changeme' },
+    });
+    fireEvent.click(await within(dialog).findByRole('button', { name: 'Change password' }));
+
+    await waitFor(() => {
+      expect(coreStart.notifications.toasts.addDanger).toHaveBeenCalledWith({
+        text: 'Error message',
+        title: 'Could not change password',
+      });
+    });
   });
 
-  it('allows existing users to be edited', async () => {
-    const user = createUser('existing_user');
-    const { apiClient, rolesAPIClient } = buildClients(user);
-    const securitySetup = buildSecuritySetup();
-    const wrapper = mountWithIntl(
-      <EditUserPage
-        username={user.username}
-        userAPIClient={apiClient}
-        rolesAPIClient={rolesAPIClient}
-        authc={securitySetup.authc}
-        notifications={coreMock.createStart().notifications}
-        history={history}
-      />
+  it('validates change password form', async () => {
+    const coreStart = coreMock.createStart();
+    const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] });
+    const authc = securityMock.createSetup().authc;
+
+    coreStart.http.get.mockResolvedValueOnce(userMock);
+    coreStart.http.get.mockResolvedValueOnce([]);
+    authc.getCurrentUser.mockResolvedValueOnce(mockAuthenticatedUser(userMock));
+    coreStart.http.post.mockResolvedValueOnce({});
+
+    const { findByRole } = render(
+      <Providers services={coreStart} authc={authc} history={history}>
+        <EditUserPage username={userMock.username} />
+      </Providers>
     );
 
-    await waitForRender(wrapper);
+    fireEvent.click(await findByRole('button', { name: 'Change password' }));
+
+    const dialog = await findByRole('dialog');
+    fireEvent.click(await within(dialog).findByRole('button', { name: 'Change password' }));
 
-    expect(apiClient.getUser).toBeCalledTimes(1);
-    expect(securitySetup.authc.getCurrentUser).toBeCalledTimes(1);
+    await within(dialog).findByText(/Enter your current password/i);
+    await within(dialog).findByText(/Enter a new password/i);
 
-    expect(findTestSubject(wrapper, 'hasDeprecatedRolesAssignedHelpText')).toHaveLength(0);
-    expectSaveButton(wrapper);
+    fireEvent.change(await within(dialog).findByLabelText('Current password'), {
+      target: { value: 'changeme' },
+    });
+
+    fireEvent.change(await within(dialog).findByLabelText('New password'), {
+      target: { value: '111' },
+    });
+
+    await within(dialog).findAllByText(/Password must be at least 6 characters/i);
+
+    fireEvent.change(await within(dialog).findByLabelText('New password'), {
+      target: { value: '123456' },
+    });
+    fireEvent.change(await within(dialog).findByLabelText('Confirm password'), {
+      target: { value: '111' },
+    });
+
+    await within(dialog).findAllByText(/Passwords do not match/i);
   });
 
-  it('warns when viewing a depreciated user', async () => {
-    const user = createUser('deprecated_user');
-    const { apiClient, rolesAPIClient } = buildClients(user);
-    const securitySetup = buildSecuritySetup();
-
-    const wrapper = mountWithIntl(
-      <EditUserPage
-        username={user.username}
-        userAPIClient={apiClient}
-        rolesAPIClient={rolesAPIClient}
-        authc={securitySetup.authc}
-        notifications={coreMock.createStart().notifications}
-        history={history}
-      />
+  it('deactivates user when confirming and closes dialog', async () => {
+    const coreStart = coreMock.createStart();
+    const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] });
+    const authc = securityMock.createSetup().authc;
+
+    coreStart.http.get.mockResolvedValueOnce(userMock);
+    coreStart.http.get.mockResolvedValueOnce([]);
+    coreStart.http.post.mockResolvedValueOnce({});
+
+    const { getByRole, findByRole } = render(
+      <Providers services={coreStart} authc={authc} history={history}>
+        <EditUserPage username={userMock.username} />
+      </Providers>
     );
 
-    await waitForRender(wrapper);
-    expect(apiClient.getUser).toBeCalledTimes(1);
-    expect(securitySetup.authc.getCurrentUser).toBeCalledTimes(1);
+    fireEvent.click(await findByRole('button', { name: 'Deactivate user' }));
+
+    const dialog = getByRole('dialog');
+    fireEvent.click(within(dialog).getByRole('button', { name: 'Deactivate user' }));
 
-    expect(findTestSubject(wrapper, 'deprecatedUserWarning')).toHaveLength(1);
+    await waitForElementToBeRemoved(() => getByRole('dialog'));
+    expect(coreStart.http.post).toHaveBeenLastCalledWith('/internal/security/users/jdoe/_disable');
   });
 
-  it('warns when user is assigned a deprecated role', async () => {
-    const user = createUser('existing_user', ['deprecated-role']);
-    const { apiClient, rolesAPIClient } = buildClients(user);
-    const securitySetup = buildSecuritySetup();
-
-    const wrapper = mountWithIntl(
-      <EditUserPage
-        username={user.username}
-        userAPIClient={apiClient}
-        rolesAPIClient={rolesAPIClient}
-        authc={securitySetup.authc}
-        notifications={coreMock.createStart().notifications}
-        history={history}
-      />
+  it('activates user when confirming and closes dialog', async () => {
+    const coreStart = coreMock.createStart();
+    const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] });
+    const authc = securityMock.createSetup().authc;
+
+    coreStart.http.get.mockResolvedValueOnce({ ...userMock, enabled: false });
+    coreStart.http.get.mockResolvedValueOnce([]);
+    coreStart.http.post.mockResolvedValueOnce({});
+
+    const { getByRole, findAllByRole } = render(
+      <Providers services={coreStart} authc={authc} history={history}>
+        <EditUserPage username={userMock.username} />
+      </Providers>
     );
 
-    await waitForRender(wrapper);
+    const [enableButton] = await findAllByRole('button', { name: 'Activate user' });
+    fireEvent.click(enableButton);
 
-    expect(apiClient.getUser).toBeCalledTimes(1);
-    expect(securitySetup.authc.getCurrentUser).toBeCalledTimes(1);
+    const dialog = getByRole('dialog');
+    fireEvent.click(within(dialog).getByRole('button', { name: 'Activate user' }));
 
-    expect(findTestSubject(wrapper, 'hasDeprecatedRolesAssignedHelpText')).toHaveLength(1);
+    await waitForElementToBeRemoved(() => getByRole('dialog'));
+    expect(coreStart.http.post).toHaveBeenLastCalledWith('/internal/security/users/jdoe/_enable');
   });
-});
 
-async function waitForRender(wrapper: ReactWrapper<any, any>) {
-  await act(async () => {
-    await nextTick();
-    wrapper.update();
+  it('deletes user when confirming and redirects back', async () => {
+    const coreStart = coreMock.createStart();
+    const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] });
+    const authc = securityMock.createSetup().authc;
+
+    coreStart.http.get.mockResolvedValueOnce(userMock);
+    coreStart.http.get.mockResolvedValueOnce([]);
+    coreStart.http.delete.mockResolvedValueOnce({});
+
+    const { getByRole, findByRole } = render(
+      <Providers services={coreStart} authc={authc} history={history}>
+        <EditUserPage username={userMock.username} />
+      </Providers>
+    );
+
+    fireEvent.click(await findByRole('button', { name: 'Delete user' }));
+
+    const dialog = getByRole('dialog');
+    fireEvent.click(within(dialog).getByRole('button', { name: 'Delete user' }));
+
+    expect(coreStart.http.delete).toHaveBeenLastCalledWith('/internal/security/users/jdoe');
+    await waitFor(() => {
+      expect(history.location.pathname).toBe('/');
+    });
   });
-}
+});
diff --git a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx
index dc0c3336cb85f..68c01bf509b0d 100644
--- a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx
+++ b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx
@@ -4,615 +4,315 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import './edit_user_page.scss';
-
-import { get } from 'lodash';
-import React, { Component, Fragment, ChangeEvent } from 'react';
+import React, { FunctionComponent, useState, useEffect } from 'react';
 import {
+  EuiAvatar,
   EuiButton,
   EuiCallOut,
-  EuiButtonEmpty,
   EuiFlexGroup,
   EuiFlexItem,
-  EuiLink,
-  EuiTitle,
-  EuiForm,
-  EuiFormRow,
-  EuiIcon,
-  EuiText,
-  EuiFieldText,
+  EuiHorizontalRule,
   EuiPageContent,
+  EuiPageContentBody,
   EuiPageContentHeader,
   EuiPageContentHeaderSection,
-  EuiPageContentBody,
-  EuiHorizontalRule,
+  EuiPanel,
   EuiSpacer,
+  EuiText,
+  EuiTitle,
+  EuiDescriptionList,
+  EuiDescriptionListTitle,
+  EuiDescriptionListDescription,
 } from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
-import type { PublicMethodsOf } from '@kbn/utility-types';
-import { NotificationsStart, ScopedHistory } from 'src/core/public';
-import { User, EditUser, Role, isRoleDeprecated } from '../../../../common/model';
-import { AuthenticationServiceSetup } from '../../../authentication';
-import { RolesAPIClient } from '../../roles';
-import { ConfirmDeleteUsers, ChangePasswordForm } from '../components';
-import { UserValidator, UserValidationResult } from './validate_user';
-import { RoleComboBox } from '../../role_combo_box';
-import { isUserDeprecated, getExtendedUserDeprecationNotice, isUserReserved } from '../user_utils';
+import { useHistory } from 'react-router-dom';
+import useAsyncFn from 'react-use/lib/useAsyncFn';
+import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
+import { getUserDisplayName } from '../../../../common/model';
+import { isUserDeprecated, isUserReserved } from '../user_utils';
+import { UserForm } from './user_form';
+import { ChangePasswordFlyout } from './change_password_flyout';
+import { ConfirmDisableUsers } from './confirm_disable_users';
+import { ConfirmEnableUsers } from './confirm_enable_users';
+import { ConfirmDeleteUsers } from './confirm_delete_users';
 import { UserAPIClient } from '..';
 
-interface Props {
-  username?: string;
-  userAPIClient: PublicMethodsOf<UserAPIClient>;
-  rolesAPIClient: PublicMethodsOf<RolesAPIClient>;
-  authc: AuthenticationServiceSetup;
-  notifications: NotificationsStart;
-  history: ScopedHistory;
-}
-
-interface State {
-  isLoaded: boolean;
-  isNewUser: boolean;
-  currentUser: User | null;
-  showChangePasswordForm: boolean;
-  showDeleteConfirmation: boolean;
-  user: EditUser;
-  roles: Role[];
-  selectedRoles: readonly string[];
-  formError: UserValidationResult | null;
+export interface EditUserPageProps {
+  username: string;
 }
 
-export class EditUserPage extends Component<Props, State> {
-  private validator: UserValidator;
-
-  constructor(props: Props) {
-    super(props);
-    this.validator = new UserValidator({ shouldValidate: false });
-    this.state = {
-      isLoaded: false,
-      isNewUser: true,
-      currentUser: null,
-      showChangePasswordForm: false,
-      showDeleteConfirmation: false,
-      user: {
-        email: '',
-        username: '',
-        full_name: '',
-        roles: [],
-        enabled: true,
-        password: '',
-        confirmPassword: '',
-      },
-      roles: [],
-      selectedRoles: [],
-      formError: null,
-    };
-  }
-
-  public async componentDidMount() {
-    await this.setCurrentUser();
-  }
-
-  public async componentDidUpdate(prevProps: Props) {
-    if (prevProps.username !== this.props.username) {
-      await this.setCurrentUser();
+export type EditUserPageAction =
+  | 'changePassword'
+  | 'disableUser'
+  | 'enableUser'
+  | 'deleteUser'
+  | 'none';
+
+export const EditUserPage: FunctionComponent<EditUserPageProps> = ({ username }) => {
+  const { services } = useKibana();
+  const history = useHistory();
+  const [{ value: user, error }, getUser] = useAsyncFn(
+    () => new UserAPIClient(services.http!).getUser(username),
+    [services.http]
+  );
+  const [action, setAction] = useState<EditUserPageAction>('none');
+
+  const backToUsers = () => history.push('/');
+
+  useEffect(() => {
+    getUser();
+  }, []); // eslint-disable-line react-hooks/exhaustive-deps
+
+  useEffect(() => {
+    if (error) {
+      backToUsers();
     }
-  }
+  }, [error]); // eslint-disable-line react-hooks/exhaustive-deps
 
-  private backToUserList() {
-    this.props.history.push('/');
+  if (!user) {
+    return null;
   }
 
-  private async setCurrentUser() {
-    const { username, userAPIClient, rolesAPIClient, notifications, authc } = this.props;
-    let { user, currentUser } = this.state;
-    if (username) {
-      try {
-        user = {
-          ...(await userAPIClient.getUser(username)),
-          password: '',
-          confirmPassword: '',
-        };
-        currentUser = await authc.getCurrentUser();
-      } catch (err) {
-        notifications.toasts.addDanger({
-          title: i18n.translate('xpack.security.management.users.editUser.errorLoadingUserTitle', {
-            defaultMessage: 'Error loading user',
-          }),
-          text: get(err, 'body.message') || err.message,
-        });
-        return this.backToUserList();
-      }
-    }
-
-    let roles: Role[] = [];
-    try {
-      roles = await rolesAPIClient.getRoles();
-    } catch (err) {
-      notifications.toasts.addDanger({
-        title: i18n.translate('xpack.security.management.users.editUser.errorLoadingRolesTitle', {
-          defaultMessage: 'Error loading roles',
-        }),
-        text: get(err, 'body.message') || err.message,
-      });
-    }
-
-    this.setState({
-      isLoaded: true,
-      isNewUser: !username,
-      currentUser,
-      user,
-      roles,
-      selectedRoles: user.roles || [],
-    });
-  }
-
-  private handleDelete = (usernames: string[], errors: string[]) => {
-    if (errors.length === 0) {
-      this.backToUserList();
-    }
-  };
-
-  private saveUser = async () => {
-    this.validator.enableValidation();
-
-    const result = this.validator.validateForSave(this.state.user, this.state.isNewUser);
-    if (result.isInvalid) {
-      this.setState({
-        formError: result,
-      });
-    } else {
-      this.setState({
-        formError: null,
-      });
-      const { userAPIClient } = this.props;
-      const { user, isNewUser, selectedRoles } = this.state;
-      const userToSave: EditUser = { ...user };
-      if (!isNewUser) {
-        delete userToSave.password;
-      }
-      delete userToSave.confirmPassword;
-      userToSave.roles = [...selectedRoles];
-      try {
-        await userAPIClient.saveUser(userToSave);
-        this.props.notifications.toasts.addSuccess(
-          i18n.translate(
-            'xpack.security.management.users.editUser.userSuccessfullySavedNotificationMessage',
-            {
-              defaultMessage: 'Saved user {message}',
-              values: { message: user.username },
-            }
-          )
-        );
-
-        this.backToUserList();
-      } catch (e) {
-        this.props.notifications.toasts.addDanger(
-          i18n.translate('xpack.security.management.users.editUser.savingUserErrorMessage', {
-            defaultMessage: 'Error saving user: {message}',
-            values: { message: get(e, 'body.message', 'Unknown error') },
-          })
-        );
-      }
-    }
-  };
-
-  private passwordFields = () => {
-    return (
-      <Fragment>
-        <EuiFormRow
-          label={i18n.translate('xpack.security.management.users.editUser.passwordFormRowLabel', {
-            defaultMessage: 'Password',
-          })}
-          {...this.validator.validatePassword(this.state.user)}
-        >
-          <EuiFieldText
-            autoComplete="new-password"
-            data-test-subj="passwordInput"
-            name="password"
-            type="password"
-            onChange={this.onPasswordChange}
-          />
-        </EuiFormRow>
-        <EuiFormRow
-          label={i18n.translate(
-            'xpack.security.management.users.editUser.confirmPasswordFormRowLabel',
-            { defaultMessage: 'Confirm password' }
-          )}
-          {...this.validator.validateConfirmPassword(this.state.user)}
-        >
-          <EuiFieldText
-            autoComplete="new-password"
-            data-test-subj="passwordConfirmationInput"
-            onChange={this.onConfirmPasswordChange}
-            name="confirm_password"
-            type="password"
-          />
-        </EuiFormRow>
-      </Fragment>
-    );
-  };
-
-  private changePasswordForm = () => {
-    const { showChangePasswordForm, user, currentUser } = this.state;
-
-    const userIsLoggedInUser = Boolean(
-      currentUser && user.username && user.username === currentUser.username
-    );
-
-    if (!showChangePasswordForm) {
-      return null;
-    }
-    return (
-      <Fragment>
+  const isReservedUser = isUserReserved(user);
+  const isDeprecatedUser = isUserDeprecated(user);
+  const displayName = getUserDisplayName(user);
+
+  return (
+    <EuiPageContent>
+      <EuiPageContentHeader>
+        <EuiPageContentHeaderSection>
+          <EuiFlexGroup alignItems="center" responsive={false}>
+            <EuiFlexItem grow={false}>
+              <EuiAvatar name={displayName!} size="xl" />
+            </EuiFlexItem>
+            <EuiFlexItem>
+              <EuiTitle>
+                <h1>{displayName}</h1>
+              </EuiTitle>
+              <EuiText>{user.email}</EuiText>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </EuiPageContentHeaderSection>
+      </EuiPageContentHeader>
+      <EuiPageContentBody>
         <EuiHorizontalRule />
-        {user.username === 'kibana' || user.username === 'kibana_system' ? (
-          <Fragment>
+        {isDeprecatedUser ? (
+          <>
             <EuiCallOut
-              title={i18n.translate(
-                'xpack.security.management.users.editUser.changePasswordExtraStepTitle',
-                { defaultMessage: 'Extra step needed' }
-              )}
+              title={
+                <FormattedMessage
+                  id="xpack.security.management.users.editUserPage.deprecatedUserWarning"
+                  defaultMessage="This user is deprecated."
+                />
+              }
+              iconType="alert"
               color="warning"
-              iconType="help"
             >
-              <p>
+              {user.metadata?._deprecated_reason?.replace(/\[(.+)\]/, "'$1'")}
+            </EuiCallOut>
+            <EuiSpacer />
+          </>
+        ) : isReservedUser ? (
+          <>
+            <EuiCallOut
+              title={
+                <FormattedMessage
+                  id="xpack.security.management.users.editUserPage.reservedUserWarning"
+                  defaultMessage="This user is built in and can't be updated or deleted."
+                />
+              }
+              iconType="lock"
+            />
+            <EuiSpacer />
+          </>
+        ) : user.enabled === false ? (
+          <>
+            <EuiCallOut
+              title={
                 <FormattedMessage
-                  id="xpack.security.management.users.editUser.changePasswordUpdateKibanaTitle"
-                  defaultMessage="After you change the password for the {username} user, you must update the {kibana}
-                  file and restart Kibana."
-                  values={{ kibana: 'kibana.yml', username: user.username }}
+                  id="xpack.security.management.users.editUserPage.disabledUserWarning"
+                  defaultMessage="This user has been deactivated and can't access Elastic."
                 />
-              </p>
+              }
+            >
+              <EuiButton onClick={() => setAction('enableUser')} size="s">
+                <FormattedMessage
+                  id="xpack.security.management.users.editUserPage.enableUserButton"
+                  defaultMessage="Activate user"
+                />
+              </EuiButton>
             </EuiCallOut>
             <EuiSpacer />
-          </Fragment>
-        ) : null}
-        <ChangePasswordForm
-          user={this.state.user}
-          isUserChangingOwnPassword={userIsLoggedInUser}
-          onChangePassword={this.toggleChangePasswordForm}
-          userAPIClient={this.props.userAPIClient}
-          notifications={this.props.notifications}
+          </>
+        ) : undefined}
+
+        <UserForm
+          isReservedUser={isReservedUser}
+          defaultValues={user}
+          onCancel={backToUsers}
+          onSuccess={backToUsers}
         />
-      </Fragment>
-    );
-  };
-
-  private toggleChangePasswordForm = () => {
-    const { showChangePasswordForm } = this.state;
-    this.setState({ showChangePasswordForm: !showChangePasswordForm });
-  };
-
-  private onUsernameChange = (e: ChangeEvent<HTMLInputElement>) => {
-    const user = {
-      ...this.state.user,
-      username: e.target.value || '',
-    };
-    const formError = this.validator.validateForSave(user, this.state.isNewUser);
-
-    this.setState({
-      user,
-      formError,
-    });
-  };
 
-  private onEmailChange = (e: ChangeEvent<HTMLInputElement>) => {
-    const user = {
-      ...this.state.user,
-      email: e.target.value || '',
-    };
-    const formError = this.validator.validateForSave(user, this.state.isNewUser);
-
-    this.setState({
-      user,
-      formError,
-    });
-  };
-
-  private onFullNameChange = (e: ChangeEvent<HTMLInputElement>) => {
-    const user = {
-      ...this.state.user,
-      full_name: e.target.value || '',
-    };
-    const formError = this.validator.validateForSave(user, this.state.isNewUser);
-
-    this.setState({
-      user,
-      formError,
-    });
-  };
-
-  private onPasswordChange = (e: ChangeEvent<HTMLInputElement>) => {
-    const user = {
-      ...this.state.user,
-      password: e.target.value || '',
-    };
-    const formError = this.validator.validateForSave(user, this.state.isNewUser);
-
-    this.setState({
-      user,
-      formError,
-    });
-  };
-
-  private onConfirmPasswordChange = (e: ChangeEvent<HTMLInputElement>) => {
-    const user = {
-      ...this.state.user,
-      confirmPassword: e.target.value || '',
-    };
-    const formError = this.validator.validateForSave(user, this.state.isNewUser);
-
-    this.setState({
-      user,
-      formError,
-    });
-  };
-
-  private onRolesChange = (selectedRoles: string[]) => {
-    this.setState({
-      selectedRoles,
-    });
-  };
-
-  private cannotSaveUser = () => {
-    const { user, isNewUser } = this.state;
-    const result = this.validator.validateForSave(user, isNewUser);
-    return result.isInvalid;
-  };
-
-  private onCancelDelete = () => {
-    this.setState({ showDeleteConfirmation: false });
-  };
-
-  public render() {
-    const {
-      user,
-      selectedRoles,
-      roles,
-      showChangePasswordForm,
-      isNewUser,
-      showDeleteConfirmation,
-    } = this.state;
-    const reserved = isUserReserved(user);
-    if (!user || !roles) {
-      return null;
-    }
-
-    if (!this.state.isLoaded) {
-      return null;
-    }
-
-    const hasAnyDeprecatedRolesAssigned = selectedRoles.some((selected) => {
-      const role = roles.find((r) => r.name === selected);
-      return role && isRoleDeprecated(role);
-    });
+        {action === 'changePassword' ? (
+          <ChangePasswordFlyout
+            username={username!}
+            onCancel={() => setAction('none')}
+            onSuccess={() => setAction('none')}
+          />
+        ) : action === 'disableUser' ? (
+          <ConfirmDisableUsers
+            usernames={[username!]}
+            onCancel={() => setAction('none')}
+            onSuccess={() => {
+              setAction('none');
+              getUser();
+            }}
+          />
+        ) : action === 'enableUser' ? (
+          <ConfirmEnableUsers
+            usernames={[username!]}
+            onCancel={() => setAction('none')}
+            onSuccess={() => {
+              setAction('none');
+              getUser();
+            }}
+          />
+        ) : action === 'deleteUser' ? (
+          <ConfirmDeleteUsers
+            usernames={[username!]}
+            onCancel={() => setAction('none')}
+            onSuccess={backToUsers}
+          />
+        ) : undefined}
 
-    const roleHelpText = hasAnyDeprecatedRolesAssigned ? (
-      <span data-test-subj="hasDeprecatedRolesAssignedHelpText">
-        <FormattedMessage
-          id="xpack.security.management.users.editUser.deprecatedRolesAssignedWarning"
-          defaultMessage="This user is assigned a deprecated role. Please migrate to a supported role."
-        />
-      </span>
-    ) : undefined;
+        <EuiSpacer />
+        <EuiHorizontalRule />
 
-    return (
-      <div className="secUsersEditPage">
-        <EuiPageContent className="secUsersEditPage__content">
-          <EuiPageContentHeader>
-            <EuiPageContentHeaderSection>
-              <EuiTitle>
-                <h1>
-                  {isNewUser ? (
+        <EuiPanel color="subdued" hasShadow={false}>
+          <EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
+            <EuiFlexItem>
+              <EuiDescriptionList>
+                <EuiDescriptionListTitle>
+                  <FormattedMessage
+                    id="xpack.security.management.users.editUserPage.changePasswordTitle"
+                    defaultMessage="Change password"
+                  />
+                </EuiDescriptionListTitle>
+                <EuiDescriptionListDescription>
+                  <FormattedMessage
+                    id="xpack.security.management.users.editUserPage.changePasswordDescription"
+                    defaultMessage="The user will not be able to log in using their previous
+                    password."
+                  />
+                </EuiDescriptionListDescription>
+              </EuiDescriptionList>
+            </EuiFlexItem>
+            <EuiFlexItem grow={false}>
+              <EuiButton onClick={() => setAction('changePassword')} size="s">
+                <FormattedMessage
+                  id="xpack.security.management.users.editUserPage.changePasswordButton"
+                  defaultMessage="Change password"
+                />
+              </EuiButton>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </EuiPanel>
+
+        <EuiSpacer />
+        {user.enabled === false ? (
+          <EuiPanel color="subdued" hasShadow={false}>
+            <EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
+              <EuiFlexItem>
+                <EuiDescriptionList>
+                  <EuiDescriptionListTitle>
                     <FormattedMessage
-                      id="xpack.security.management.users.editUser.newUserTitle"
-                      defaultMessage="New user"
+                      id="xpack.security.management.users.editUserPage.enableUserTitle"
+                      defaultMessage="Activate user"
                     />
-                  ) : (
+                  </EuiDescriptionListTitle>
+                  <EuiDescriptionListDescription>
                     <FormattedMessage
-                      id="xpack.security.management.users.editUser.editUserTitle"
-                      defaultMessage="Edit {userName} user"
-                      values={{ userName: user.username }}
+                      id="xpack.security.management.users.editUserPage.enableUserDescription"
+                      defaultMessage="Allow the user to access Elastic."
                     />
-                  )}
-                </h1>
-              </EuiTitle>
-            </EuiPageContentHeaderSection>
-            {reserved && (
-              <EuiPageContentHeaderSection>
-                <EuiIcon type="lock" size="l" color="subdued" />
-              </EuiPageContentHeaderSection>
-            )}
-          </EuiPageContentHeader>
-          <EuiPageContentBody>
-            {reserved && (
-              <Fragment>
-                <EuiText size="s" color="subdued">
-                  <p>
+                  </EuiDescriptionListDescription>
+                </EuiDescriptionList>
+              </EuiFlexItem>
+              <EuiFlexItem grow={false}>
+                <EuiButton onClick={() => setAction('enableUser')} size="s">
+                  <FormattedMessage
+                    id="xpack.security.management.users.editUserPage.enableUserButton"
+                    defaultMessage="Activate user"
+                  />
+                </EuiButton>
+              </EuiFlexItem>
+            </EuiFlexGroup>
+          </EuiPanel>
+        ) : (
+          <EuiPanel color="subdued" hasShadow={false}>
+            <EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
+              <EuiFlexItem>
+                <EuiDescriptionList>
+                  <EuiDescriptionListTitle>
                     <FormattedMessage
-                      id="xpack.security.management.users.editUser.modifyingReservedUsersDescription"
-                      defaultMessage="Reserved users are built-in and cannot be removed or modified. Only the password
-                    may be changed."
-                    />
-                  </p>
-                </EuiText>
-                <EuiSpacer size="s" />
-              </Fragment>
-            )}
-
-            {isUserDeprecated(user) && (
-              <Fragment>
-                <EuiCallOut
-                  data-test-subj="deprecatedUserWarning"
-                  title={getExtendedUserDeprecationNotice(user)}
-                  color="warning"
-                  iconType="alert"
-                  size="s"
-                />
-                <EuiSpacer size="s" />
-              </Fragment>
-            )}
-
-            {showDeleteConfirmation ? (
-              <ConfirmDeleteUsers
-                onCancel={this.onCancelDelete}
-                usersToDelete={[user.username]}
-                callback={this.handleDelete}
-                userAPIClient={this.props.userAPIClient}
-                notifications={this.props.notifications}
-              />
-            ) : null}
-
-            <EuiForm {...this.state.formError}>
-              <EuiFormRow
-                {...this.validator.validateUsername(this.state.user)}
-                helpText={
-                  !isNewUser && !reserved
-                    ? i18n.translate(
-                        'xpack.security.management.users.editUser.changingUserNameAfterCreationDescription',
-                        { defaultMessage: `Usernames can't be changed after creation.` }
-                      )
-                    : null
-                }
-                label={i18n.translate(
-                  'xpack.security.management.users.editUser.usernameFormRowLabel',
-                  { defaultMessage: 'Username' }
-                )}
-              >
-                <EuiFieldText
-                  value={user.username || ''}
-                  name="username"
-                  data-test-subj="userFormUserNameInput"
-                  disabled={!isNewUser}
-                  onChange={this.onUsernameChange}
-                />
-              </EuiFormRow>
-              {isNewUser ? this.passwordFields() : null}
-              {reserved ? null : (
-                <Fragment>
-                  <EuiFormRow
-                    label={i18n.translate(
-                      'xpack.security.management.users.editUser.fullNameFormRowLabel',
-                      { defaultMessage: 'Full name' }
-                    )}
-                  >
-                    <EuiFieldText
-                      data-test-subj="userFormFullNameInput"
-                      name="full_name"
-                      value={user.full_name || ''}
-                      onChange={this.onFullNameChange}
-                    />
-                  </EuiFormRow>
-                  <EuiFormRow
-                    {...this.validator.validateEmail(this.state.user)}
-                    label={i18n.translate(
-                      'xpack.security.management.users.editUser.emailAddressFormRowLabel',
-                      { defaultMessage: 'Email address' }
-                    )}
-                  >
-                    <EuiFieldText
-                      data-test-subj="userFormEmailInput"
-                      name="email"
-                      value={user.email || ''}
-                      onChange={this.onEmailChange}
+                      id="xpack.security.management.users.editUserPage.disableUserTitle"
+                      defaultMessage="Deactivate user"
                     />
-                  </EuiFormRow>
-                </Fragment>
-              )}
-              <EuiFormRow
-                label={i18n.translate(
-                  'xpack.security.management.users.editUser.rolesFormRowLabel',
-                  { defaultMessage: 'Roles' }
-                )}
-                helpText={roleHelpText}
-              >
-                <RoleComboBox
-                  availableRoles={roles}
-                  selectedRoleNames={selectedRoles}
-                  onChange={this.onRolesChange}
-                  isDisabled={reserved}
-                />
-              </EuiFormRow>
-
-              {isNewUser || showChangePasswordForm ? null : (
-                <EuiFormRow label="Password">
-                  <EuiLink data-test-subj="changePassword" onClick={this.toggleChangePasswordForm}>
+                  </EuiDescriptionListTitle>
+                  <EuiDescriptionListDescription>
                     <FormattedMessage
-                      id="xpack.security.management.users.editUser.changePasswordButtonLabel"
-                      defaultMessage="Change password"
+                      id="xpack.security.management.users.editUserPage.disableUserDescription"
+                      defaultMessage="Prevent the user from accessing Elastic."
                     />
-                  </EuiLink>
-                </EuiFormRow>
-              )}
-              {this.changePasswordForm()}
-
-              <EuiHorizontalRule />
-
-              {reserved && (
-                <EuiButton onClick={() => this.backToUserList()}>
+                  </EuiDescriptionListDescription>
+                </EuiDescriptionList>
+              </EuiFlexItem>
+              <EuiFlexItem grow={false}>
+                <EuiButton onClick={() => setAction('disableUser')} size="s">
                   <FormattedMessage
-                    id="xpack.security.management.users.editUser.returnToUserListButtonLabel"
-                    defaultMessage="Return to user list"
+                    id="xpack.security.management.users.editUserPage.disableUserButton"
+                    defaultMessage="Deactivate user"
                   />
                 </EuiButton>
-              )}
-              {reserved ? null : (
-                <EuiFlexGroup responsive={false}>
-                  <EuiFlexItem grow={false}>
-                    <EuiButton
-                      disabled={this.cannotSaveUser()}
-                      fill
-                      data-test-subj="userFormSaveButton"
-                      onClick={() => this.saveUser()}
-                    >
-                      {isNewUser ? (
-                        <FormattedMessage
-                          id="xpack.security.management.users.editUser.createUserButtonLabel"
-                          defaultMessage="Create user"
-                        />
-                      ) : (
-                        <FormattedMessage
-                          id="xpack.security.management.users.editUser.updateUserButtonLabel"
-                          defaultMessage="Update user"
-                        />
-                      )}
-                    </EuiButton>
-                  </EuiFlexItem>
-                  <EuiFlexItem grow={false}>
-                    <EuiButtonEmpty
-                      data-test-subj="userFormCancelButton"
-                      onClick={() => this.backToUserList()}
-                    >
+              </EuiFlexItem>
+            </EuiFlexGroup>
+          </EuiPanel>
+        )}
+
+        {!isReservedUser && (
+          <>
+            <EuiSpacer />
+            <EuiPanel color="subdued" hasShadow={false}>
+              <EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
+                <EuiFlexItem>
+                  <EuiDescriptionList>
+                    <EuiDescriptionListTitle>
                       <FormattedMessage
-                        id="xpack.security.management.users.editUser.cancelButtonLabel"
-                        defaultMessage="Cancel"
+                        id="xpack.security.management.users.editUserPage.deleteUserTitle"
+                        defaultMessage="Delete user"
                       />
-                    </EuiButtonEmpty>
-                  </EuiFlexItem>
-                  <EuiFlexItem grow={true} />
-                  {isNewUser || reserved ? null : (
-                    <EuiFlexItem grow={false}>
-                      <EuiButtonEmpty
-                        onClick={() => {
-                          this.setState({ showDeleteConfirmation: true });
-                        }}
-                        data-test-subj="userFormDeleteButton"
-                        color="danger"
-                      >
-                        <FormattedMessage
-                          id="xpack.security.management.users.editUser.deleteUserButtonLabel"
-                          defaultMessage="Delete user"
-                        />
-                      </EuiButtonEmpty>
-                    </EuiFlexItem>
-                  )}
-                </EuiFlexGroup>
-              )}
-            </EuiForm>
-          </EuiPageContentBody>
-        </EuiPageContent>
-      </div>
-    );
-  }
-}
+                    </EuiDescriptionListTitle>
+                    <EuiDescriptionListDescription>
+                      <FormattedMessage
+                        id="xpack.security.management.users.editUserPage.deleteUserDescription"
+                        defaultMessage="Permanently delete the user and remove access to Elastic."
+                      />
+                    </EuiDescriptionListDescription>
+                  </EuiDescriptionList>
+                </EuiFlexItem>
+                <EuiFlexItem grow={false}>
+                  <EuiButton onClick={() => setAction('deleteUser')} size="s" color="danger">
+                    <FormattedMessage
+                      id="xpack.security.management.users.editUserPage.deleteUserButton"
+                      defaultMessage="Delete user"
+                    />
+                  </EuiButton>
+                </EuiFlexItem>
+              </EuiFlexGroup>
+            </EuiPanel>
+          </>
+        )}
+      </EuiPageContentBody>
+    </EuiPageContent>
+  );
+};
diff --git a/x-pack/plugins/security/public/management/users/edit_user/index.ts b/x-pack/plugins/security/public/management/users/edit_user/index.ts
index 92eb17b9ebd36..30069d8e97c31 100644
--- a/x-pack/plugins/security/public/management/users/edit_user/index.ts
+++ b/x-pack/plugins/security/public/management/users/edit_user/index.ts
@@ -5,3 +5,4 @@
  */
 
 export { EditUserPage } from './edit_user_page';
+export { CreateUserPage } from './create_user_page';
diff --git a/x-pack/plugins/security/public/management/users/edit_user/user_form.tsx b/x-pack/plugins/security/public/management/users/edit_user/user_form.tsx
new file mode 100644
index 0000000000000..daa488d674fbb
--- /dev/null
+++ b/x-pack/plugins/security/public/management/users/edit_user/user_form.tsx
@@ -0,0 +1,466 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FunctionComponent, useEffect, useCallback } from 'react';
+import {
+  EuiButton,
+  EuiButtonEmpty,
+  EuiDescribedFormGroup,
+  EuiFieldPassword,
+  EuiFieldText,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiForm,
+  EuiFormRow,
+  EuiSpacer,
+  EuiTextColor,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import useAsyncFn from 'react-use/lib/useAsyncFn';
+import { throttle } from 'lodash';
+import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
+import { User, Role, isRoleDeprecated } from '../../../../common/model';
+import { NAME_REGEX, MAX_NAME_LENGTH } from '../../../../common/constants';
+import { useForm, ValidationErrors } from '../../../components/use_form';
+import { DocLink } from '../../../components/doc_link';
+import { RolesAPIClient } from '../../roles';
+import { RoleComboBox } from '../../role_combo_box';
+import { UserAPIClient } from '..';
+
+export const THROTTLE_USERS_WAIT = 10000;
+
+export interface UserFormValues {
+  username?: string;
+  full_name: string;
+  email: string;
+  password?: string;
+  confirm_password?: string;
+  roles: readonly string[];
+}
+
+export interface UserFormProps {
+  isNewUser?: boolean;
+  isReservedUser?: boolean;
+  isCurrentUser?: boolean;
+  defaultValues?: UserFormValues;
+  onCancel(): void;
+  onSuccess?(): void;
+}
+
+const defaultDefaultValues: UserFormValues = {
+  username: '',
+  password: '',
+  confirm_password: '',
+  full_name: '',
+  email: '',
+  roles: [],
+};
+
+export const UserForm: FunctionComponent<UserFormProps> = ({
+  isNewUser = false,
+  isReservedUser = false,
+  defaultValues = defaultDefaultValues,
+  onSuccess,
+  onCancel,
+}) => {
+  const { services } = useKibana();
+
+  const [rolesState, getRoles] = useAsyncFn(() => new RolesAPIClient(services.http!).getRoles(), [
+    services.http,
+  ]);
+
+  // eslint-disable-next-line react-hooks/exhaustive-deps
+  const getUsersThrottled = useCallback(
+    throttle(() => new UserAPIClient(services.http!).getUsers(), THROTTLE_USERS_WAIT),
+    [services.http]
+  );
+
+  const [form, eventHandlers] = useForm({
+    onSubmit: async (values) => {
+      // eslint-disable-next-line @typescript-eslint/naming-convention
+      const { password, confirm_password, ...rest } = values;
+      const user = isNewUser ? { password, ...rest } : rest;
+      try {
+        await new UserAPIClient(services.http!).saveUser(user as User);
+        services.notifications!.toasts.addSuccess(
+          isNewUser
+            ? i18n.translate('xpack.security.management.users.userForm.createSuccessMessage', {
+                defaultMessage: "Created user '{username}'",
+                values: { username: user.username },
+              })
+            : i18n.translate('xpack.security.management.users.userForm.updateSuccessMessage', {
+                defaultMessage: "Updated user '{username}'",
+                values: { username: user.username },
+              })
+        );
+        onSuccess?.();
+      } catch (error) {
+        services.notifications!.toasts.addDanger({
+          title: isNewUser
+            ? i18n.translate('xpack.security.management.users.userForm.createErrorMessage', {
+                defaultMessage: "Could not create user '{username}'",
+                values: { username: user.username },
+              })
+            : i18n.translate('xpack.security.management.users.userForm.updateErrorMessage', {
+                defaultMessage: "Could not update user '{username}'",
+                values: { username: user.username },
+              }),
+          text: (error as any).body?.message || error.message,
+        });
+        throw error;
+      }
+    },
+    validate: async (values) => {
+      const errors: ValidationErrors<typeof values> = {};
+
+      if (isNewUser) {
+        if (!values.username) {
+          errors.username = i18n.translate(
+            'xpack.security.management.users.userForm.usernameRequiredError',
+            {
+              defaultMessage: 'Enter a username.',
+            }
+          );
+        } else if (values.username.length > MAX_NAME_LENGTH) {
+          errors.username = i18n.translate(
+            'xpack.security.management.users.userForm.usernameMaxLengthError',
+            {
+              defaultMessage: 'Username must not exceed {maxLength} characters.',
+              values: { maxLength: MAX_NAME_LENGTH },
+            }
+          );
+        } else if (values.username.trim() !== values.username) {
+          errors.username = i18n.translate(
+            'xpack.security.management.users.userForm.usernameWhitespaceError',
+            {
+              defaultMessage: `Username must not contain leading or trailing spaces.`,
+            }
+          );
+        } else if (!values.username.match(NAME_REGEX)) {
+          errors.username = i18n.translate(
+            'xpack.security.management.users.userForm.usernameInvalidError',
+            {
+              defaultMessage:
+                'Username must contain only letters, numbers, spaces, punctuation, and symbols.',
+            }
+          );
+        } else {
+          try {
+            const users = await getUsersThrottled();
+            if (users.some((user) => user.username === values.username)) {
+              errors.username = i18n.translate(
+                'xpack.security.management.users.userForm.usernameTakenError',
+                {
+                  defaultMessage: "User '{username}' already exists.",
+                  values: { username: values.username },
+                }
+              );
+            }
+          } catch (error) {} // eslint-disable-line no-empty
+        }
+
+        if (!values.password) {
+          errors.password = i18n.translate(
+            'xpack.security.management.users.userForm.passwordRequiredError',
+            {
+              defaultMessage: 'Enter a password.',
+            }
+          );
+        } else if (values.password.length < 6) {
+          errors.password = i18n.translate(
+            'xpack.security.management.users.userForm.passwordInvalidError',
+            {
+              defaultMessage: 'Password must be at least 6 characters.',
+            }
+          );
+        } else if (!values.confirm_password) {
+          errors.confirm_password = i18n.translate(
+            'xpack.security.management.users.userForm.confirmPasswordRequiredError',
+            {
+              defaultMessage: 'Passwords do not match.',
+            }
+          );
+        } else if (values.password !== values.confirm_password) {
+          errors.confirm_password = i18n.translate(
+            'xpack.security.management.users.userForm.confirmPasswordInvalidError',
+            {
+              defaultMessage: 'Passwords do not match.',
+            }
+          );
+        }
+      }
+
+      return errors;
+    },
+    defaultValues,
+  });
+
+  useEffect(() => {
+    form.reset(defaultValues);
+  }, [defaultValues]); // eslint-disable-line react-hooks/exhaustive-deps
+
+  useEffect(() => {
+    getRoles();
+  }, []); // eslint-disable-line react-hooks/exhaustive-deps
+
+  const availableRoles = rolesState.value ?? [];
+  const selectedRoleNames = form.values.roles ?? [];
+  const deprecatedRoles = selectedRoleNames.reduce<Role[]>((roles, name) => {
+    const role = availableRoles.find((r) => r.name === name);
+    if (role && isRoleDeprecated(role)) {
+      roles.push(role);
+    }
+    return roles;
+  }, []);
+
+  return (
+    <EuiForm
+      component="form"
+      error={Object.values(form.errors)}
+      isInvalid={form.isInvalid}
+      invalidCallout={form.isSubmitted ? 'above' : 'none'}
+      onSubmit={eventHandlers.onSubmit}
+      noValidate
+    >
+      <EuiDescribedFormGroup
+        title={
+          <h2>
+            <FormattedMessage
+              id="xpack.security.management.users.userForm.profileTitle"
+              defaultMessage="Profile"
+            />
+          </h2>
+        }
+        description={i18n.translate('xpack.security.management.users.userForm.profileDescription', {
+          defaultMessage: 'Provide personal details.',
+        })}
+      >
+        <EuiFormRow
+          label={i18n.translate('xpack.security.management.users.userForm.usernameLabel', {
+            defaultMessage: 'Username',
+          })}
+          helpText={
+            !isNewUser && !isReservedUser
+              ? i18n.translate(
+                  'xpack.security.management.users.userForm.changingUserNameAfterCreationDescription',
+                  { defaultMessage: `Username can't be changed once created.` }
+                )
+              : undefined
+          }
+          error={form.errors.username}
+          isInvalid={form.touched.username && !!form.errors.username}
+        >
+          <EuiFieldText
+            name="username"
+            icon="user"
+            value={form.values.username}
+            isLoading={form.isValidating}
+            isInvalid={form.touched.username && !!form.errors.username}
+            disabled={!isNewUser}
+            onChange={eventHandlers.onChange}
+            onBlur={eventHandlers.onBlur}
+          />
+        </EuiFormRow>
+
+        {!isReservedUser ? (
+          <>
+            <EuiFormRow
+              label={i18n.translate('xpack.security.management.users.userForm.fullNameLabel', {
+                defaultMessage: 'Full name',
+              })}
+              error={form.errors.full_name}
+              isInvalid={form.touched.full_name && !!form.errors.full_name}
+            >
+              <EuiFieldText
+                name="full_name"
+                value={form.values.full_name}
+                isInvalid={form.touched.full_name && !!form.errors.full_name}
+                onChange={eventHandlers.onChange}
+                onBlur={eventHandlers.onBlur}
+              />
+            </EuiFormRow>
+            <EuiFormRow
+              label={i18n.translate('xpack.security.management.users.userForm.emailLabel', {
+                defaultMessage: 'Email address',
+              })}
+              error={form.errors.email}
+              isInvalid={form.touched.email && !!form.errors.email}
+            >
+              <EuiFieldText
+                name="email"
+                value={form.values.email}
+                isInvalid={form.touched.email && !!form.errors.email}
+                onChange={eventHandlers.onChange}
+                onBlur={eventHandlers.onBlur}
+              />
+            </EuiFormRow>
+          </>
+        ) : undefined}
+      </EuiDescribedFormGroup>
+
+      {isNewUser ? (
+        <EuiDescribedFormGroup
+          title={
+            <h2>
+              <FormattedMessage
+                id="xpack.security.management.users.userForm.passwordTitle"
+                defaultMessage="Password"
+              />
+            </h2>
+          }
+          description={i18n.translate(
+            'xpack.security.management.users.userForm.passwordDescription',
+            {
+              defaultMessage: 'Protect your data with a strong password.',
+            }
+          )}
+        >
+          <EuiFormRow
+            label={i18n.translate('xpack.security.management.users.userForm.passwordLabel', {
+              defaultMessage: 'Password',
+            })}
+            helpText={i18n.translate('xpack.security.management.users.userForm.passwordHelpText', {
+              defaultMessage: 'Password must be at least 6 characters.',
+            })}
+            error={form.errors.password}
+            isInvalid={form.touched.password && !!form.errors.password}
+          >
+            <EuiFieldPassword
+              name="password"
+              type="dual"
+              value={form.values.password}
+              isInvalid={form.touched.password && !!form.errors.password}
+              autoComplete="new-password"
+              onChange={eventHandlers.onChange}
+              onBlur={eventHandlers.onBlur}
+            />
+          </EuiFormRow>
+          <EuiFormRow
+            label={i18n.translate('xpack.security.management.users.userForm.confirmPasswordLabel', {
+              defaultMessage: 'Confirm password',
+            })}
+            error={form.errors.confirm_password}
+            isInvalid={form.touched.confirm_password && !!form.errors.confirm_password}
+          >
+            <EuiFieldPassword
+              name="confirm_password"
+              type="dual"
+              value={form.values.confirm_password}
+              isInvalid={form.touched.confirm_password && !!form.errors.confirm_password}
+              autoComplete="new-password"
+              onChange={eventHandlers.onChange}
+              onBlur={eventHandlers.onBlur}
+            />
+          </EuiFormRow>
+        </EuiDescribedFormGroup>
+      ) : null}
+
+      <EuiDescribedFormGroup
+        title={
+          <h2>
+            <FormattedMessage
+              id="xpack.security.management.users.userForm.privilegesTitle"
+              defaultMessage="Privileges"
+            />
+          </h2>
+        }
+        description={i18n.translate(
+          'xpack.security.management.users.userForm.privilegesDescription',
+          {
+            defaultMessage: 'Assign roles to manage access and permissions.',
+          }
+        )}
+      >
+        <EuiFormRow
+          label={i18n.translate('xpack.security.management.users.userForm.rolesLabel', {
+            defaultMessage: 'Roles',
+          })}
+          helpText={
+            !isReservedUser && deprecatedRoles.length > 0 ? (
+              <EuiTextColor color="warning">
+                {deprecatedRoles.map((role) => (
+                  <p key={role.name}>
+                    <FormattedMessage
+                      id="xpack.security.management.users.userForm.deprecatedRolesAssignedWarning"
+                      defaultMessage="Role '{name}' is deprecated. {reason}."
+                      values={{
+                        name: role.name,
+                        reason: role.metadata?._deprecated_reason?.replace(/\[(.+)\]/, "'$1'"),
+                      }}
+                    />
+                  </p>
+                ))}
+              </EuiTextColor>
+            ) : (
+              <DocLink app="elasticsearch" doc="built-in-roles.html">
+                <FormattedMessage
+                  id="xpack.security.management.users.userForm.rolesHelpText"
+                  defaultMessage="Learn what privileges individual roles grant."
+                />
+              </DocLink>
+            )
+          }
+        >
+          <RoleComboBox
+            availableRoles={availableRoles}
+            selectedRoleNames={selectedRoleNames}
+            onChange={(value) => form.setValue('roles', value)}
+            isLoading={rolesState.loading}
+            isDisabled={isReservedUser}
+          />
+        </EuiFormRow>
+
+        <EuiSpacer size="xxl" />
+        {isReservedUser ? (
+          <EuiFlexGroup responsive={false}>
+            <EuiFlexItem grow={false}>
+              <EuiButton iconType="arrowLeft" onClick={onCancel}>
+                <FormattedMessage
+                  id="xpack.security.management.users.userForm.backToUsersButton"
+                  defaultMessage="Back to users"
+                />
+              </EuiButton>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        ) : (
+          <EuiFlexGroup responsive={false}>
+            <EuiFlexItem grow={false}>
+              <EuiButton
+                type="submit"
+                isLoading={form.isSubmitting}
+                isDisabled={form.isSubmitted && form.isInvalid}
+                fill
+              >
+                {isNewUser ? (
+                  <FormattedMessage
+                    id="xpack.security.management.users.userForm.createUserButton"
+                    defaultMessage="{isSubmitting, select, true{Creating user…} other{Create user}}"
+                    values={{ isSubmitting: form.isSubmitting }}
+                  />
+                ) : (
+                  <FormattedMessage
+                    id="xpack.security.management.users.userForm.updateUserButton"
+                    defaultMessage="{isSubmitting, select, true{Updating user…} other{Update user}}"
+                    values={{ isSubmitting: form.isSubmitting }}
+                  />
+                )}
+              </EuiButton>
+            </EuiFlexItem>
+            <EuiFlexItem grow={false}>
+              <EuiButtonEmpty flush="left" isDisabled={form.isSubmitting} onClick={onCancel}>
+                <FormattedMessage
+                  id="xpack.security.management.users.userForm.cancelButton"
+                  defaultMessage="Cancel"
+                />
+              </EuiButtonEmpty>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        )}
+      </EuiDescribedFormGroup>
+    </EuiForm>
+  );
+};
diff --git a/x-pack/plugins/security/public/management/users/edit_user/validate_user.test.ts b/x-pack/plugins/security/public/management/users/edit_user/validate_user.test.ts
deleted file mode 100644
index 6050e1868a759..0000000000000
--- a/x-pack/plugins/security/public/management/users/edit_user/validate_user.test.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { UserValidator, UserValidationResult } from './validate_user';
-import { User, EditUser } from '../../../../common/model';
-
-function expectValid(result: UserValidationResult) {
-  expect(result.isInvalid).toBe(false);
-}
-
-function expectInvalid(result: UserValidationResult) {
-  expect(result.isInvalid).toBe(true);
-}
-
-describe('UserValidator', () => {
-  describe('#validateUsername', () => {
-    it(`returns 'valid' if validation is disabled`, () => {
-      expectValid(new UserValidator().validateUsername({} as User));
-    });
-
-    it(`returns 'invalid' if username is missing`, () => {
-      expectInvalid(new UserValidator({ shouldValidate: true }).validateUsername({} as User));
-    });
-
-    it(`returns 'invalid' if username contains invalid characters`, () => {
-      expectInvalid(
-        new UserValidator({ shouldValidate: true }).validateUsername({
-          username: '!@#$%^&*()',
-        } as User)
-      );
-    });
-
-    it(`returns 'valid' for correct usernames`, () => {
-      expectValid(
-        new UserValidator({ shouldValidate: true }).validateUsername({
-          username: 'my_user',
-        } as User)
-      );
-    });
-  });
-
-  describe('#validateEmail', () => {
-    it(`returns 'valid' if validation is disabled`, () => {
-      expectValid(new UserValidator().validateEmail({} as EditUser));
-    });
-
-    it(`returns 'valid' if email is missing`, () => {
-      expectValid(new UserValidator({ shouldValidate: true }).validateEmail({} as EditUser));
-    });
-
-    it(`returns 'invalid' for invalid emails`, () => {
-      expectInvalid(
-        new UserValidator({ shouldValidate: true }).validateEmail({
-          email: 'asf',
-        } as EditUser)
-      );
-    });
-
-    it(`returns 'valid' for correct emails`, () => {
-      expectValid(
-        new UserValidator({ shouldValidate: true }).validateEmail({
-          email: 'foo@bar.co',
-        } as EditUser)
-      );
-    });
-  });
-
-  describe('#validatePassword', () => {
-    it(`returns 'valid' if validation is disabled`, () => {
-      expectValid(new UserValidator().validatePassword({} as EditUser));
-    });
-
-    it(`returns 'invalid' if password is missing`, () => {
-      expectInvalid(new UserValidator({ shouldValidate: true }).validatePassword({} as EditUser));
-    });
-
-    it(`returns 'invalid' for invalid password`, () => {
-      expectInvalid(
-        new UserValidator({ shouldValidate: true }).validatePassword({
-          password: 'short',
-        } as EditUser)
-      );
-    });
-
-    it(`returns 'valid' for correct passwords`, () => {
-      expectValid(
-        new UserValidator({ shouldValidate: true }).validatePassword({
-          password: 'changeme',
-        } as EditUser)
-      );
-    });
-  });
-
-  describe('#validateConfirmPassword', () => {
-    it(`returns 'valid' if validation is disabled`, () => {
-      expectValid(new UserValidator().validateConfirmPassword({} as EditUser));
-    });
-
-    it(`returns 'invalid' if confirm password is missing`, () => {
-      expectInvalid(
-        new UserValidator({ shouldValidate: true }).validateConfirmPassword({
-          password: 'changeme',
-        } as EditUser)
-      );
-    });
-
-    it(`returns 'invalid' for mismatched passwords`, () => {
-      expectInvalid(
-        new UserValidator({ shouldValidate: true }).validateConfirmPassword({
-          password: 'changeme',
-          confirmPassword: 'changeyou',
-        } as EditUser)
-      );
-    });
-
-    it(`returns 'valid' for correct passwords`, () => {
-      expectValid(
-        new UserValidator({ shouldValidate: true }).validateConfirmPassword({
-          password: 'changeme',
-          confirmPassword: 'changeme',
-        } as EditUser)
-      );
-    });
-  });
-});
diff --git a/x-pack/plugins/security/public/management/users/edit_user/validate_user.ts b/x-pack/plugins/security/public/management/users/edit_user/validate_user.ts
deleted file mode 100644
index 5edd96c68bf0d..0000000000000
--- a/x-pack/plugins/security/public/management/users/edit_user/validate_user.ts
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { i18n } from '@kbn/i18n';
-import { User, EditUser } from '../../../../common/model';
-
-interface UserValidatorOptions {
-  shouldValidate?: boolean;
-}
-
-export interface UserValidationResult {
-  isInvalid: boolean;
-  error?: string;
-}
-
-const validEmailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
-const validUsernameRegex = /[a-zA-Z_][a-zA-Z0-9_@\-\$\.]*/;
-
-export class UserValidator {
-  private shouldValidate?: boolean;
-
-  constructor(options: UserValidatorOptions = {}) {
-    this.shouldValidate = options.shouldValidate;
-  }
-
-  public enableValidation() {
-    this.shouldValidate = true;
-  }
-
-  public disableValidation() {
-    this.shouldValidate = false;
-  }
-
-  public validateUsername(user: User): UserValidationResult {
-    if (!this.shouldValidate) {
-      return valid();
-    }
-
-    const { username } = user;
-    if (!username) {
-      return invalid(
-        i18n.translate('xpack.security.management.users.editUser.requiredUsernameErrorMessage', {
-          defaultMessage: 'Username is required',
-        })
-      );
-    } else if (username && !username.match(validUsernameRegex)) {
-      return invalid(
-        i18n.translate(
-          'xpack.security.management.users.editUser.usernameAllowedCharactersErrorMessage',
-          {
-            defaultMessage:
-              'Username must begin with a letter or underscore and contain only letters, underscores, and numbers',
-          }
-        )
-      );
-    }
-
-    return valid();
-  }
-
-  public validateEmail(user: EditUser): UserValidationResult {
-    if (!this.shouldValidate) {
-      return valid();
-    }
-
-    const { email } = user;
-    if (email && !email.match(validEmailRegex)) {
-      return invalid(
-        i18n.translate('xpack.security.management.users.editUser.validEmailRequiredErrorMessage', {
-          defaultMessage: 'Email address is invalid',
-        })
-      );
-    }
-    return valid();
-  }
-
-  public validatePassword(user: EditUser): UserValidationResult {
-    if (!this.shouldValidate) {
-      return valid();
-    }
-
-    const { password } = user;
-    if (!password || password.length < 6) {
-      return invalid(
-        i18n.translate('xpack.security.management.users.editUser.passwordLengthErrorMessage', {
-          defaultMessage: 'Password must be at least 6 characters',
-        })
-      );
-    }
-    return valid();
-  }
-
-  public validateConfirmPassword(user: EditUser): UserValidationResult {
-    if (!this.shouldValidate) {
-      return valid();
-    }
-
-    const { password, confirmPassword } = user;
-    if (password && confirmPassword !== null && password !== confirmPassword) {
-      return invalid(
-        i18n.translate('xpack.security.management.users.editUser.passwordDoNotMatchErrorMessage', {
-          defaultMessage: 'Passwords do not match',
-        })
-      );
-    }
-    return valid();
-  }
-
-  public validateForSave(user: EditUser, isNewUser: boolean): UserValidationResult {
-    const { isInvalid: isUsernameInvalid } = this.validateUsername(user);
-    const { isInvalid: isEmailInvalid } = this.validateEmail(user);
-    let isPasswordInvalid = false;
-    let isConfirmPasswordInvalid = false;
-
-    if (isNewUser) {
-      isPasswordInvalid = this.validatePassword(user).isInvalid;
-      isConfirmPasswordInvalid = this.validateConfirmPassword(user).isInvalid;
-    }
-
-    if (isUsernameInvalid || isEmailInvalid || isPasswordInvalid || isConfirmPasswordInvalid) {
-      return invalid();
-    }
-
-    return valid();
-  }
-}
-
-function invalid(error?: string): UserValidationResult {
-  return {
-    isInvalid: true,
-    error,
-  };
-}
-
-function valid(): UserValidationResult {
-  return {
-    isInvalid: false,
-  };
-}
diff --git a/x-pack/plugins/security/public/management/users/user_api_client.mock.ts b/x-pack/plugins/security/public/management/users/user_api_client.mock.ts
index 7223f78d57fdc..54c7ae8f4ae3b 100644
--- a/x-pack/plugins/security/public/management/users/user_api_client.mock.ts
+++ b/x-pack/plugins/security/public/management/users/user_api_client.mock.ts
@@ -9,6 +9,8 @@ export const userAPIClientMock = {
     getUsers: jest.fn(),
     getUser: jest.fn(),
     deleteUser: jest.fn(),
+    enableUser: jest.fn(),
+    disableUser: jest.fn(),
     saveUser: jest.fn(),
     changePassword: jest.fn(),
   }),
diff --git a/x-pack/plugins/security/public/management/users/user_api_client.ts b/x-pack/plugins/security/public/management/users/user_api_client.ts
index 61dd09d2c5e3d..b96596ba7c653 100644
--- a/x-pack/plugins/security/public/management/users/user_api_client.ts
+++ b/x-pack/plugins/security/public/management/users/user_api_client.ts
@@ -30,7 +30,7 @@ export class UserAPIClient {
     });
   }
 
-  public async changePassword(username: string, password: string, currentPassword: string) {
+  public async changePassword(username: string, password: string, currentPassword?: string) {
     const data: Record<string, string> = {
       newPassword: password,
     };
@@ -42,4 +42,12 @@ export class UserAPIClient {
       body: JSON.stringify(data),
     });
   }
+
+  public async disableUser(username: string) {
+    await this.http.post(`${usersUrl}/${encodeURIComponent(username)}/_disable`);
+  }
+
+  public async enableUser(username: string) {
+    await this.http.post(`${usersUrl}/${encodeURIComponent(username)}/_enable`);
+  }
 }
diff --git a/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.tsx b/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.tsx
index 37747f9a1ccfa..3b1705d2bc46b 100644
--- a/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.tsx
+++ b/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.tsx
@@ -237,7 +237,7 @@ export class UsersGridPage extends Component<Props, State> {
             <EuiPageContentHeaderSection>
               <EuiButton
                 data-test-subj="createUserButton"
-                {...reactRouterNavigate(this.props.history, `/edit/`)}
+                {...reactRouterNavigate(this.props.history, `/create`)}
               >
                 <FormattedMessage
                   id="xpack.security.management.users.createNewUserButtonLabel"
diff --git a/x-pack/plugins/security/public/management/users/users_management_app.test.tsx b/x-pack/plugins/security/public/management/users/users_management_app.test.tsx
index f0a594469bd16..8dd4bdc5fd378 100644
--- a/x-pack/plugins/security/public/management/users/users_management_app.test.tsx
+++ b/x-pack/plugins/security/public/management/users/users_management_app.test.tsx
@@ -4,125 +4,34 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-jest.mock('./users_grid', () => ({
-  UsersGridPage: (props: any) => `Users Page: ${JSON.stringify(props)}`,
-}));
-
-jest.mock('./edit_user', () => ({
-  EditUserPage: (props: any) => `User Edit Page: ${JSON.stringify(props)}`,
-}));
-
-import { usersManagementApp } from './users_management_app';
-
 import { coreMock, scopedHistoryMock } from '../../../../../../src/core/public/mocks';
 import { securityMock } from '../../mocks';
+import { usersManagementApp } from './users_management_app';
 
-async function mountApp(basePath: string, pathname: string) {
-  const container = document.createElement('div');
-  const setBreadcrumbs = jest.fn();
+const element = document.body.appendChild(document.createElement('div'));
 
-  const unmount = await usersManagementApp
-    .create({
-      authc: securityMock.createSetup().authc,
-      getStartServices: coreMock.createSetup().getStartServices as any,
-    })
-    .mount({
-      basePath,
-      element: container,
+describe('usersManagementApp', () => {
+  it('renders application and sets breadcrumbs', async () => {
+    const { getStartServices } = coreMock.createSetup();
+    const coreStartMock = coreMock.createStart();
+    getStartServices.mockResolvedValue([coreStartMock, {}, {}]);
+    const { authc } = securityMock.createSetup();
+
+    const setBreadcrumbs = jest.fn();
+    const history = scopedHistoryMock.create({ pathname: '/create' });
+
+    const unmount = await usersManagementApp.create({ authc, getStartServices }).mount({
+      basePath: '/',
+      element,
       setBreadcrumbs,
-      history: scopedHistoryMock.create({ pathname }),
+      history,
     });
 
-  return { unmount, container, setBreadcrumbs };
-}
-
-describe('usersManagementApp', () => {
-  it('create() returns proper management app descriptor', () => {
-    expect(
-      usersManagementApp.create({
-        authc: securityMock.createSetup().authc,
-        getStartServices: coreMock.createSetup().getStartServices as any,
-      })
-    ).toMatchInlineSnapshot(`
-      Object {
-        "id": "users",
-        "mount": [Function],
-        "order": 10,
-        "title": "Users",
-      }
-    `);
-  });
-
-  it('mount() works for the `grid` page', async () => {
-    const { setBreadcrumbs, container, unmount } = await mountApp('/', '/');
-
-    expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
-    expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `/`, text: 'Users' }]);
-    expect(container).toMatchInlineSnapshot(`
-      <div>
-        Users Page: {"notifications":{"toasts":{}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}}}
-      </div>
-    `);
-
-    unmount();
-
-    expect(container).toMatchInlineSnapshot(`<div />`);
-  });
-
-  it('mount() works for the `create user` page', async () => {
-    const { setBreadcrumbs, container, unmount } = await mountApp('/', '/edit');
-
-    expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
-    expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `/`, text: 'Users' }, { text: 'Create' }]);
-    expect(container).toMatchInlineSnapshot(`
-      <div>
-        User Edit Page: {"authc":{},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"notifications":{"toasts":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}}}
-      </div>
-    `);
-
-    unmount();
-
-    expect(container).toMatchInlineSnapshot(`<div />`);
-  });
-
-  it('mount() works for the `edit user` page', async () => {
-    const userName = 'foo@bar.com';
-
-    const { setBreadcrumbs, container, unmount } = await mountApp('/', `/edit/${userName}`);
-
-    expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
-    expect(setBreadcrumbs).toHaveBeenCalledWith([
-      { href: `/`, text: 'Users' },
-      { href: `/edit/${encodeURIComponent(userName)}`, text: userName },
+    expect(setBreadcrumbs).toHaveBeenLastCalledWith([
+      { href: '/', text: 'Users' },
+      { href: '/create', text: 'Create' },
     ]);
-    expect(container).toMatchInlineSnapshot(`
-      <div>
-        User Edit Page: {"authc":{},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"notifications":{"toasts":{}},"username":"foo@bar.com","history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/foo@bar.com","search":"","hash":""}}}
-      </div>
-    `);
 
     unmount();
-
-    expect(container).toMatchInlineSnapshot(`<div />`);
-  });
-
-  const usernames = ['foo@bar.com', 'foo&bar.com', 'some 安全性 user'];
-  usernames.forEach((username) => {
-    it(
-      'mount() properly encodes user name in `edit user` page link in breadcrumbs for user ' +
-        username,
-      async () => {
-        const { setBreadcrumbs } = await mountApp('/', `/edit/${username}`);
-
-        expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
-        expect(setBreadcrumbs).toHaveBeenCalledWith([
-          { href: `/`, text: 'Users' },
-          {
-            href: `/edit/${encodeURIComponent(username)}`,
-            text: username,
-          },
-        ]);
-      }
-    );
   });
 });
diff --git a/x-pack/plugins/security/public/management/users/users_management_app.tsx b/x-pack/plugins/security/public/management/users/users_management_app.tsx
index 2f16f85d5fcae..cbb303d1a128d 100644
--- a/x-pack/plugins/security/public/management/users/users_management_app.tsx
+++ b/x-pack/plugins/security/public/management/users/users_management_app.tsx
@@ -4,14 +4,24 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import React from 'react';
+import React, { FunctionComponent } from 'react';
 import { render, unmountComponentAtNode } from 'react-dom';
-import { Router, Route, Switch, useParams } from 'react-router-dom';
+import { Router, Route, Switch, Redirect, RouteComponentProps } from 'react-router-dom';
+import { History } from 'history';
 import { i18n } from '@kbn/i18n';
-import { StartServicesAccessor } from 'src/core/public';
+import { I18nProvider } from '@kbn/i18n/react';
+import { StartServicesAccessor, CoreStart } from '../../../../../../src/core/public';
 import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public';
+import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
 import { AuthenticationServiceSetup } from '../../authentication';
 import { PluginStartDependencies } from '../../plugin';
+import {
+  BreadcrumbsProvider,
+  BreadcrumbsChangeHandler,
+  Breadcrumb,
+  getDocTitle,
+} from '../../components/breadcrumb';
+import { AuthenticationProvider } from '../../components/use_current_user';
 import { tryDecodeURIComponent } from '../url_utils';
 
 interface CreateParams {
@@ -19,6 +29,10 @@ interface CreateParams {
   getStartServices: StartServicesAccessor<PluginStartDependencies>;
 }
 
+interface EditUserParams {
+  username: string;
+}
+
 export const usersManagementApp = Object.freeze({
   id: 'users',
   create({ authc, getStartServices }: CreateParams) {
@@ -27,18 +41,10 @@ export const usersManagementApp = Object.freeze({
       order: 10,
       title: i18n.translate('xpack.security.management.usersTitle', { defaultMessage: 'Users' }),
       async mount({ element, setBreadcrumbs, history }) {
-        const [coreStart] = await getStartServices();
-        const usersBreadcrumbs = [
-          {
-            text: i18n.translate('xpack.security.users.breadcrumb', { defaultMessage: 'Users' }),
-            href: `/`,
-          },
-        ];
-
         const [
-          [{ http, notifications, i18n: i18nStart }],
+          [coreStart],
           { UsersGridPage },
-          { EditUserPage },
+          { CreateUserPage, EditUserPage },
           { UserAPIClient },
           { RolesAPIClient },
         ] = await Promise.all([
@@ -49,64 +55,61 @@ export const usersManagementApp = Object.freeze({
           import('../roles'),
         ]);
 
-        const userAPIClient = new UserAPIClient(http);
-        const rolesAPIClient = new RolesAPIClient(http);
-        const UsersGridPageWithBreadcrumbs = () => {
-          setBreadcrumbs(usersBreadcrumbs);
-          return (
-            <UsersGridPage
-              notifications={notifications}
-              userAPIClient={userAPIClient}
-              rolesAPIClient={rolesAPIClient}
-              history={history}
-              navigateToApp={coreStart.application.navigateToApp}
-            />
-          );
-        };
-
-        const EditUserPageWithBreadcrumbs = () => {
-          const { username } = useParams<{ username?: string }>();
-
-          // Additional decoding is a workaround for a bug in react-router's version of the `history` module.
-          // See https://github.com/elastic/kibana/issues/82440
-          const decodedUsername = username ? tryDecodeURIComponent(username) : undefined;
-
-          setBreadcrumbs([
-            ...usersBreadcrumbs,
-            username
-              ? { text: decodedUsername, href: `/edit/${encodeURIComponent(username)}` }
-              : {
-                  text: i18n.translate('xpack.security.users.createBreadcrumb', {
-                    defaultMessage: 'Create',
-                  }),
-                },
-          ]);
-
-          return (
-            <EditUserPage
-              authc={authc}
-              userAPIClient={userAPIClient}
-              rolesAPIClient={new RolesAPIClient(http)}
-              notifications={notifications}
-              username={decodedUsername}
-              history={history}
-            />
-          );
-        };
-
         render(
-          <i18nStart.Context>
-            <Router history={history}>
+          <Providers
+            services={coreStart}
+            history={history}
+            authc={authc}
+            onChange={(breadcrumbs) => {
+              setBreadcrumbs(breadcrumbs);
+              coreStart.chrome.docTitle.change(getDocTitle(breadcrumbs));
+            }}
+          >
+            <Breadcrumb
+              text={i18n.translate('xpack.security.users.breadcrumb', {
+                defaultMessage: 'Users',
+              })}
+              href="/"
+            >
               <Switch>
                 <Route path={['/', '']} exact>
-                  <UsersGridPageWithBreadcrumbs />
+                  <UsersGridPage
+                    notifications={coreStart.notifications}
+                    userAPIClient={new UserAPIClient(coreStart.http)}
+                    rolesAPIClient={new RolesAPIClient(coreStart.http)}
+                    history={history}
+                    navigateToApp={coreStart.application.navigateToApp}
+                  />
                 </Route>
-                <Route path="/edit/:username?">
-                  <EditUserPageWithBreadcrumbs />
+                <Route path="/create">
+                  <Breadcrumb
+                    text={i18n.translate('xpack.security.users.editUserPage.createBreadcrumb', {
+                      defaultMessage: 'Create',
+                    })}
+                    href="/create"
+                  >
+                    <CreateUserPage />
+                  </Breadcrumb>
+                </Route>
+                <Route
+                  path="/edit/:username"
+                  render={(props: RouteComponentProps<EditUserParams>) => {
+                    // Additional decoding is a workaround for a bug in react-router's version of the `history` module.
+                    // See https://github.com/elastic/kibana/issues/82440
+                    const username = tryDecodeURIComponent(props.match.params.username);
+                    return (
+                      <Breadcrumb text={username} href={`/edit/${encodeURIComponent(username)}`}>
+                        <EditUserPage username={username} />
+                      </Breadcrumb>
+                    );
+                  }}
+                />
+                <Route path="/edit">
+                  <Redirect to="/create" />
                 </Route>
               </Switch>
-            </Router>
-          </i18nStart.Context>,
+            </Breadcrumb>
+          </Providers>,
           element
         );
 
@@ -117,3 +120,28 @@ export const usersManagementApp = Object.freeze({
     } as RegisterManagementAppArgs;
   },
 });
+
+export interface ProvidersProps {
+  services: CoreStart;
+  history: History;
+  authc: AuthenticationServiceSetup;
+  onChange?: BreadcrumbsChangeHandler;
+}
+
+export const Providers: FunctionComponent<ProvidersProps> = ({
+  services,
+  history,
+  authc,
+  onChange,
+  children,
+}) => (
+  <KibanaContextProvider services={services}>
+    <AuthenticationProvider authc={authc}>
+      <I18nProvider>
+        <Router history={history}>
+          <BreadcrumbsProvider onChange={onChange}>{children}</BreadcrumbsProvider>
+        </Router>
+      </I18nProvider>
+    </AuthenticationProvider>
+  </KibanaContextProvider>
+);
diff --git a/x-pack/plugins/security/server/routes/users/create_or_update.ts b/x-pack/plugins/security/server/routes/users/create_or_update.ts
index a98848a583500..bdb6e89719037 100644
--- a/x-pack/plugins/security/server/routes/users/create_or_update.ts
+++ b/x-pack/plugins/security/server/routes/users/create_or_update.ts
@@ -30,12 +30,7 @@ export function defineCreateOrUpdateUserRoutes({ router }: RouteDefinitionParams
       try {
         await context.core.elasticsearch.client.asCurrentUser.security.putUser({
           username: request.params.username,
-          // Omit `username`, `enabled` and all fields with `null` value.
-          body: Object.fromEntries(
-            Object.entries(request.body).filter(
-              ([key, value]) => value !== null && key !== 'enabled' && key !== 'username'
-            )
-          ),
+          body: request.body,
         });
 
         return response.ok({ body: request.body });
diff --git a/x-pack/plugins/security/server/routes/users/disable.ts b/x-pack/plugins/security/server/routes/users/disable.ts
new file mode 100644
index 0000000000000..45e1f63149e1a
--- /dev/null
+++ b/x-pack/plugins/security/server/routes/users/disable.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;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema } from '@kbn/config-schema';
+import { RouteDefinitionParams } from '../index';
+import { wrapIntoCustomErrorResponse } from '../../errors';
+import { createLicensedRouteHandler } from '../licensed_route_handler';
+
+export function defineDisableUserRoutes({ router }: RouteDefinitionParams) {
+  router.post(
+    {
+      path: '/internal/security/users/{username}/_disable',
+      validate: {
+        params: schema.object({ username: schema.string({ minLength: 1, maxLength: 1024 }) }),
+      },
+    },
+    createLicensedRouteHandler(async (context, request, response) => {
+      try {
+        await context.core.elasticsearch.client.asCurrentUser.security.disableUser({
+          username: request.params.username,
+        });
+
+        return response.noContent();
+      } catch (error) {
+        return response.customError(wrapIntoCustomErrorResponse(error));
+      }
+    })
+  );
+}
diff --git a/x-pack/plugins/security/server/routes/users/enable.ts b/x-pack/plugins/security/server/routes/users/enable.ts
new file mode 100644
index 0000000000000..0f4e15c953a42
--- /dev/null
+++ b/x-pack/plugins/security/server/routes/users/enable.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;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema } from '@kbn/config-schema';
+import { RouteDefinitionParams } from '../index';
+import { wrapIntoCustomErrorResponse } from '../../errors';
+import { createLicensedRouteHandler } from '../licensed_route_handler';
+
+export function defineEnableUserRoutes({ router }: RouteDefinitionParams) {
+  router.post(
+    {
+      path: '/internal/security/users/{username}/_enable',
+      validate: {
+        params: schema.object({ username: schema.string({ minLength: 1, maxLength: 1024 }) }),
+      },
+    },
+    createLicensedRouteHandler(async (context, request, response) => {
+      try {
+        await context.core.elasticsearch.client.asCurrentUser.security.enableUser({
+          username: request.params.username,
+        });
+
+        return response.noContent();
+      } catch (error) {
+        return response.customError(wrapIntoCustomErrorResponse(error));
+      }
+    })
+  );
+}
diff --git a/x-pack/plugins/security/server/routes/users/index.ts b/x-pack/plugins/security/server/routes/users/index.ts
index 931af0734b416..473b3459ad4e1 100644
--- a/x-pack/plugins/security/server/routes/users/index.ts
+++ b/x-pack/plugins/security/server/routes/users/index.ts
@@ -9,6 +9,8 @@ import { defineGetUserRoutes } from './get';
 import { defineGetAllUsersRoutes } from './get_all';
 import { defineCreateOrUpdateUserRoutes } from './create_or_update';
 import { defineDeleteUserRoutes } from './delete';
+import { defineDisableUserRoutes } from './disable';
+import { defineEnableUserRoutes } from './enable';
 import { defineChangeUserPasswordRoutes } from './change_password';
 
 export function defineUsersRoutes(params: RouteDefinitionParams) {
@@ -16,5 +18,7 @@ export function defineUsersRoutes(params: RouteDefinitionParams) {
   defineGetAllUsersRoutes(params);
   defineCreateOrUpdateUserRoutes(params);
   defineDeleteUserRoutes(params);
+  defineDisableUserRoutes(params);
+  defineEnableUserRoutes(params);
   defineChangeUserPasswordRoutes(params);
 }
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 1bbf4b8033755..3a579adcf88c2 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -16894,7 +16894,7 @@
     "xpack.security.management.editRole.updateRoleText": "ロールを更新",
     "xpack.security.management.editRole.validateRole.indicesTypeErrorMessage": "{elasticIndices} は数列でなければなりません",
     "xpack.security.management.editRole.validateRole.nameAllowedCharactersWarningMessage": "名前は文字またはアンダーラインで始まり、文字、アンダーライン、数字のみ使用できます。",
-    "xpack.security.management.editRole.validateRole.nameLengthWarningMessage": "名前は 1024 文字以内でなければなりません",
+    "xpack.security.management.editRole.validateRole.nameLengthWarningMessage": "名前は {maxLength} 文字以内でなければなりません",
     "xpack.security.management.editRole.validateRole.onePrivilegeRequiredWarningMessage": "権限が最低 1 つ必要です",
     "xpack.security.management.editRole.validateRole.oneSpaceRequiredWarningMessage": "スペースが最低 1 つ必要です",
     "xpack.security.management.editRole.validateRole.privilegeRequiredWarningMessage": "権限が必要です",
@@ -17073,37 +17073,6 @@
     "xpack.security.management.users.createNewUserButtonLabel": "ユーザーを作成",
     "xpack.security.management.users.deleteUsersButtonLabel": "{numSelected} 人のユーザー{numSelected, plural, one { } other {s}} 削除",
     "xpack.security.management.users.deniedPermissionTitle": "ユーザーを管理するにはパーミッションが必要です",
-    "xpack.security.management.users.editUser.addRolesPlaceholder": "ロールを追加",
-    "xpack.security.management.users.editUser.cancelButtonLabel": "キャンセル",
-    "xpack.security.management.users.editUser.changePasswordButtonLabel": "パスワードを変更",
-    "xpack.security.management.users.editUser.changePasswordExtraStepTitle": "追加ステップが必要です",
-    "xpack.security.management.users.editUser.changePasswordUpdateKibanaTitle": "{username}ユーザーのパスワードを変更後、{kibana}ファイルを更新し、Kibanaを再起動する必要があります。",
-    "xpack.security.management.users.editUser.changingUserNameAfterCreationDescription": "ユーザー名は作成後変更できません。",
-    "xpack.security.management.users.editUser.confirmPasswordFormRowLabel": "パスワードの確認",
-    "xpack.security.management.users.editUser.createUserButtonLabel": "ユーザーを作成",
-    "xpack.security.management.users.editUser.deleteUserButtonLabel": "ユーザーを削除",
-    "xpack.security.management.users.editUser.deprecatedRolesAssignedWarning": "このユーザーには非推奨ロールが割り当てられています。サポートされているロールに移行してください。",
-    "xpack.security.management.users.editUser.deprecatedRoleText": "(非推奨)",
-    "xpack.security.management.users.editUser.editUserTitle": "ユーザー {userName} の編集",
-    "xpack.security.management.users.editUser.emailAddressFormRowLabel": "メールアドレス",
-    "xpack.security.management.users.editUser.errorLoadingRolesTitle": "ロールの読み込み中にエラーが発生",
-    "xpack.security.management.users.editUser.errorLoadingUserTitle": "ユーザーの読み込み中にエラーが発生",
-    "xpack.security.management.users.editUser.fullNameFormRowLabel": "フルネーム",
-    "xpack.security.management.users.editUser.modifyingReservedUsersDescription": "リザーブされたユーザーはビルトインのため削除または変更できません。パスワードのみ変更できます。",
-    "xpack.security.management.users.editUser.newUserTitle": "新規ユーザー",
-    "xpack.security.management.users.editUser.passwordDoNotMatchErrorMessage": "パスワードが一致しません",
-    "xpack.security.management.users.editUser.passwordFormRowLabel": "パスワード",
-    "xpack.security.management.users.editUser.passwordLengthErrorMessage": "パスワードは最低 6 文字必要です",
-    "xpack.security.management.users.editUser.requiredUsernameErrorMessage": "ユーザー名が必要です",
-    "xpack.security.management.users.editUser.returnToUserListButtonLabel": "ユーザーリストに戻る",
-    "xpack.security.management.users.editUser.rolesFormRowLabel": "ロール",
-    "xpack.security.management.users.editUser.savingUserErrorMessage": "ユーザーの保存中にエラーが発生しました: {message}",
-    "xpack.security.management.users.editUser.settingPasswordErrorMessage": "パスワードの設定中にエラーが発生しました: {message}",
-    "xpack.security.management.users.editUser.updateUserButtonLabel": "ユーザーを更新",
-    "xpack.security.management.users.editUser.usernameAllowedCharactersErrorMessage": "ユーザー名は文字またはアンダーラインで始まり、文字、アンダーライン、数字のみ使用できます",
-    "xpack.security.management.users.editUser.usernameFormRowLabel": "ユーザー名",
-    "xpack.security.management.users.editUser.userSuccessfullySavedNotificationMessage": "保存されたユーザー {message}",
-    "xpack.security.management.users.editUser.validEmailRequiredErrorMessage": "メールアドレスが無効です",
     "xpack.security.management.users.emailAddressColumnName": "メールアドレス",
     "xpack.security.management.users.extendedUserDeprecationNotice": "{username}ユーザーは推奨されません。{reason}",
     "xpack.security.management.users.fetchingUsersErrorMessage": "ユーザーの取得中にエラーが発生: {message}",
@@ -17140,7 +17109,6 @@
     "xpack.security.roles.breadcrumb": "ロール",
     "xpack.security.roles.createBreadcrumb": "作成",
     "xpack.security.users.breadcrumb": "ユーザー",
-    "xpack.security.users.createBreadcrumb": "作成",
     "xpack.securitySolution.accessibility.tooltipWithKeyboardShortcut.pressTooltipLabel": "プレス",
     "xpack.securitySolution.add_filter_to_global_search_bar.filterForValueHoverAction": "値でフィルター",
     "xpack.securitySolution.add_filter_to_global_search_bar.filterOutValueHoverAction": "値を除外",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 51205a3420be5..9099fa0267ba9 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -16938,7 +16938,7 @@
     "xpack.security.management.editRole.updateRoleText": "更新角色",
     "xpack.security.management.editRole.validateRole.indicesTypeErrorMessage": "{elasticIndices} 应为数组",
     "xpack.security.management.editRole.validateRole.nameAllowedCharactersWarningMessage": "名称必须以字母或下划线开头,且只能包含字母、下划线和数字。",
-    "xpack.security.management.editRole.validateRole.nameLengthWarningMessage": "名称不能超过 1024 个字符",
+    "xpack.security.management.editRole.validateRole.nameLengthWarningMessage": "名称不能超过 {maxLength} 个字符",
     "xpack.security.management.editRole.validateRole.onePrivilegeRequiredWarningMessage": "至少需要一个权限",
     "xpack.security.management.editRole.validateRole.oneSpaceRequiredWarningMessage": "至少需要一个工作区",
     "xpack.security.management.editRole.validateRole.privilegeRequiredWarningMessage": "“权限”必填",
@@ -17117,37 +17117,6 @@
     "xpack.security.management.users.createNewUserButtonLabel": "创建用户",
     "xpack.security.management.users.deleteUsersButtonLabel": "删除 {numSelected} 个用户{numSelected, plural, one { } other { 个用户}}",
     "xpack.security.management.users.deniedPermissionTitle": "您需要用于管理用户的权限",
-    "xpack.security.management.users.editUser.addRolesPlaceholder": "添加角色",
-    "xpack.security.management.users.editUser.cancelButtonLabel": "取消",
-    "xpack.security.management.users.editUser.changePasswordButtonLabel": "更改密码",
-    "xpack.security.management.users.editUser.changePasswordExtraStepTitle": "需要额外的步骤",
-    "xpack.security.management.users.editUser.changePasswordUpdateKibanaTitle": "更改 {username} 用户的密码后,必须更新 {kibana} 文件并重新启动 Kibana。",
-    "xpack.security.management.users.editUser.changingUserNameAfterCreationDescription": "用户名一经创建,将无法更改。",
-    "xpack.security.management.users.editUser.confirmPasswordFormRowLabel": "确认密码",
-    "xpack.security.management.users.editUser.createUserButtonLabel": "创建用户",
-    "xpack.security.management.users.editUser.deleteUserButtonLabel": "删除用户",
-    "xpack.security.management.users.editUser.deprecatedRolesAssignedWarning": "为此用户分配了过时的角色。请迁移到支持的角色。",
-    "xpack.security.management.users.editUser.deprecatedRoleText": "(已过时)",
-    "xpack.security.management.users.editUser.editUserTitle": "编辑 {userName} 用户",
-    "xpack.security.management.users.editUser.emailAddressFormRowLabel": "电子邮件地址",
-    "xpack.security.management.users.editUser.errorLoadingRolesTitle": "加载角色时出错",
-    "xpack.security.management.users.editUser.errorLoadingUserTitle": "加载用户时出错",
-    "xpack.security.management.users.editUser.fullNameFormRowLabel": "全名",
-    "xpack.security.management.users.editUser.modifyingReservedUsersDescription": "保留用户是内置用户,无法删除或修改。只能更改密码。",
-    "xpack.security.management.users.editUser.newUserTitle": "新建用户",
-    "xpack.security.management.users.editUser.passwordDoNotMatchErrorMessage": "密码不匹配",
-    "xpack.security.management.users.editUser.passwordFormRowLabel": "密码",
-    "xpack.security.management.users.editUser.passwordLengthErrorMessage": "密码长度必须至少为 6 个字符",
-    "xpack.security.management.users.editUser.requiredUsernameErrorMessage": "“用户名”必填",
-    "xpack.security.management.users.editUser.returnToUserListButtonLabel": "返回到用户列表",
-    "xpack.security.management.users.editUser.rolesFormRowLabel": "角色",
-    "xpack.security.management.users.editUser.savingUserErrorMessage": "保存用户时出错:{message}",
-    "xpack.security.management.users.editUser.settingPasswordErrorMessage": "设置密码时出错:{message}",
-    "xpack.security.management.users.editUser.updateUserButtonLabel": "更新用户",
-    "xpack.security.management.users.editUser.usernameAllowedCharactersErrorMessage": "用户名必须以字母或下划线开头,并只能包含字母、下划线和数字",
-    "xpack.security.management.users.editUser.usernameFormRowLabel": "用户名",
-    "xpack.security.management.users.editUser.userSuccessfullySavedNotificationMessage": "已保存用户{message}",
-    "xpack.security.management.users.editUser.validEmailRequiredErrorMessage": "电子邮件地址无效",
     "xpack.security.management.users.emailAddressColumnName": "电子邮件地址",
     "xpack.security.management.users.extendedUserDeprecationNotice": "用户 {username} 已过时。{reason}",
     "xpack.security.management.users.fetchingUsersErrorMessage": "提取用户时出错:{message}",
@@ -17184,7 +17153,6 @@
     "xpack.security.roles.breadcrumb": "角色",
     "xpack.security.roles.createBreadcrumb": "创建",
     "xpack.security.users.breadcrumb": "用户",
-    "xpack.security.users.createBreadcrumb": "创建",
     "xpack.securitySolution.accessibility.tooltipWithKeyboardShortcut.pressTooltipLabel": "按",
     "xpack.securitySolution.add_filter_to_global_search_bar.filterForValueHoverAction": "筛留值",
     "xpack.securitySolution.add_filter_to_global_search_bar.filterOutValueHoverAction": "筛除值",
diff --git a/x-pack/test/accessibility/apps/users.ts b/x-pack/test/accessibility/apps/users.ts
index efdcf4f3f022f..ede120ca43de7 100644
--- a/x-pack/test/accessibility/apps/users.ts
+++ b/x-pack/test/accessibility/apps/users.ts
@@ -13,6 +13,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
   const a11y = getService('a11y');
   const esArchiver = getService('esArchiver');
   const testSubjects = getService('testSubjects');
+  const find = getService('find');
   const retry = getService('retry');
 
   describe('Kibana users page a11y tests', () => {
@@ -52,24 +53,29 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
     });
 
     it('a11y test for roles drop down', async () => {
-      await testSubjects.setValue('userFormUserNameInput', 'a11y');
-      await testSubjects.setValue('passwordInput', 'password');
-      await testSubjects.setValue('passwordConfirmationInput', 'password');
-      await testSubjects.setValue('userFormFullNameInput', 'a11y user');
-      await testSubjects.setValue('userFormEmailInput', 'example@example.com');
+      await PageObjects.security.clickElasticsearchUsers();
+      await PageObjects.security.clickCreateNewUser();
+      await PageObjects.security.fillUserForm({
+        username: 'a11y',
+        password: 'password',
+        confirm_password: 'password',
+        full_name: 'a11y user',
+        email: 'example@example.com',
+        roles: ['apm_user'],
+      });
       await testSubjects.click('rolesDropdown');
       await a11y.testAppSnapshot();
     });
 
-    it('a11y test for display of delete button on users page ', async () => {
-      await testSubjects.setValue('userFormUserNameInput', 'deleteA11y');
-      await testSubjects.setValue('passwordInput', 'password');
-      await testSubjects.setValue('passwordConfirmationInput', 'password');
-      await testSubjects.setValue('userFormFullNameInput', 'DeleteA11y user');
-      await testSubjects.setValue('userFormEmailInput', 'example@example.com');
-      await testSubjects.click('rolesDropdown');
-      await testSubjects.setValue('rolesDropdown', 'roleOption-apm_user');
-      await testSubjects.click('userFormSaveButton');
+    it('a11y test for display of delete button on users page', async () => {
+      await PageObjects.security.createUser({
+        username: 'deleteA11y',
+        password: 'password',
+        confirm_password: 'password',
+        full_name: 'DeleteA11y user',
+        email: 'example@example.com',
+        roles: ['apm_user'],
+      });
       await testSubjects.click('checkboxSelectRow-deleteA11y');
       await a11y.testAppSnapshot();
     });
@@ -77,17 +83,24 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
     it('a11y test for delete user panel ', async () => {
       await testSubjects.click('deleteUserButton');
       await a11y.testAppSnapshot();
+      await testSubjects.click('confirmModalCancelButton');
     });
 
     it('a11y test for edit user panel', async () => {
-      await testSubjects.click('confirmModalCancelButton');
       await PageObjects.settings.clickLinkText('deleteA11y');
       await a11y.testAppSnapshot();
     });
 
-    it('a11y test for Change password screen', async () => {
+    it('a11y test for change password screen', async () => {
+      await PageObjects.settings.clickLinkText('deleteA11y');
+      await find.clickByButtonText('Change password');
+      await a11y.testAppSnapshot();
+      await testSubjects.click('formFlyoutCancelButton');
+    });
+
+    it('a11y test for deactivate user screen', async () => {
       await PageObjects.settings.clickLinkText('deleteA11y');
-      await testSubjects.click('changePassword');
+      await find.clickByButtonText('Deactivate user');
       await a11y.testAppSnapshot();
     });
   });
diff --git a/x-pack/test/api_integration/apis/security/index.ts b/x-pack/test/api_integration/apis/security/index.ts
index 2d112215f4fc1..9084e635f8109 100644
--- a/x-pack/test/api_integration/apis/security/index.ts
+++ b/x-pack/test/api_integration/apis/security/index.ts
@@ -19,6 +19,7 @@ export default function ({ loadTestFile }: FtrProviderContext) {
     loadTestFile(require.resolve('./change_password'));
     loadTestFile(require.resolve('./index_fields'));
     loadTestFile(require.resolve('./roles'));
+    loadTestFile(require.resolve('./users'));
     loadTestFile(require.resolve('./privileges'));
   });
 }
diff --git a/x-pack/test/api_integration/apis/security/security_basic.ts b/x-pack/test/api_integration/apis/security/security_basic.ts
index 191523e969717..6872f423fe630 100644
--- a/x-pack/test/api_integration/apis/security/security_basic.ts
+++ b/x-pack/test/api_integration/apis/security/security_basic.ts
@@ -19,6 +19,7 @@ export default function ({ loadTestFile }: FtrProviderContext) {
     loadTestFile(require.resolve('./change_password'));
     loadTestFile(require.resolve('./index_fields'));
     loadTestFile(require.resolve('./roles'));
+    loadTestFile(require.resolve('./users'));
     loadTestFile(require.resolve('./privileges_basic'));
   });
 }
diff --git a/x-pack/test/api_integration/apis/security/users.ts b/x-pack/test/api_integration/apis/security/users.ts
new file mode 100644
index 0000000000000..e177cf998beee
--- /dev/null
+++ b/x-pack/test/api_integration/apis/security/users.ts
@@ -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;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import expect from '@kbn/expect';
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export default function ({ getService }: FtrProviderContext) {
+  const supertest = getService('supertest');
+  const security = getService('security');
+  const es = getService('es');
+
+  const mockUserName = 'test-user';
+  const mockUserPassword = 'test-password';
+
+  describe('Users', () => {
+    beforeEach(async () => {
+      await security.user.create(mockUserName, { password: mockUserPassword, roles: [] });
+    });
+
+    afterEach(async () => {
+      await security.user.delete(mockUserName);
+    });
+
+    it('should disable user', async () => {
+      await supertest
+        .post(`/internal/security/users/${mockUserName}/_disable`)
+        .set('kbn-xsrf', 'xxx')
+        .expect(204);
+
+      const { body } = await es.security.getUser({ username: mockUserName });
+      expect(body[mockUserName].enabled).to.be(false);
+    });
+
+    it('should enable user', async () => {
+      await supertest
+        .post(`/internal/security/users/${mockUserName}/_enable`)
+        .set('kbn-xsrf', 'xxx')
+        .expect(204);
+
+      const { body } = await es.security.getUser({ username: mockUserName });
+      expect(body[mockUserName].enabled).to.be(true);
+    });
+  });
+}
diff --git a/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js b/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js
index 00183113a4d59..b2ddf7d47b1f1 100644
--- a/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js
+++ b/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js
@@ -68,46 +68,36 @@ export default function ({ getService, getPageObjects }) {
 
       before('Create dashboard only mode user', async () => {
         await PageObjects.settings.navigateTo();
-        await PageObjects.security.clickUsersSection();
-        await PageObjects.security.clickCreateNewUser();
-        await testSubjects.setValue('userFormUserNameInput', 'dashuser');
-        await testSubjects.setValue('passwordInput', '123456');
-        await testSubjects.setValue('passwordConfirmationInput', '123456');
-        await testSubjects.setValue('userFormFullNameInput', 'dashuser');
-        await testSubjects.setValue('userFormEmailInput', 'example@example.com');
-        await PageObjects.security.assignRoleToUser('kibana_dashboard_only_user');
-        await PageObjects.security.assignRoleToUser('logstash-data');
-
-        await PageObjects.security.clickSaveEditUser();
+        await PageObjects.security.createUser({
+          username: 'dashuser',
+          password: '123456',
+          confirm_password: '123456',
+          email: 'example@example.com',
+          full_name: 'dashuser',
+          roles: ['kibana_dashboard_only_user', 'logstash-data'],
+        });
       });
 
       before('Create user with mixes roles', async () => {
-        await PageObjects.security.clickCreateNewUser();
-
-        await testSubjects.setValue('userFormUserNameInput', 'mixeduser');
-        await testSubjects.setValue('passwordInput', '123456');
-        await testSubjects.setValue('passwordConfirmationInput', '123456');
-        await testSubjects.setValue('userFormFullNameInput', 'mixeduser');
-        await testSubjects.setValue('userFormEmailInput', 'example@example.com');
-        await PageObjects.security.assignRoleToUser('kibana_dashboard_only_user');
-        await PageObjects.security.assignRoleToUser('kibana_admin');
-        await PageObjects.security.assignRoleToUser('logstash-data');
-
-        await PageObjects.security.clickSaveEditUser();
+        await PageObjects.security.createUser({
+          username: 'mixeduser',
+          password: '123456',
+          confirm_password: '123456',
+          email: 'example@example.com',
+          full_name: 'mixeduser',
+          roles: ['kibana_dashboard_only_user', 'kibana_admin', 'logstash-data'],
+        });
       });
 
       before('Create user with dashboard and superuser role', async () => {
-        await PageObjects.security.clickCreateNewUser();
-
-        await testSubjects.setValue('userFormUserNameInput', 'mysuperuser');
-        await testSubjects.setValue('passwordInput', '123456');
-        await testSubjects.setValue('passwordConfirmationInput', '123456');
-        await testSubjects.setValue('userFormFullNameInput', 'mixeduser');
-        await testSubjects.setValue('userFormEmailInput', 'example@example.com');
-        await PageObjects.security.assignRoleToUser('kibana_dashboard_only_user');
-        await PageObjects.security.assignRoleToUser('superuser');
-
-        await PageObjects.security.clickSaveEditUser();
+        await PageObjects.security.createUser({
+          username: 'mysuperuser',
+          password: '123456',
+          confirm_password: '123456',
+          email: 'example@example.com',
+          full_name: 'mixeduser',
+          roles: ['kibana_dashboard_only_user', 'superuser'],
+        });
       });
 
       after(async () => {
diff --git a/x-pack/test/functional/apps/security/doc_level_security_roles.js b/x-pack/test/functional/apps/security/doc_level_security_roles.js
index 0595322ad2d21..a76475fbbbd8c 100644
--- a/x-pack/test/functional/apps/security/doc_level_security_roles.js
+++ b/x-pack/test/functional/apps/security/doc_level_security_roles.js
@@ -51,14 +51,12 @@ export default function ({ getService, getPageObjects }) {
     });
 
     it('should add new user userEAST ', async function () {
-      await PageObjects.security.clickElasticsearchUsers();
-      await PageObjects.security.addUser({
+      await PageObjects.security.createUser({
         username: 'userEast',
         password: 'changeme',
-        confirmPassword: 'changeme',
-        fullname: 'dls EAST',
+        confirm_password: 'changeme',
+        full_name: 'dls EAST',
         email: 'dlstest@elastic.com',
-        save: true,
         roles: ['kibana_admin', 'myroleEast'],
       });
       const users = keyBy(await PageObjects.security.getElasticsearchUsers(), 'username');
diff --git a/x-pack/test/functional/apps/security/field_level_security.js b/x-pack/test/functional/apps/security/field_level_security.js
index 3f3984dd05a94..a4e2680c394ee 100644
--- a/x-pack/test/functional/apps/security/field_level_security.js
+++ b/x-pack/test/functional/apps/security/field_level_security.js
@@ -71,14 +71,12 @@ export default function ({ getService, getPageObjects }) {
     });
 
     it('should add new user customer1 ', async function () {
-      await PageObjects.security.clickElasticsearchUsers();
-      await PageObjects.security.addUser({
+      await PageObjects.security.createUser({
         username: 'customer1',
         password: 'changeme',
-        confirmPassword: 'changeme',
-        fullname: 'customer one',
+        confirm_password: 'changeme',
+        full_name: 'customer one',
         email: 'flstest@elastic.com',
-        save: true,
         roles: ['kibana_admin', 'a_viewssnrole'],
       });
       const users = keyBy(await PageObjects.security.getElasticsearchUsers(), 'username');
@@ -87,14 +85,12 @@ export default function ({ getService, getPageObjects }) {
     });
 
     it('should add new user customer2 ', async function () {
-      await PageObjects.security.clickElasticsearchUsers();
-      await PageObjects.security.addUser({
+      await PageObjects.security.createUser({
         username: 'customer2',
         password: 'changeme',
-        confirmPassword: 'changeme',
-        fullname: 'customer two',
+        confirm_password: 'changeme',
+        full_name: 'customer two',
         email: 'flstest@elastic.com',
-        save: true,
         roles: ['kibana_admin', 'a_view_no_ssn_role'],
       });
       const users = keyBy(await PageObjects.security.getElasticsearchUsers(), 'username');
diff --git a/x-pack/test/functional/apps/security/rbac_phase1.js b/x-pack/test/functional/apps/security/rbac_phase1.js
index 58c72eaa3072e..de4515c501187 100644
--- a/x-pack/test/functional/apps/security/rbac_phase1.js
+++ b/x-pack/test/functional/apps/security/rbac_phase1.js
@@ -58,13 +58,12 @@ export default function ({ getService, getPageObjects }) {
           ],
         },
       });
-      await PageObjects.security.clickElasticsearchUsers();
       log.debug('After Add user new: , userObj.userName');
-      await PageObjects.security.addUser({
+      await PageObjects.security.createUser({
         username: 'kibanauser',
         password: 'changeme',
-        confirmPassword: 'changeme',
-        fullname: 'kibanafirst kibanalast',
+        confirm_password: 'changeme',
+        full_name: 'kibanafirst kibanalast',
         email: 'kibanauser@myEmail.com',
         save: true,
         roles: ['rbac_all'],
@@ -76,13 +75,12 @@ export default function ({ getService, getPageObjects }) {
       expect(users.kibanauser.roles).to.eql(['rbac_all']);
       expect(users.kibanauser.fullname).to.eql('kibanafirst kibanalast');
       expect(users.kibanauser.reserved).to.be(false);
-      await PageObjects.security.clickElasticsearchUsers();
       log.debug('After Add user new: , userObj.userName');
-      await PageObjects.security.addUser({
+      await PageObjects.security.createUser({
         username: 'kibanareadonly',
         password: 'changeme',
-        confirmPassword: 'changeme',
-        fullname: 'kibanareadonlyFirst kibanareadonlyLast',
+        confirm_password: 'changeme',
+        full_name: 'kibanareadonlyFirst kibanareadonlyLast',
         email: 'kibanareadonly@myEmail.com',
         save: true,
         roles: ['rbac_read'],
diff --git a/x-pack/test/functional/apps/security/role_mappings.ts b/x-pack/test/functional/apps/security/role_mappings.ts
index 96f16aebd11b9..6f76367801536 100644
--- a/x-pack/test/functional/apps/security/role_mappings.ts
+++ b/x-pack/test/functional/apps/security/role_mappings.ts
@@ -9,7 +9,7 @@ import { parse } from 'url';
 import { FtrProviderContext } from '../../ftr_provider_context';
 
 export default ({ getPageObjects, getService }: FtrProviderContext) => {
-  const pageObjects = getPageObjects(['common', 'roleMappings']);
+  const pageObjects = getPageObjects(['common', 'security', 'roleMappings']);
   const security = getService('security');
   const testSubjects = getService('testSubjects');
   const browser = getService('browser');
@@ -32,8 +32,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
     it('allows a role mapping to be created', async () => {
       await testSubjects.click('createRoleMappingButton');
       await testSubjects.setValue('roleMappingFormNameInput', 'new_role_mapping');
-      await testSubjects.setValue('rolesDropdown', 'superuser');
-      await browser.pressKeys(browser.keys.ENTER);
+      await pageObjects.security.selectRole('superuser');
 
       await testSubjects.click('roleMappingsAddRuleButton');
 
diff --git a/x-pack/test/functional/apps/security/secure_roles_perm.js b/x-pack/test/functional/apps/security/secure_roles_perm.js
index c547657bf880a..830d8384f1e3d 100644
--- a/x-pack/test/functional/apps/security/secure_roles_perm.js
+++ b/x-pack/test/functional/apps/security/secure_roles_perm.js
@@ -51,15 +51,13 @@ export default function ({ getService, getPageObjects }) {
     });
 
     it('should add new user', async function () {
-      await PageObjects.security.clickElasticsearchUsers();
       log.debug('After Add user new: , userObj.userName');
-      await PageObjects.security.addUser({
+      await PageObjects.security.createUser({
         username: 'Rashmi',
         password: 'changeme',
-        confirmPassword: 'changeme',
-        fullname: 'RashmiFirst RashmiLast',
+        confirm_password: 'changeme',
+        full_name: 'RashmiFirst RashmiLast',
         email: 'rashmi@myEmail.com',
-        save: true,
         roles: ['logstash_reader', 'kibana_admin'],
       });
       log.debug('After Add user: , userObj.userName');
diff --git a/x-pack/test/functional/apps/security/user_email.js b/x-pack/test/functional/apps/security/user_email.js
index a2a2b705172d7..c05220b6a59f3 100644
--- a/x-pack/test/functional/apps/security/user_email.js
+++ b/x-pack/test/functional/apps/security/user_email.js
@@ -19,13 +19,12 @@ export default function ({ getService, getPageObjects }) {
     });
 
     it('should add new user', async function () {
-      await PageObjects.security.addUser({
+      await PageObjects.security.createUser({
         username: 'newuser',
         password: 'changeme',
-        confirmPassword: 'changeme',
-        fullname: 'newuserFirst newuserLast',
+        confirm_password: 'changeme',
+        full_name: 'newuserFirst newuserLast',
         email: 'newuser@myEmail.com',
-        save: true,
         roles: ['kibana_admin', 'superuser'],
       });
       const users = keyBy(await PageObjects.security.getElasticsearchUsers(), 'username');
diff --git a/x-pack/test/functional/apps/security/users.js b/x-pack/test/functional/apps/security/users.js
index 4fd4384a93c59..7f2b0cfd96ca2 100644
--- a/x-pack/test/functional/apps/security/users.js
+++ b/x-pack/test/functional/apps/security/users.js
@@ -41,13 +41,12 @@ export default function ({ getService, getPageObjects }) {
     });
 
     it('should add new user', async function () {
-      await PageObjects.security.addUser({
+      await PageObjects.security.createUser({
         username: 'Lee',
         password: 'LeePwd',
-        confirmPassword: 'LeePwd',
-        fullname: 'LeeFirst LeeLast',
+        confirm_password: 'LeePwd',
+        full_name: 'LeeFirst LeeLast',
         email: 'lee@myEmail.com',
-        save: true,
         roles: ['kibana_admin'],
       });
       const users = keyBy(await PageObjects.security.getElasticsearchUsers(), 'username');
@@ -59,11 +58,10 @@ export default function ({ getService, getPageObjects }) {
     });
 
     it('should add new user with optional fields left empty', async function () {
-      await PageObjects.security.addUser({
+      await PageObjects.security.createUser({
         username: 'OptionalUser',
         password: 'OptionalUserPwd',
-        confirmPassword: 'OptionalUserPwd',
-        save: true,
+        confirm_password: 'OptionalUserPwd',
         roles: [],
       });
       const users = keyBy(await PageObjects.security.getElasticsearchUsers(), 'username');
diff --git a/x-pack/test/functional/page_objects/security_page.ts b/x-pack/test/functional/page_objects/security_page.ts
index cad5e29528e9c..868d8115e7f0f 100644
--- a/x-pack/test/functional/page_objects/security_page.ts
+++ b/x-pack/test/functional/page_objects/security_page.ts
@@ -7,6 +7,7 @@
 import { adminTestUser } from '@kbn/test';
 import { FtrProviderContext } from '../ftr_provider_context';
 import { AuthenticatedUser, Role } from '../../../plugins/security/common/model';
+import type { UserFormValues } from '../../../plugins/security/public/management/users/edit_user/user_form';
 
 export function SecurityPageProvider({ getService, getPageObjects }: FtrProviderContext) {
   const browser = getService('browser');
@@ -275,7 +276,7 @@ export function SecurityPageProvider({ getService, getPageObjects }: FtrProvider
     }
 
     async clickCancelEditUser() {
-      await testSubjects.click('userFormCancelButton');
+      await find.clickByButtonText('Cancel');
     }
 
     async clickCancelEditRole() {
@@ -283,7 +284,7 @@ export function SecurityPageProvider({ getService, getPageObjects }: FtrProvider
     }
 
     async clickSaveEditUser() {
-      await testSubjects.click('userFormSaveButton');
+      await find.clickByButtonText('Update user');
       await PageObjects.header.waitUntilLoadingHasFinished();
     }
 
@@ -380,53 +381,58 @@ export function SecurityPageProvider({ getService, getPageObjects }: FtrProvider
       return roles;
     }
 
+    /**
+     * @deprecated Use `PageObjects.security.clickCreateNewUser` instead
+     */
     async clickNewUser() {
       return await testSubjects.click('createUserButton');
     }
 
+    /**
+     * @deprecated Use `PageObjects.security.clickCreateNewUser` instead
+     */
     async clickNewRole() {
       return await testSubjects.click('createRoleButton');
     }
 
-    async addUser(userObj: {
-      username: string;
-      password: string;
-      confirmPassword: string;
-      email: string;
-      fullname: string;
-      roles: string[];
-      save?: boolean;
-    }) {
-      const self = this;
-      await this.clickNewUser();
-      log.debug('username = ' + userObj.username);
-      await testSubjects.setValue('userFormUserNameInput', userObj.username);
-      await testSubjects.setValue('passwordInput', userObj.password);
-      await testSubjects.setValue('passwordConfirmationInput', userObj.confirmPassword);
-      if (userObj.fullname) {
-        await testSubjects.setValue('userFormFullNameInput', userObj.fullname);
+    async fillUserForm(user: UserFormValues) {
+      if (user.username) {
+        await find.setValue('[name=username]', user.username);
+      }
+      if (user.password) {
+        await find.setValue('[name=password]', user.password);
+      }
+      if (user.confirm_password) {
+        await find.setValue('[name=confirm_password]', user.confirm_password);
       }
-      if (userObj.email) {
-        await testSubjects.setValue('userFormEmailInput', userObj.email);
+      if (user.full_name) {
+        await find.setValue('[name=full_name]', user.full_name);
+      }
+      if (user.email) {
+        await find.setValue('[name=email]', user.email);
       }
 
-      log.debug('Add roles: ', userObj.roles);
-      const rolesToAdd = userObj.roles || [];
+      const rolesToAdd = user.roles || [];
       for (let i = 0; i < rolesToAdd.length; i++) {
-        await self.selectRole(rolesToAdd[i]);
-      }
-      log.debug('After Add role: , userObj.roleName');
-      if (userObj.save === true) {
-        await testSubjects.click('userFormSaveButton');
-      } else {
-        await testSubjects.click('userFormCancelButton');
+        await this.selectRole(rolesToAdd[i]);
       }
     }
 
+    async submitCreateUserForm() {
+      await find.clickByButtonText('Create user');
+    }
+
+    async createUser(user: UserFormValues) {
+      await this.clickElasticsearchUsers();
+      await this.clickCreateNewUser();
+      await this.fillUserForm(user);
+      await this.submitCreateUserForm();
+    }
+
     async addRole(roleName: string, roleObj: Role) {
       const self = this;
 
-      await this.clickNewRole();
+      await this.clickCreateNewRole();
 
       // We have to use non-test-subject selectors because this markup is generated by ui-select.
       log.debug('roleObj.indices[0].names = ' + roleObj.elasticsearch.indices[0].names);
@@ -498,37 +504,23 @@ export function SecurityPageProvider({ getService, getPageObjects }: FtrProvider
       const dropdown = await testSubjects.find('rolesDropdown');
       const input = await dropdown.findByCssSelector('input');
       await input.type(role);
-      await testSubjects.click(`roleOption-${role}`);
+      await find.clickByCssSelector(`[role=option][title="${role}"]`);
       await testSubjects.click('comboBoxToggleListButton');
-      await testSubjects.find(`roleOption-${role}`);
     }
 
-    deleteUser(username: string) {
-      let alertText: string;
+    async deleteUser(username: string) {
       log.debug('Delete user ' + username);
-      return find
-        .clickByDisplayedLinkText(username)
-        .then(() => {
-          return PageObjects.header.awaitGlobalLoadingIndicatorHidden();
-        })
-        .then(() => {
-          log.debug('Find delete button and click');
-          return testSubjects.click('userFormDeleteButton');
-        })
-        .then(() => {
-          return PageObjects.common.sleep(2000);
-        })
-        .then(() => {
-          return testSubjects.getVisibleText('confirmModalBodyText');
-        })
-        .then((alert) => {
-          alertText = alert;
-          log.debug('Delete user alert text = ' + alertText);
-          return testSubjects.click('confirmModalConfirmButton');
-        })
-        .then(() => {
-          return alertText;
-        });
+      await find.clickByDisplayedLinkText(username);
+      await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
+
+      log.debug('Find delete button and click');
+      await find.clickByButtonText('Delete user');
+      await PageObjects.common.sleep(2000);
+
+      const confirmText = await testSubjects.getVisibleText('confirmModalBodyText');
+      log.debug('Delete user alert text = ' + confirmText);
+      await testSubjects.click('confirmModalConfirmButton');
+      return confirmText;
     }
   }
   return new SecurityPage();
diff --git a/yarn.lock b/yarn.lock
index 828a3b630a838..e7870415b0dda 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5137,10 +5137,10 @@
   dependencies:
     "@types/sizzle" "*"
 
-"@types/js-cookie@2.2.5":
-  version "2.2.5"
-  resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.5.tgz#38dfaacae8623b37cc0b0d27398e574e3fc28b1e"
-  integrity sha512-cpmwBRcHJmmZx0OGU7aPVwGWGbs4iKwVYchk9iuMtxNCA2zorwdaTz4GkLgs2WGxiRZRFKnV1k6tRUHX7tBMxg==
+"@types/js-cookie@2.2.6":
+  version "2.2.6"
+  resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.6.tgz#f1a1cb35aff47bc5cfb05cb0c441ca91e914c26f"
+  integrity sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw==
 
 "@types/js-search@^1.4.0":
   version "1.4.0"
@@ -6374,10 +6374,10 @@
   dependencies:
     tslib "^1.9.3"
 
-"@xobotyi/scrollbar-width@1.9.4":
-  version "1.9.4"
-  resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.4.tgz#a7dce20b7465bcad29cd6bbb557695e4ea7863cb"
-  integrity sha512-o12FCQt/X5n3pgKEWGpt0f/7Eg4mfv3uRwPUrctiOT8ZuxbH3cNLGWfH/8y6KxVJg4L2885ucuXQ6XECZzUiJA==
+"@xobotyi/scrollbar-width@1.9.5":
+  version "1.9.5"
+  resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d"
+  integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==
 
 "@xtuc/ieee754@^1.2.0":
   version "1.2.0"
@@ -13368,7 +13368,7 @@ fancy-log@^1.3.2:
     color-support "^1.1.3"
     time-stamp "^1.0.0"
 
-fast-deep-equal@^3.1.1, fast-deep-equal@~3.1.3:
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3, fast-deep-equal@~3.1.3:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
   integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
@@ -23597,24 +23597,30 @@ react-transition-group@^4.3.0:
     loose-envify "^1.4.0"
     prop-types "^15.6.2"
 
-react-use@^13.27.0:
-  version "13.27.0"
-  resolved "https://registry.yarnpkg.com/react-use/-/react-use-13.27.0.tgz#53a619dc9213e2cbe65d6262e8b0e76641ade4aa"
-  integrity sha512-2lyTyqJWyvnaP/woVtDcFS4B5pUYz0FQWI9pVHk/6TBWom2x3/ziJthkEn/LbCA9Twv39xSQU7Dn0zdIWfsNTQ==
+react-universal-interface@^0.6.2:
+  version "0.6.2"
+  resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b"
+  integrity sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==
+
+react-use@^15.3.4:
+  version "15.3.4"
+  resolved "https://registry.yarnpkg.com/react-use/-/react-use-15.3.4.tgz#f853d310bd71f75b38900a8caa3db93f6dc6e872"
+  integrity sha512-cHq1dELW6122oi1+xX7lwNyE/ugZs5L902BuO8eFJCfn2api1KeuPVG1M/GJouVARoUf54S2dYFMKo5nQXdTag==
   dependencies:
-    "@types/js-cookie" "2.2.5"
-    "@xobotyi/scrollbar-width" "1.9.4"
+    "@types/js-cookie" "2.2.6"
+    "@xobotyi/scrollbar-width" "1.9.5"
     copy-to-clipboard "^3.2.0"
-    fast-deep-equal "^3.1.1"
+    fast-deep-equal "^3.1.3"
     fast-shallow-equal "^1.0.0"
     js-cookie "^2.2.1"
     nano-css "^5.2.1"
+    react-universal-interface "^0.6.2"
     resize-observer-polyfill "^1.5.1"
     screenfull "^5.0.0"
     set-harmonic-interval "^1.0.1"
     throttle-debounce "^2.1.0"
     ts-easing "^0.2.0"
-    tslib "^1.10.0"
+    tslib "^2.0.0"
 
 react-virtualized-auto-sizer@^1.0.2:
   version "1.0.2"

From e8f338e78c47969801d6de9f7822f7020eecc34c Mon Sep 17 00:00:00 2001
From: Shahzad <shahzad.muhammad@elastic.co>
Date: Mon, 25 Jan 2021 14:56:55 +0100
Subject: [PATCH 16/46] [Uptime] Added View performance breakdown button
 (#88658)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
 .../components/common/step_detail_link.tsx    | 17 ++++----
 .../monitor/synthetics/executed_step.tsx      | 42 ++++++++-----------
 .../monitor/synthetics/translations.ts        | 14 +++++++
 3 files changed, 42 insertions(+), 31 deletions(-)
 create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/translations.ts

diff --git a/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx b/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx
index a8e4c90f2d29a..886496a7f6e2f 100644
--- a/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx
+++ b/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx
@@ -5,8 +5,7 @@
  */
 
 import React, { FC } from 'react';
-import { EuiLink } from '@elastic/eui';
-import { Link } from 'react-router-dom';
+import { ReactRouterEuiButton } from './react_router_helpers';
 
 interface StepDetailLinkProps {
   /**
@@ -23,10 +22,14 @@ export const StepDetailLink: FC<StepDetailLinkProps> = ({ children, checkGroupId
   const to = `/journey/${checkGroupId}/step/${stepIndex}`;
 
   return (
-    <EuiLink>
-      <Link data-test-subj={`step-detail-link`} to={to}>
-        {children}
-      </Link>
-    </EuiLink>
+    <ReactRouterEuiButton
+      data-test-subj={`step-detail-link`}
+      to={to}
+      size="s"
+      fill
+      fullWidth={false}
+    >
+      {children}
+    </ReactRouterEuiButton>
   );
 };
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx
index 01a599f8e8a60..934427643757d 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx
@@ -13,6 +13,7 @@ import { StepScreenshotDisplay } from './step_screenshot_display';
 import { StatusBadge } from './status_badge';
 import { Ping } from '../../../../common/runtime_types';
 import { StepDetailLink } from '../../common/step_detail_link';
+import { VIEW_PERFORMANCE } from './translations';
 
 const CODE_BLOCK_OVERFLOW_HEIGHT = 360;
 
@@ -26,24 +27,9 @@ export const ExecutedStep: FC<ExecutedStepProps> = ({ step, index, checkGroup })
   return (
     <>
       <div style={{ padding: '8px' }}>
-        <div>
-          {step.synthetics?.step?.index && checkGroup ? (
-            <StepDetailLink checkGroupId={checkGroup} stepIndex={step.synthetics.step.index!}>
-              <EuiText>
-                <strong>
-                  <FormattedMessage
-                    id="xpack.uptime.synthetics.executedStep.stepName"
-                    defaultMessage="{stepNumber}. {stepName}"
-                    values={{
-                      stepNumber: index + 1,
-                      stepName: step.synthetics?.step?.name,
-                    }}
-                  />
-                </strong>
-              </EuiText>
-            </StepDetailLink>
-          ) : (
-            <EuiText>
+        <EuiFlexGroup>
+          <EuiFlexItem grow={false}>
+            <EuiText grow={false}>
               <strong>
                 <FormattedMessage
                   id="xpack.uptime.synthetics.executedStep.stepName"
@@ -55,12 +41,12 @@ export const ExecutedStep: FC<ExecutedStepProps> = ({ step, index, checkGroup })
                 />
               </strong>
             </EuiText>
-          )}
-        </div>
-        <EuiSpacer size="s" />
-        <div>
-          <StatusBadge status={step.synthetics?.payload?.status} />
-        </div>
+            <EuiSpacer size="s" />
+            <div>
+              <StatusBadge status={step.synthetics?.payload?.status} />
+            </div>
+          </EuiFlexItem>
+        </EuiFlexGroup>
         <EuiSpacer size="s" />
         <div>
           <EuiFlexGroup>
@@ -73,6 +59,14 @@ export const ExecutedStep: FC<ExecutedStepProps> = ({ step, index, checkGroup })
               />
             </EuiFlexItem>
             <EuiFlexItem>
+              {step.synthetics?.step?.index && (
+                <span>
+                  <StepDetailLink checkGroupId={checkGroup} stepIndex={step.synthetics.step.index}>
+                    {VIEW_PERFORMANCE}
+                  </StepDetailLink>
+                  <EuiSpacer size="s" />
+                </span>
+              )}
               <CodeBlockAccordion
                 id={step.synthetics?.step?.name + String(index)}
                 buttonContent={i18n.translate(
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/translations.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/translations.ts
new file mode 100644
index 0000000000000..85a9db1527d57
--- /dev/null
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/translations.ts
@@ -0,0 +1,14 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const VIEW_PERFORMANCE = i18n.translate(
+  'xpack.uptime.pingList.synthetics.performanceBreakDown',
+  {
+    defaultMessage: 'View performance breakdown',
+  }
+);

From 77851e838244eda55189b5e2bc3a739d53159847 Mon Sep 17 00:00:00 2001
From: Sonja Krause-Harder <sonja.krause-harder@elastic.co>
Date: Mon, 25 Jan 2021 15:01:29 +0100
Subject: [PATCH 17/46] Ignore missing asset errors on remove. (#89115)

---
 x-pack/plugins/fleet/server/services/epm/packages/remove.ts | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/x-pack/plugins/fleet/server/services/epm/packages/remove.ts b/x-pack/plugins/fleet/server/services/epm/packages/remove.ts
index 331b6bfa882da..94e81e296b5a9 100644
--- a/x-pack/plugins/fleet/server/services/epm/packages/remove.ts
+++ b/x-pack/plugins/fleet/server/services/epm/packages/remove.ts
@@ -115,7 +115,10 @@ async function deleteAssets(
   try {
     await Promise.all(deletePromises);
   } catch (err) {
-    logger.error(err);
+    // in the rollback case, partial installs are likely, so missing assets are not an error
+    if (!savedObjectsClient.errors.isNotFoundError(err)) {
+      logger.error(err);
+    }
   }
 }
 

From 93c46f5dfc88b74cd3b2790588aa457ac55bfedf Mon Sep 17 00:00:00 2001
From: Pierre Gayvallet <pierre.gayvallet@elastic.co>
Date: Mon, 25 Jan 2021 15:29:10 +0100
Subject: [PATCH 18/46] Remove tag name validation (#88800)

* Remove tag name validation

* remove i18n key

* add FTR test on searching for tag with special chars in name
---
 docs/management/managing-tags.asciidoc        |  3 +-
 .../common/validation.test.ts                 |  6 +-
 .../common/validation.ts                      |  6 --
 .../translations/translations/ja-JP.json      |  1 -
 .../translations/translations/zh-CN.json      |  1 -
 .../global_search/search_syntax/data.json     | 56 +++++++++++++++++++
 .../global_search/global_search_bar.ts        | 13 ++++-
 .../tagging_api/apis/create.ts                |  4 +-
 .../tagging_api/apis/update.ts                |  4 +-
 .../functional/tests/create.ts                |  4 +-
 .../functional/tests/edit.ts                  |  4 +-
 11 files changed, 79 insertions(+), 23 deletions(-)

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/x-pack/plugins/saved_objects_tagging/common/validation.test.ts b/x-pack/plugins/saved_objects_tagging/common/validation.test.ts
index 232387e964cbf..a601a96a49e75 100644
--- a/x-pack/plugins/saved_objects_tagging/common/validation.test.ts
+++ b/x-pack/plugins/saved_objects_tagging/common/validation.test.ts
@@ -20,10 +20,8 @@ describe('Tag attributes validation', () => {
       );
     });
 
-    it('returns an error message if the name contains invalid characters', () => {
-      expect(validateTagName('t^ag+name&')).toMatchInlineSnapshot(
-        `"Tag name can only include a-z, 0-9, _, -,:."`
-      );
+    it('does not return an error message if the name contains special characters', () => {
+      expect(validateTagName('t^ag+name&')).toBeUndefined();
     });
   });
 
diff --git a/x-pack/plugins/saved_objects_tagging/common/validation.ts b/x-pack/plugins/saved_objects_tagging/common/validation.ts
index 12149d7bdbe79..5cb9e068516fe 100644
--- a/x-pack/plugins/saved_objects_tagging/common/validation.ts
+++ b/x-pack/plugins/saved_objects_tagging/common/validation.ts
@@ -12,7 +12,6 @@ export const tagNameMaxLength = 50;
 export const tagDescriptionMaxLength = 100;
 
 const hexColorRegexp = /^#[0-9A-F]{6}$/i;
-const nameValidCharsRegexp = /^[0-9A-Z:\-_\s]+$/i;
 
 export interface TagValidation {
   valid: boolean;
@@ -49,11 +48,6 @@ export const validateTagName = (name: string): string | undefined => {
       },
     });
   }
-  if (!nameValidCharsRegexp.test(name)) {
-    return i18n.translate('xpack.savedObjectsTagging.validation.name.errorInvalidCharacters', {
-      defaultMessage: 'Tag name can only include a-z, 0-9, _, -,:.',
-    });
-  }
 };
 
 export const validateTagDescription = (description: string): string | undefined => {
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 3a579adcf88c2..ef2149c4931fa 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -16633,7 +16633,6 @@
     "xpack.savedObjectsTagging.uiApi.table.columnTagsName": "タグ",
     "xpack.savedObjectsTagging.validation.color.errorInvalid": "タグ色は有効な 16 進数値色でなければなりません",
     "xpack.savedObjectsTagging.validation.description.errorTooLong": "タグ説明は {length} 文字以下で入力してください",
-    "xpack.savedObjectsTagging.validation.name.errorInvalidCharacters": "タグ名には、a-z、0-9、-、: のみを使用できます。",
     "xpack.savedObjectsTagging.validation.name.errorTooLong": "タグ名は {length} 文字以下で入力してください",
     "xpack.savedObjectsTagging.validation.name.errorTooShort": "タグ名は {length} 文字以上で入力してください",
     "xpack.searchProfiler.advanceTimeDescription": "イテレーターを次のドキュメントに進めるためにかかった時間。",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 9099fa0267ba9..08d064ce8a05c 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -16675,7 +16675,6 @@
     "xpack.savedObjectsTagging.uiApi.table.columnTagsName": "标签",
     "xpack.savedObjectsTagging.validation.color.errorInvalid": "标签颜色必须为有效的十六进制颜色",
     "xpack.savedObjectsTagging.validation.description.errorTooLong": "标签描述不能超过 {length} 个字符。",
-    "xpack.savedObjectsTagging.validation.name.errorInvalidCharacters": "标签名称只能包含 a-z、0-9、_、-、:。",
     "xpack.savedObjectsTagging.validation.name.errorTooLong": "标签名称不能超过 {length} 个字符",
     "xpack.savedObjectsTagging.validation.name.errorTooShort": "标签名称必须至少有 {length} 个字符",
     "xpack.searchProfiler.advanceTimeDescription": "将迭代器推进至下一文档所用时间。",
diff --git a/x-pack/test/plugin_functional/es_archives/global_search/search_syntax/data.json b/x-pack/test/plugin_functional/es_archives/global_search/search_syntax/data.json
index 69220756639dc..8379290f5d9bb 100644
--- a/x-pack/test/plugin_functional/es_archives/global_search/search_syntax/data.json
+++ b/x-pack/test/plugin_functional/es_archives/global_search/search_syntax/data.json
@@ -88,6 +88,24 @@
   }
 }
 
+{
+  "type": "doc",
+  "value": {
+    "id": "tag:tag-special-chars",
+    "index": ".kibana",
+    "source": {
+      "tag": {
+        "name": "my%tag",
+        "description": "Special chars",
+        "color": "#AA0077"
+      },
+      "type": "tag",
+      "updated_at": "2017-09-21T18:49:16.270Z"
+    },
+    "type": "doc"
+  }
+}
+
 {
   "type": "doc",
   "value": {
@@ -356,3 +374,41 @@
     }
   }
 }
+
+{
+  "type": "doc",
+  "value": {
+    "id": "dashboard:ref-to-tag-special-chars",
+    "index": ".kibana",
+    "source": {
+      "dashboard": {
+        "title": "dashboard 4 (tag-special-chars)",
+        "description": "",
+        "hits": 0,
+        "kibanaSavedObjectMeta": {
+          "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"animal\",\"value\":\"dog\",\"params\":{\"query\":\"dog\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"animal\":{\"query\":\"dog\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"highlightAll\":true,\"version\":true}"
+        },
+        "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}",
+        "panelsJSON": "[]",
+        "timeFrom": "Mon Apr 09 2018 17:56:08 GMT-0400",
+        "timeRestore": true,
+        "timeTo": "Wed Apr 11 2018 17:56:08 GMT-0400",
+        "version": 1
+      },
+      "migrationVersion": {
+        "dashboard": "7.3.0"
+      },
+      "references": [
+        {
+          "id": "tag-special-chars",
+          "name": "tag-special-ref",
+          "type": "tag"
+        }
+      ],
+      "type": "dashboard",
+      "updated_at": "2018-04-11T21:57:52.253Z"
+    }
+  }
+}
+
+
diff --git a/x-pack/test/plugin_functional/test_suites/global_search/global_search_bar.ts b/x-pack/test/plugin_functional/test_suites/global_search/global_search_bar.ts
index f0c70ee8f718d..6f84440fc27e6 100644
--- a/x-pack/test/plugin_functional/test_suites/global_search/global_search_bar.ts
+++ b/x-pack/test/plugin_functional/test_suites/global_search/global_search_bar.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
 import { FtrProviderContext } from '../../ftr_provider_context';
 
 export default function ({ getPageObjects, getService }: FtrProviderContext) {
-  describe('GlobalSearchBar', function () {
+  describe('TOTO GlobalSearchBar', function () {
     const { common, navigationalSearch } = getPageObjects(['common', 'navigationalSearch']);
     const esArchiver = getService('esArchiver');
     const browser = getService('browser');
@@ -61,6 +61,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
           'dashboard 1 (tag-2)',
           'dashboard 2 (tag-3)',
           'dashboard 3 (tag-1 and tag-3)',
+          'dashboard 4 (tag-special-chars)',
         ]);
       });
       it('shows a suggestion when searching for a term matching a tag name', async () => {
@@ -94,6 +95,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
           'dashboard 1 (tag-2)',
           'dashboard 2 (tag-3)',
           'dashboard 3 (tag-1 and tag-3)',
+          'dashboard 4 (tag-special-chars)',
         ]);
       });
 
@@ -111,6 +113,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
           'dashboard 1 (tag-2)',
           'dashboard 2 (tag-3)',
           'dashboard 3 (tag-1 and tag-3)',
+          'dashboard 4 (tag-special-chars)',
         ]);
       });
 
@@ -181,6 +184,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
         expect(results.map((result) => result.label)).to.eql(['My awesome vis (tag-4)']);
       });
 
+      it('allows to filter by tags containing special characters', async () => {
+        await navigationalSearch.searchFor('tag:"my%tag"');
+
+        const results = await navigationalSearch.getDisplayedResults();
+
+        expect(results.map((result) => result.label)).to.eql(['dashboard 4 (tag-special-chars)']);
+      });
+
       it('returns no results when searching for an unknown tag', async () => {
         await navigationalSearch.searchFor('tag:unknown');
 
diff --git a/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/create.ts b/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/create.ts
index bd7fa7538703c..30008e635b628 100644
--- a/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/create.ts
+++ b/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/create.ts
@@ -60,7 +60,7 @@ export default function ({ getService }: FtrProviderContext) {
       await supertest
         .post(`/api/saved_objects_tagging/tags/create`)
         .send({
-          name: 'Inv%li& t@g n*me',
+          name: 'a',
           description: 'some desc',
           color: 'this is not a valid color',
         })
@@ -74,7 +74,7 @@ export default function ({ getService }: FtrProviderContext) {
               valid: false,
               warnings: [],
               errors: {
-                name: 'Tag name can only include a-z, 0-9, _, -,:.',
+                name: 'Tag name must be at least 2 characters',
                 color: 'Tag color must be a valid hex color',
               },
             },
diff --git a/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/update.ts b/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/update.ts
index 7b4298607c666..ddf39ccf90b34 100644
--- a/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/update.ts
+++ b/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/update.ts
@@ -78,7 +78,7 @@ export default function ({ getService }: FtrProviderContext) {
       await supertest
         .post(`/api/saved_objects_tagging/tags/tag-1`)
         .send({
-          name: 'Inv%li& t@g n*me',
+          name: 'a',
           description: 'some desc',
           color: 'this is not a valid color',
         })
@@ -92,7 +92,7 @@ export default function ({ getService }: FtrProviderContext) {
               valid: false,
               warnings: [],
               errors: {
-                name: 'Tag name can only include a-z, 0-9, _, -,:.',
+                name: 'Tag name must be at least 2 characters',
                 color: 'Tag color must be a valid hex color',
               },
             },
diff --git a/x-pack/test/saved_object_tagging/functional/tests/create.ts b/x-pack/test/saved_object_tagging/functional/tests/create.ts
index b62e9a70b43e8..2f2db856c0657 100644
--- a/x-pack/test/saved_object_tagging/functional/tests/create.ts
+++ b/x-pack/test/saved_object_tagging/functional/tests/create.ts
@@ -54,7 +54,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
       await tagModal.openCreate();
       await tagModal.fillForm(
         {
-          name: 'invalid&$%name',
+          name: 'a',
           description: 'The name will fails validation',
           color: '#FF00CC',
         },
@@ -73,7 +73,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
       await tagModal.openCreate();
       await tagModal.fillForm(
         {
-          name: 'invalid&$%name',
+          name: 'a',
           description: 'The name will fails validation',
           color: '#FF00CC',
         },
diff --git a/x-pack/test/saved_object_tagging/functional/tests/edit.ts b/x-pack/test/saved_object_tagging/functional/tests/edit.ts
index 1883d3f23dc9d..1de101433179d 100644
--- a/x-pack/test/saved_object_tagging/functional/tests/edit.ts
+++ b/x-pack/test/saved_object_tagging/functional/tests/edit.ts
@@ -71,7 +71,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
       await tagModal.openEdit('tag-2');
       await tagModal.fillForm(
         {
-          name: 'invalid&$%name',
+          name: 'a',
         },
         { submit: true }
       );
@@ -88,7 +88,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
       await tagModal.openEdit('tag-2');
       await tagModal.fillForm(
         {
-          name: 'invalid&$%name',
+          name: 'a',
           description: 'edited description',
           color: '#FF00CC',
         },

From f4c43002017a93ad77c4b8c2abe4bc5ee6d1cded Mon Sep 17 00:00:00 2001
From: Matthias Wilhelm <matthias.wilhelm@elastic.co>
Date: Mon, 25 Jan 2021 15:35:30 +0100
Subject: [PATCH 19/46] [Discover] Deangularize $element and $timeout (#88214)

* Remove $element for document.getElementById
* Remove $timeout
---
 .../public/application/angular/discover.js    | 22 +++++++++----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js
index 75fed9f809aa6..946baa7f4ecb1 100644
--- a/src/plugins/discover/public/application/angular/discover.js
+++ b/src/plugins/discover/public/application/angular/discover.js
@@ -175,7 +175,7 @@ app.directive('discoverApp', function () {
   };
 });
 
-function discoverController($element, $route, $scope, $timeout, Promise) {
+function discoverController($route, $scope, Promise) {
   const { isDefault: isDefaultType } = indexPatternsUtils;
   const subscriptions = new Subscription();
   const refetch$ = new Subject();
@@ -725,20 +725,20 @@ function discoverController($element, $route, $scope, $timeout, Promise) {
     $route.reload();
   };
 
-  $scope.onSkipBottomButtonClick = function () {
+  $scope.onSkipBottomButtonClick = async () => {
     // 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 () {

From eed938396949f9ca2127c2859eded50781b3f14b Mon Sep 17 00:00:00 2001
From: Rudolf Meijering <skaapgif@gmail.com>
Date: Mon, 25 Jan 2021 15:36:48 +0100
Subject: [PATCH 20/46] Migrations v2 docs (#88820)

* Migrations v2 docs

* Not all kibana distributions automatically restarted a killed process

* Mention that we add a write block to the outdated index

* Formating: collapse three notes into a single note with three bullet points

* Update docs/setup/upgrade/upgrade-standard.asciidoc

Co-authored-by: Josh Dover <1813008+joshdover@users.noreply.github.com>

* Add table of outdated / upgraded indices per version of Kibana

* Review feedback: separate section for multi-instance upgrade migrations

* Review feedback: link to saved objects management

* Review feedback: stronger wording for not deleting any indices

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Josh Dover <1813008+joshdover@users.noreply.github.com>
---
 .../setup/upgrade/upgrade-migrations.asciidoc | 79 +++++++++----------
 docs/setup/upgrade/upgrade-standard.asciidoc  | 14 ++--
 2 files changed, 46 insertions(+), 47 deletions(-)

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 <<managing-saved-objects,saved objects>> 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
   <<kibana-plugins,plugins>> 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.

From 164d6b2d9934927373b1ad01246613674c05accf Mon Sep 17 00:00:00 2001
From: Marco Vettorello <vettorello.marco@gmail.com>
Date: Mon, 25 Jan 2021 15:51:11 +0100
Subject: [PATCH 21/46] [discover] add valye formatter on y axis and display
 only integer values(#88941)

---
 .../public/application/angular/directives/histogram.tsx     | 6 ++++++
 .../public/application/angular/helpers/point_series.ts      | 3 ++-
 2 files changed, 8 insertions(+), 1 deletion(-)

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<DiscoverHistogramProps, Discove
       type: TooltipType.VerticalCursor,
     };
 
+    const xAxisFormatter = getServices().data.fieldFormats.deserialize(
+      this.props.chartData.yAxisFormat
+    );
+
     return (
       <Chart size="100%">
         <Settings
@@ -169,6 +173,8 @@ export class DiscoverHistogram extends Component<DiscoverHistogramProps, Discove
           position={Position.Left}
           ticks={5}
           title={chartData.yAxisLabel}
+          integersOnly
+          tickFormat={(value) => xAxisFormatter.convert(value)}
         />
         <Axis
           id="discover-histogram-bottom-axis"
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,

From e7e42a47117da9ad2415f0108736842dd3798a8c Mon Sep 17 00:00:00 2001
From: Shahzad <shahzad.muhammad@elastic.co>
Date: Mon, 25 Jan 2021 16:07:53 +0100
Subject: [PATCH 22/46] [Uptime] Display networks requests total (#88672)

---
 .../common/runtime_types/network_events.ts    |  1 +
 .../waterfall/waterfall_chart_container.tsx   |  5 +-
 .../waterfall/waterfall_chart_wrapper.tsx     |  5 +-
 .../network_requests_total.test.tsx           | 28 ++++++++++
 .../components/network_requests_total.tsx     | 44 ++++++++++++++++
 .../synthetics/waterfall/components/styles.ts |  7 ++-
 .../waterfall/components/waterfall_chart.tsx  | 16 +++++-
 .../waterfall/context/waterfall_chart.tsx     | 17 +++++-
 .../public/state/reducers/network_events.ts   | 12 ++++-
 .../lib/requests/get_network_events.test.ts   | 52 +++++++++++--------
 .../server/lib/requests/get_network_events.ts | 45 +++++++++-------
 .../network_events/get_network_events.ts      |  4 +-
 12 files changed, 183 insertions(+), 53 deletions(-)
 create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/network_requests_total.test.tsx
 create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/network_requests_total.tsx

diff --git a/x-pack/plugins/uptime/common/runtime_types/network_events.ts b/x-pack/plugins/uptime/common/runtime_types/network_events.ts
index 6104758f28fd8..fc666c803e2c3 100644
--- a/x-pack/plugins/uptime/common/runtime_types/network_events.ts
+++ b/x-pack/plugins/uptime/common/runtime_types/network_events.ts
@@ -41,6 +41,7 @@ export type NetworkEvent = t.TypeOf<typeof NetworkEventType>;
 
 export const SyntheticsNetworkEventsApiResponseType = t.type({
   events: t.array(NetworkEventType),
+  total: t.number,
 });
 
 export type SyntheticsNetworkEventsApiResponse = t.TypeOf<
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx
index 7657ca7f9c64a..680e3f257841e 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx
@@ -59,7 +59,10 @@ export const WaterfallChartContainer: React.FC<Props> = ({ checkGroup, stepIndex
         </EuiFlexGroup>
       )}
       {networkEvents && !networkEvents.loading && networkEvents.events.length > 0 && (
-        <WaterfallChartWrapper data={extractItems(networkEvents.events)} />
+        <WaterfallChartWrapper
+          data={extractItems(networkEvents.events)}
+          total={networkEvents.total}
+        />
       )}
     </>
   );
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx
index a84765c4ea154..7b904511b58ab 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx
@@ -48,10 +48,11 @@ export const renderLegendItem: RenderItem<LegendItem> = (item) => {
 };
 
 interface Props {
+  total: number;
   data: NetworkItems;
 }
 
-export const WaterfallChartWrapper: React.FC<Props> = ({ data }) => {
+export const WaterfallChartWrapper: React.FC<Props> = ({ data, total }) => {
   const [networkData] = useState<NetworkItems>(data);
 
   const { series, domain } = useMemo(() => {
@@ -66,6 +67,8 @@ export const WaterfallChartWrapper: React.FC<Props> = ({ data }) => {
 
   return (
     <WaterfallProvider
+      totalNetworkRequests={total}
+      fetchedNetworkRequests={networkData.length}
       data={series}
       sidebarItems={sidebarItems}
       legendItems={legendItems}
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/network_requests_total.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/network_requests_total.test.tsx
new file mode 100644
index 0000000000000..16b3841336f94
--- /dev/null
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/network_requests_total.test.tsx
@@ -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;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { NetworkRequestsTotal } from './network_requests_total';
+import { render } from '../../../../../lib/helper/rtl_helpers';
+
+describe('NetworkRequestsTotal', () => {
+  it('message in case total is greater than fetched', () => {
+    const { getByText, getByLabelText } = render(
+      <NetworkRequestsTotal fetchedNetworkRequests={1000} totalNetworkRequests={1100} />
+    );
+
+    expect(getByText('First 1000/1100 network requests')).toBeInTheDocument();
+    expect(getByLabelText('Info')).toBeInTheDocument();
+  });
+
+  it('message in case total is equal to fetched requests', () => {
+    const { getByText } = render(
+      <NetworkRequestsTotal fetchedNetworkRequests={500} totalNetworkRequests={500} />
+    );
+
+    expect(getByText('500 network requests')).toBeInTheDocument();
+  });
+});
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/network_requests_total.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/network_requests_total.tsx
new file mode 100644
index 0000000000000..c54e32238f81c
--- /dev/null
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/network_requests_total.tsx
@@ -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;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { i18n } from '@kbn/i18n';
+import { EuiIconTip } from '@elastic/eui';
+import { NetworkRequestsTotalStyle } from './styles';
+
+interface Props {
+  totalNetworkRequests: number;
+  fetchedNetworkRequests: number;
+}
+
+export const NetworkRequestsTotal = ({ totalNetworkRequests, fetchedNetworkRequests }: Props) => {
+  return (
+    <NetworkRequestsTotalStyle size="xs" color="subdued">
+      <strong>
+        {i18n.translate('xpack.uptime.synthetics.waterfall.requestsTotalMessage', {
+          defaultMessage: '{numNetworkRequests} network requests',
+          values: {
+            numNetworkRequests:
+              totalNetworkRequests > fetchedNetworkRequests
+                ? i18n.translate('xpack.uptime.synthetics.waterfall.requestsTotalMessage.first', {
+                    defaultMessage: 'First {count}',
+                    values: { count: `${fetchedNetworkRequests}/${totalNetworkRequests}` },
+                  })
+                : totalNetworkRequests,
+          },
+        })}
+      </strong>
+      {totalNetworkRequests > fetchedNetworkRequests && (
+        <EuiIconTip
+          type={'iInCircle'}
+          content={i18n.translate('xpack.uptime.synthetics.waterfall.requestsTotalMessage.info', {
+            defaultMessage: 'Waterfall view only shows up to 1000 requests',
+          })}
+        />
+      )}
+    </NetworkRequestsTotalStyle>
+  );
+};
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts
index 1f70354db154e..7bf5100730f5e 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { EuiPanel, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
 import { rgba } from 'polished';
 import { euiStyled } from '../../../../../../../observability/public';
 import { FIXED_AXIS_HEIGHT } from './constants';
@@ -103,3 +103,8 @@ export const WaterfallChartTooltip = euiStyled.div`
   color: ${(props) => props.theme.eui.euiColorLightestShade};
   padding: ${(props) => props.theme.eui.paddingSizes.s};
 `;
+
+export const NetworkRequestsTotalStyle = euiStyled(EuiText)`
+  line-height: ${FIXED_AXIS_HEIGHT}px;
+  margin-left: ${(props) => props.theme.eui.paddingSizes.m}
+`;
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx
index e937c3d35ec08..e449fed6decf4 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx
@@ -37,6 +37,7 @@ import { BAR_HEIGHT, CANVAS_MAX_ITEMS, MAIN_GROW_SIZE, SIDEBAR_GROW_SIZE } from
 import { Sidebar } from './sidebar';
 import { Legend } from './legend';
 import { useBarCharts } from './use_bar_charts';
+import { NetworkRequestsTotal } from './network_requests_total';
 
 const Tooltip = (tooltipInfo: TooltipInfo) => {
   const { data, renderTooltipItem } = useWaterfallContext();
@@ -84,7 +85,13 @@ export const WaterfallChart = ({
   maxHeight = '800px',
   fullHeight = false,
 }: WaterfallChartProps) => {
-  const { data, sidebarItems, legendItems } = useWaterfallContext();
+  const {
+    data,
+    sidebarItems,
+    legendItems,
+    totalNetworkRequests,
+    fetchedNetworkRequests,
+  } = useWaterfallContext();
 
   const [darkMode] = useUiSetting$<boolean>('theme:darkMode');
 
@@ -115,7 +122,12 @@ export const WaterfallChart = ({
           <EuiFlexGroup gutterSize="none" responsive={false}>
             {shouldRenderSidebar && (
               <EuiFlexItem grow={SIDEBAR_GROW_SIZE}>
-                <WaterfallChartFixedTopContainerSidebarCover paddingSize="none" hasShadow={false} />
+                <WaterfallChartFixedTopContainerSidebarCover paddingSize="none" hasShadow={false}>
+                  <NetworkRequestsTotal
+                    totalNetworkRequests={totalNetworkRequests}
+                    fetchedNetworkRequests={fetchedNetworkRequests}
+                  />
+                </WaterfallChartFixedTopContainerSidebarCover>
               </EuiFlexItem>
             )}
             <EuiFlexItem grow={shouldRenderSidebar ? MAIN_GROW_SIZE : true}>
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx
index ccee9d7994c80..4cf22f317bbd4 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx
@@ -8,6 +8,8 @@ import React, { createContext, useContext, Context } from 'react';
 import { WaterfallData, WaterfallDataEntry } from '../types';
 
 export interface IWaterfallContext {
+  totalNetworkRequests: number;
+  fetchedNetworkRequests: number;
   data: WaterfallData;
   sidebarItems?: unknown[];
   legendItems?: unknown[];
@@ -20,6 +22,8 @@ export interface IWaterfallContext {
 export const WaterfallContext = createContext<Partial<IWaterfallContext>>({});
 
 interface ProviderProps {
+  totalNetworkRequests: number;
+  fetchedNetworkRequests: number;
   data: IWaterfallContext['data'];
   sidebarItems?: IWaterfallContext['sidebarItems'];
   legendItems?: IWaterfallContext['legendItems'];
@@ -32,9 +36,20 @@ export const WaterfallProvider: React.FC<ProviderProps> = ({
   sidebarItems,
   legendItems,
   renderTooltipItem,
+  totalNetworkRequests,
+  fetchedNetworkRequests,
 }) => {
   return (
-    <WaterfallContext.Provider value={{ data, sidebarItems, legendItems, renderTooltipItem }}>
+    <WaterfallContext.Provider
+      value={{
+        data,
+        sidebarItems,
+        legendItems,
+        renderTooltipItem,
+        totalNetworkRequests,
+        fetchedNetworkRequests,
+      }}
+    >
       {children}
     </WaterfallContext.Provider>
   );
diff --git a/x-pack/plugins/uptime/public/state/reducers/network_events.ts b/x-pack/plugins/uptime/public/state/reducers/network_events.ts
index 44a23b0fa53d7..666617f785182 100644
--- a/x-pack/plugins/uptime/public/state/reducers/network_events.ts
+++ b/x-pack/plugins/uptime/public/state/reducers/network_events.ts
@@ -18,6 +18,7 @@ export interface NetworkEventsState {
   [checkGroup: string]: {
     [stepIndex: number]: {
       events: NetworkEvent[];
+      total: number;
       loading: boolean;
       error?: Error;
     };
@@ -45,16 +46,19 @@ export const networkEventsReducer = handleActions<NetworkEventsState, Payload>(
                   ...state[checkGroup][stepIndex],
                   loading: true,
                   events: [],
+                  total: 0,
                 }
               : {
                   loading: true,
                   events: [],
+                  total: 0,
                 },
           }
         : {
             [stepIndex]: {
               loading: true,
               events: [],
+              total: 0,
             },
           },
     }),
@@ -62,7 +66,7 @@ export const networkEventsReducer = handleActions<NetworkEventsState, Payload>(
     [String(getNetworkEventsSuccess)]: (
       state: NetworkEventsState,
       {
-        payload: { events, checkGroup, stepIndex },
+        payload: { events, total, checkGroup, stepIndex },
       }: Action<SyntheticsNetworkEventsApiResponse & FetchNetworkEventsParams>
     ) => {
       return {
@@ -74,16 +78,19 @@ export const networkEventsReducer = handleActions<NetworkEventsState, Payload>(
                     ...state[checkGroup][stepIndex],
                     loading: false,
                     events,
+                    total,
                   }
                 : {
                     loading: false,
                     events,
+                    total,
                   },
             }
           : {
               [stepIndex]: {
                 loading: false,
                 events,
+                total,
               },
             },
       };
@@ -101,11 +108,13 @@ export const networkEventsReducer = handleActions<NetworkEventsState, Payload>(
                   ...state[checkGroup][stepIndex],
                   loading: false,
                   events: [],
+                  total: 0,
                   error,
                 }
               : {
                   loading: false,
                   events: [],
+                  total: 0,
                   error,
                 },
           }
@@ -113,6 +122,7 @@ export const networkEventsReducer = handleActions<NetworkEventsState, Payload>(
             [stepIndex]: {
               loading: false,
               events: [],
+              total: 0,
               error,
             },
           },
diff --git a/x-pack/plugins/uptime/server/lib/requests/get_network_events.test.ts b/x-pack/plugins/uptime/server/lib/requests/get_network_events.test.ts
index e8618fabc4cca..2d590e80ca42d 100644
--- a/x-pack/plugins/uptime/server/lib/requests/get_network_events.test.ts
+++ b/x-pack/plugins/uptime/server/lib/requests/get_network_events.test.ts
@@ -158,6 +158,7 @@ describe('getNetworkEvents', () => {
     esClient.search.mockResolvedValueOnce({
       body: {
         hits: {
+          total: { value: 1 },
           hits: mockHits,
         },
       },
@@ -196,6 +197,7 @@ describe('getNetworkEvents', () => {
                 },
               },
               "size": 1000,
+              "track_total_hits": true,
             },
             "index": "heartbeat-8*",
           },
@@ -210,6 +212,7 @@ describe('getNetworkEvents', () => {
     esClient.search.mockResolvedValueOnce({
       body: {
         hits: {
+          total: { value: 1 },
           hits: mockHits,
         },
       },
@@ -222,30 +225,33 @@ describe('getNetworkEvents', () => {
     });
 
     expect(result).toMatchInlineSnapshot(`
-      Array [
-        Object {
-          "loadEndTime": 3287298.251,
-          "method": "GET",
-          "mimeType": "image/gif",
-          "requestSentTime": 3287154.973,
-          "requestStartTime": 3287155.502,
-          "status": 200,
-          "timestamp": "2020-12-14T10:46:39.183Z",
-          "timings": Object {
-            "blocked": 0.21400000014182297,
-            "connect": -1,
-            "dns": -1,
-            "proxy": -1,
-            "queueing": 0.5289999999149586,
-            "receive": 0.5340000002433953,
-            "send": 0.18799999998009298,
-            "ssl": -1,
-            "total": 143.27800000000934,
-            "wait": 141.81299999972907,
+      Object {
+        "events": Array [
+          Object {
+            "loadEndTime": 3287298.251,
+            "method": "GET",
+            "mimeType": "image/gif",
+            "requestSentTime": 3287154.973,
+            "requestStartTime": 3287155.502,
+            "status": 200,
+            "timestamp": "2020-12-14T10:46:39.183Z",
+            "timings": Object {
+              "blocked": 0.21400000014182297,
+              "connect": -1,
+              "dns": -1,
+              "proxy": -1,
+              "queueing": 0.5289999999149586,
+              "receive": 0.5340000002433953,
+              "send": 0.18799999998009298,
+              "ssl": -1,
+              "total": 143.27800000000934,
+              "wait": 141.81299999972907,
+            },
+            "url": "www.test.com",
           },
-          "url": "www.test.com",
-        },
-      ]
+        ],
+        "total": 1,
+      }
     `);
   });
 });
diff --git a/x-pack/plugins/uptime/server/lib/requests/get_network_events.ts b/x-pack/plugins/uptime/server/lib/requests/get_network_events.ts
index 1353175a8f94d..ec1fffd62350d 100644
--- a/x-pack/plugins/uptime/server/lib/requests/get_network_events.ts
+++ b/x-pack/plugins/uptime/server/lib/requests/get_network_events.ts
@@ -14,9 +14,10 @@ interface GetNetworkEventsParams {
 
 export const getNetworkEvents: UMElasticsearchQueryFn<
   GetNetworkEventsParams,
-  NetworkEvent[]
+  { events: NetworkEvent[]; total: number }
 > = async ({ uptimeEsClient, checkGroup, stepIndex }) => {
   const params = {
+    track_total_hits: true,
     query: {
       bool: {
         filter: [
@@ -36,24 +37,28 @@ export const getNetworkEvents: UMElasticsearchQueryFn<
 
   const microToMillis = (micro: number): number => (micro === -1 ? -1 : micro * 1000);
 
-  return result.hits.hits.map<NetworkEvent>((event: any) => {
-    const requestSentTime = microToMillis(event._source.synthetics.payload.request_sent_time);
-    const loadEndTime = microToMillis(event._source.synthetics.payload.load_end_time);
-    const requestStartTime =
-      event._source.synthetics.payload.response && event._source.synthetics.payload.response.timing
-        ? microToMillis(event._source.synthetics.payload.response.timing.request_time)
-        : undefined;
+  return {
+    total: result.hits.total.value,
+    events: result.hits.hits.map<NetworkEvent>((event: any) => {
+      const requestSentTime = microToMillis(event._source.synthetics.payload.request_sent_time);
+      const loadEndTime = microToMillis(event._source.synthetics.payload.load_end_time);
+      const requestStartTime =
+        event._source.synthetics.payload.response &&
+        event._source.synthetics.payload.response.timing
+          ? microToMillis(event._source.synthetics.payload.response.timing.request_time)
+          : undefined;
 
-    return {
-      timestamp: event._source['@timestamp'],
-      method: event._source.synthetics.payload?.method,
-      url: event._source.synthetics.payload?.url,
-      status: event._source.synthetics.payload?.status,
-      mimeType: event._source.synthetics.payload?.response?.mime_type,
-      requestSentTime,
-      requestStartTime,
-      loadEndTime,
-      timings: event._source.synthetics.payload.timings,
-    };
-  });
+      return {
+        timestamp: event._source['@timestamp'],
+        method: event._source.synthetics.payload?.method,
+        url: event._source.synthetics.payload?.url,
+        status: event._source.synthetics.payload?.status,
+        mimeType: event._source.synthetics.payload?.response?.mime_type,
+        requestSentTime,
+        requestStartTime,
+        loadEndTime,
+        timings: event._source.synthetics.payload.timings,
+      };
+    }),
+  };
 };
diff --git a/x-pack/plugins/uptime/server/rest_api/network_events/get_network_events.ts b/x-pack/plugins/uptime/server/rest_api/network_events/get_network_events.ts
index f24b319baff00..7a6355ea4247d 100644
--- a/x-pack/plugins/uptime/server/rest_api/network_events/get_network_events.ts
+++ b/x-pack/plugins/uptime/server/rest_api/network_events/get_network_events.ts
@@ -26,8 +26,6 @@ export const createNetworkEventsRoute: UMRestApiRouteFactory = (libs: UMServerLi
       stepIndex,
     });
 
-    return {
-      events: result,
-    };
+    return result;
   },
 });

From 1714b22de72bd63000649516b4c0cd4068ea00f3 Mon Sep 17 00:00:00 2001
From: Christos Nasikas <christos.nasikas@elastic.co>
Date: Mon, 25 Jan 2021 17:41:25 +0200
Subject: [PATCH 23/46] [Security Solution][Case] Improve cases and actions
 docs (#87817)

---
 x-pack/plugins/actions/README.md | 179 +++++++++++++++++++------------
 x-pack/plugins/case/README.md    |  40 ++++---
 2 files changed, 135 insertions(+), 84 deletions(-)

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 <action id>`.                                                                                                                                                                                                     | 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 <action id>`.                                                                                                                                                                   | 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/case/README.md b/x-pack/plugins/case/README.md
index 30011148cd1e7..069441ab640ee 100644
--- a/x-pack/plugins/case/README.md
+++ b/x-pack/plugins/case/README.md
@@ -4,8 +4,7 @@
 
 Elastic is developing a Case Management Workflow. Follow our progress:
 
-- [Case API Documentation](https://documenter.getpostman.com/view/172706/SW7c2SuF?version=latest)
-- [Github Meta](https://github.com/elastic/kibana/issues/50103)
+- [Case API Documentation](https://www.elastic.co/guide/en/security/master/cases-overview.html)
 
 
 # Action types
@@ -42,27 +41,28 @@ This action type has no `secrets` properties.
 | description | The case’s description.                                               | string                  |
 | tags        | String array containing words and phrases that help categorize cases. | string[]                |
 | connector   | Object containing the connector’s configuration.                      | [connector](#connector) |
+| settings    | Object containing the case’s settings.                                | [settings](#settings)   |
 
 #### `subActionParams (update)`
 
-| Property    | Description                                                | Type                    |
-| ----------- | ---------------------------------------------------------- | ----------------------- |
-| id          | The ID of the case being updated.                          | string                  |
-| tile        | The updated case title.                                    | string                  |
-| description | The updated case description.                              | string                  |
-| tags        | The updated case tags.                                     | string                  |
-| connector   | Object containing the connector’s configuration.           | [connector](#connector) |
-| status      | The updated case status, which can be: `open` or `closed`. | string                  |
-| version     | The current case version.                                  | string                  |
+| Property    | Description                                                               | Type                    |
+| ----------- | ------------------------------------------------------------------------- | ----------------------- |
+| id          | The ID of the case being updated.                                         | string                  |
+| tile        | The updated case title.                                                   | string                  |
+| description | The updated case description.                                             | string                  |
+| tags        | The updated case tags.                                                    | string                  |
+| connector   | Object containing the connector’s configuration.                          | [connector](#connector) |
+| status      | The updated case status, which can be: `open`, `in-progress` or `closed`. | string                  |
+| settings    | Object containing the case’s settings.                                    | [settings](#settings)   |
+| version     | The current case version.                                                 | string                  |
 
 #### `subActionParams (addComment)`
 
-| Property | Description                                                             | Type              |
-| -------- | ----------------------------------------------------------------------- | ----------------- |
-| type     | The type of the comment                                                 | `user` \| `alert` |
-| comment  | The comment. Valid only when type is `user`.                            | string            |
-| alertId  | The alert ID. Valid only when the type is `alert`                       | string            |
-| index    | The index where the alert is saved. Valid only when the type is `alert` | string            |
+| Property | Description              | Type   |
+| -------- | ------------------------ | ------ |
+| type     | The type of the comment. | `user` |
+| comment  | The comment.             | string |
+
 #### `connector`
 
 | Property | Description                                                                                       | Type              |
@@ -96,3 +96,9 @@ For IBM Resilient connectors:
 | ------------ | ------------------------------- | -------- |
 | issueTypes   | The issue types of the issue.   | string[] |
 | severityCode | The severity code of the issue. | string   |
+
+#### `settings`
+
+| Property   | Description                    | Type    |
+| ---------- | ------------------------------ | ------- |
+| syncAlerts | Turn on or off alert synching. | boolean |
\ No newline at end of file

From 207c8eac5c50b45212b4c34e781fe2fef90fbc44 Mon Sep 17 00:00:00 2001
From: Gidi Meir Morris <github@gidi.io>
Date: Mon, 25 Jan 2021 15:56:29 +0000
Subject: [PATCH 24/46] corrected terminology in PR template (#89095)

We recently added a usage of `whitelist`, I've changed it to `allowlist`
---
 .github/PULL_REQUEST_TEMPLATE.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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)
 

From f6837a1f66db9909768afdd68bf2f0be8c3087f4 Mon Sep 17 00:00:00 2001
From: Gidi Meir Morris <github@gidi.io>
Date: Mon, 25 Jan 2021 15:57:50 +0000
Subject: [PATCH 25/46] made unit test more reliable (#89094)

Made unit test more reliable by using resolving promises rather than timed `await`s that could be flaky when the node event loop is overwhelmed.
---
 .../task_manager/server/task_pool.test.ts     | 36 +++++++++++--------
 1 file changed, 22 insertions(+), 14 deletions(-)

diff --git a/x-pack/plugins/task_manager/server/task_pool.test.ts b/x-pack/plugins/task_manager/server/task_pool.test.ts
index 324e376c32d95..14ad0561928f8 100644
--- a/x-pack/plugins/task_manager/server/task_pool.test.ts
+++ b/x-pack/plugins/task_manager/server/task_pool.test.ts
@@ -13,6 +13,7 @@ import { Logger } from '../../../../src/core/server';
 import { asOk } from './lib/result_type';
 import { SavedObjectsErrorHelpers } from '../../../../src/core/server';
 import moment from 'moment';
+import uuid from 'uuid';
 
 describe('TaskPool', () => {
   test('occupiedWorkers are a sum of running tasks', async () => {
@@ -133,7 +134,7 @@ describe('TaskPool', () => {
     const result = await pool.run([mockTask(), taskFailedToRun, mockTask()]);
 
     expect(logger.debug).toHaveBeenCalledWith(
-      'Task TaskType "shooooo" failed in attempt to run: Saved object [task/foo] not found'
+      `Task TaskType "shooooo" failed in attempt to run: Saved object [task/${taskFailedToRun.id}] not found`
     );
     expect(logger.warn).not.toHaveBeenCalled();
 
@@ -203,26 +204,28 @@ describe('TaskPool', () => {
     sinon.assert.calledOnce(secondRun);
   });
 
-  test.skip('run cancels expired tasks prior to running new tasks', async () => {
+  test('run cancels expired tasks prior to running new tasks', async () => {
     const logger = loggingSystemMock.create().get();
     const pool = new TaskPool({
       maxWorkers$: of(2),
       logger,
     });
 
-    const readyToExpire = resolvable();
+    const haltUntilWeAfterFirstRun = resolvable();
     const taskHasExpired = resolvable();
+    const haltTaskSoThatItCanBeCanceled = resolvable();
+
     const shouldRun = sinon.spy(() => Promise.resolve());
     const shouldNotRun = sinon.spy(() => Promise.resolve());
     const now = new Date();
     const result = await pool.run([
       {
-        ...mockTask(),
+        ...mockTask({ id: '1' }),
         async run() {
-          await readyToExpire;
+          await haltUntilWeAfterFirstRun;
           this.isExpired = true;
           taskHasExpired.resolve();
-          await sleep(10);
+          await haltTaskSoThatItCanBeCanceled;
           return asOk({ state: {} });
         },
         get expiration() {
@@ -235,9 +238,10 @@ describe('TaskPool', () => {
         cancel: shouldRun,
       },
       {
-        ...mockTask(),
+        ...mockTask({ id: '2' }),
         async run() {
-          await sleep(10);
+          // halt here so that we can verify that this task is counted in `occupiedWorkers`
+          await haltUntilWeAfterFirstRun;
           return asOk({ state: {} });
         },
         cancel: shouldNotRun,
@@ -248,16 +252,19 @@ describe('TaskPool', () => {
     expect(pool.occupiedWorkers).toEqual(2);
     expect(pool.availableWorkers).toEqual(0);
 
-    readyToExpire.resolve();
+    // release first stage in task so that it has time to expire, but not complete
+    haltUntilWeAfterFirstRun.resolve();
     await taskHasExpired;
 
-    expect(await pool.run([{ ...mockTask() }])).toBeTruthy();
+    expect(await pool.run([{ ...mockTask({ id: '3' }) }])).toBeTruthy();
 
     sinon.assert.calledOnce(shouldRun);
     sinon.assert.notCalled(shouldNotRun);
 
-    expect(pool.occupiedWorkers).toEqual(2);
-    expect(pool.availableWorkers).toEqual(0);
+    expect(pool.occupiedWorkers).toEqual(1);
+    expect(pool.availableWorkers).toEqual(1);
+
+    haltTaskSoThatItCanBeCanceled.resolve();
 
     expect(logger.warn).toHaveBeenCalledWith(
       `Cancelling task TaskType "shooooo" as it expired at ${now.toISOString()} after running for 05m 30s (with timeout set at 5m).`
@@ -355,10 +362,10 @@ describe('TaskPool', () => {
     });
   }
 
-  function mockTask() {
+  function mockTask(overrides = {}) {
     return {
       isExpired: false,
-      id: 'foo',
+      id: uuid.v4(),
       cancel: async () => undefined,
       markTaskAsRunning: jest.fn(async () => true),
       run: mockRun(),
@@ -377,6 +384,7 @@ describe('TaskPool', () => {
           createTaskRunner: jest.fn(),
         };
       },
+      ...overrides,
     };
   }
 });

From e251ff4be593d45d6094efb414b1dccecff42072 Mon Sep 17 00:00:00 2001
From: Oliver Gupte <ogupte@users.noreply.github.com>
Date: Mon, 25 Jan 2021 08:05:32 -0800
Subject: [PATCH 26/46] [APM] Renames significant terms feature to
 "Correlations" (#88974) (#89028)

* [APM] Renames significant terms feature to "Correlations" (#88974)

* fix capitalizations

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
 x-pack/plugins/apm/common/ui_settings_keys.ts          |  2 +-
 .../app/Correlations/LatencyCorrelations.tsx           |  2 +-
 .../apm/public/components/app/Correlations/index.tsx   | 10 +++++-----
 x-pack/plugins/apm/server/ui_settings.ts               |  9 ++++-----
 4 files changed, 11 insertions(+), 12 deletions(-)

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() {
                 <EuiFormRow
                   fullWidth={true}
                   label="Field"
-                  helpText="Fields to analyse for significant terms"
+                  helpText="Fields to analyse for correlations"
                 >
                   <EuiComboBox
                     fullWidth={true}
diff --git a/x-pack/plugins/apm/public/components/app/Correlations/index.tsx b/x-pack/plugins/apm/public/components/app/Correlations/index.tsx
index fada90039aecf..e01d1028f4868 100644
--- a/x-pack/plugins/apm/public/components/app/Correlations/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/Correlations/index.tsx
@@ -20,7 +20,7 @@ import {
 import { useHistory } from 'react-router-dom';
 import { EuiSpacer } from '@elastic/eui';
 import { isActivePlatinumLicense } from '../../../../common/license_check';
-import { enableSignificantTerms } from '../../../../common/ui_settings_keys';
+import { enableCorrelations } from '../../../../common/ui_settings_keys';
 import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context';
 import { LatencyCorrelations } from './LatencyCorrelations';
 import { ErrorCorrelations } from './ErrorCorrelations';
@@ -35,7 +35,7 @@ export function Correlations() {
   const history = useHistory();
   const [isFlyoutVisible, setIsFlyoutVisible] = useState(false);
   if (
-    !uiSettings.get(enableSignificantTerms) ||
+    !uiSettings.get(enableCorrelations) ||
     !isActivePlatinumLicense(license)
   ) {
     return null;
@@ -48,7 +48,7 @@ export function Correlations() {
           setIsFlyoutVisible(true);
         }}
       >
-        View significant terms
+        View correlations
       </EuiButton>
 
       <EuiSpacer size="s" />
@@ -62,7 +62,7 @@ export function Correlations() {
           >
             <EuiFlyoutHeader hasBorder aria-labelledby="correlations-flyout">
               <EuiTitle>
-                <h2 id="correlations-flyout">Significant terms</h2>
+                <h2 id="correlations-flyout">Correlations</h2>
               </EuiTitle>
             </EuiFlyoutHeader>
             <EuiFlyoutBody>
@@ -88,7 +88,7 @@ export function Correlations() {
                 iconType="alert"
               >
                 <p>
-                  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.
                 </p>
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<string, UiSettingsParams<boolean>> = {
-  [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(),

From 6391ef9c45409cdd47d13f5b5fb420d092562b68 Mon Sep 17 00:00:00 2001
From: Shahzad <shahzad.muhammad@elastic.co>
Date: Mon, 25 Jan 2021 17:08:31 +0100
Subject: [PATCH 27/46] [Observability] Lazy load shared components (#88802)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
 .../app/RumDashboard/UXMetrics/index.tsx      | 23 ++++++++++------
 .../public/components/app/header/index.tsx    |  2 +-
 .../components/app/section/ux/index.tsx       |  2 +-
 .../shared/core_web_vitals/index.tsx          | 17 ++++--------
 .../components/shared/header_menu_portal.tsx  | 12 +++------
 .../public/components/shared/index.tsx        | 26 +++++++++++++++++++
 .../public/components/shared/types.ts         | 23 ++++++++++++++++
 x-pack/plugins/observability/public/index.ts  |  6 ++---
 8 files changed, 78 insertions(+), 33 deletions(-)
 create mode 100644 x-pack/plugins/observability/public/components/shared/index.tsx
 create mode 100644 x-pack/plugins/observability/public/components/shared/types.ts

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 (
     <EuiPanel>
       <EuiFlexGroup justifyContent="spaceBetween" wrap>
@@ -67,12 +79,7 @@ export function UXMetrics() {
       <EuiFlexGroup justifyContent="spaceBetween" wrap>
         <EuiFlexItem grow={1} data-cy={`client-metrics`}>
           <EuiSpacer size="s" />
-          <CoreVitals
-            data={data}
-            totalPageViews={totalPageViews}
-            loading={status !== 'success'}
-            displayTrafficMetric={true}
-          />
+          {CoreVitals}
         </EuiFlexItem>
       </EuiFlexGroup>
     </EuiPanel>
diff --git a/x-pack/plugins/observability/public/components/app/header/index.tsx b/x-pack/plugins/observability/public/components/app/header/index.tsx
index b195bb52e7ed2..097871fe020e5 100644
--- a/x-pack/plugins/observability/public/components/app/header/index.tsx
+++ b/x-pack/plugins/observability/public/components/app/header/index.tsx
@@ -17,7 +17,7 @@ import { i18n } from '@kbn/i18n';
 import React, { ReactNode } from 'react';
 import styled from 'styled-components';
 import { usePluginContext } from '../../../hooks/use_plugin_context';
-import { HeaderMenuPortal } from '../../shared/header_menu_portal';
+import HeaderMenuPortal from '../../shared/header_menu_portal';
 
 const Container = styled.div<{ color: string }>`
   background: ${(props) => props.color};
diff --git a/x-pack/plugins/observability/public/components/app/section/ux/index.tsx b/x-pack/plugins/observability/public/components/app/section/ux/index.tsx
index 43f1072d06fc2..7074a895d058b 100644
--- a/x-pack/plugins/observability/public/components/app/section/ux/index.tsx
+++ b/x-pack/plugins/observability/public/components/app/section/ux/index.tsx
@@ -12,7 +12,7 @@ import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher';
 import { useHasData } from '../../../../hooks/use_has_data';
 import { useTimeRange } from '../../../../hooks/use_time_range';
 import { UXHasDataResponse } from '../../../../typings';
-import { CoreVitals } from '../../../shared/core_web_vitals';
+import CoreVitals from '../../../shared/core_web_vitals';
 
 interface Props {
   bucketSize: string;
diff --git a/x-pack/plugins/observability/public/components/shared/core_web_vitals/index.tsx b/x-pack/plugins/observability/public/components/shared/core_web_vitals/index.tsx
index f573c8cfc1f97..7d40ce089cec4 100644
--- a/x-pack/plugins/observability/public/components/shared/core_web_vitals/index.tsx
+++ b/x-pack/plugins/observability/public/components/shared/core_web_vitals/index.tsx
@@ -16,6 +16,7 @@ import {
 import { CoreVitalItem } from './core_vital_item';
 import { WebCoreVitalsTitle } from './web_core_vitals_title';
 import { ServiceName } from './service_name';
+import { CoreVitalProps } from '../types';
 
 export interface UXMetrics {
   cls: number | null;
@@ -29,7 +30,7 @@ export interface UXMetrics {
   clsRanks: number[];
 }
 
-export function formatToSec(value?: number | string, fromUnit = 'MicroSec'): string {
+function formatToSec(value?: number | string, fromUnit = 'MicroSec'): string {
   const valueInMs = Number(value ?? 0) / (fromUnit === 'MicroSec' ? 1000 : 1);
 
   if (valueInMs < 1000) {
@@ -51,23 +52,15 @@ const CoreVitalsThresholds = {
   CLS: { good: '0.1', bad: '0.25' },
 };
 
-interface Props {
-  loading: boolean;
-  data?: UXMetrics | null;
-  displayServiceName?: boolean;
-  serviceName?: string;
-  totalPageViews?: number;
-  displayTrafficMetric?: boolean;
-}
-
-export function CoreVitals({
+// eslint-disable-next-line import/no-default-export
+export default function CoreVitals({
   data,
   loading,
   displayServiceName,
   serviceName,
   totalPageViews,
   displayTrafficMetric = false,
-}: Props) {
+}: CoreVitalProps) {
   const { lcp, lcpRanks, fid, fidRanks, cls, clsRanks, coreVitalPages } = data || {};
 
   return (
diff --git a/x-pack/plugins/observability/public/components/shared/header_menu_portal.tsx b/x-pack/plugins/observability/public/components/shared/header_menu_portal.tsx
index ca03eb6ddb45a..e209e830d0f37 100644
--- a/x-pack/plugins/observability/public/components/shared/header_menu_portal.tsx
+++ b/x-pack/plugins/observability/public/components/shared/header_menu_portal.tsx
@@ -4,17 +4,13 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import React, { ReactNode, useEffect, useMemo } from 'react';
+import React, { useEffect, useMemo } from 'react';
 import { createPortalNode, InPortal, OutPortal } from 'react-reverse-portal';
-import { AppMountParameters } from '../../../../../../src/core/public';
 import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public';
+import { HeaderMenuPortalProps } from './types';
 
-interface HeaderMenuPortalProps {
-  children: ReactNode;
-  setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
-}
-
-export function HeaderMenuPortal({ children, setHeaderActionMenu }: HeaderMenuPortalProps) {
+// eslint-disable-next-line import/no-default-export
+export default function HeaderMenuPortal({ children, setHeaderActionMenu }: HeaderMenuPortalProps) {
   const portalNode = useMemo(() => createPortalNode(), []);
 
   useEffect(() => {
diff --git a/x-pack/plugins/observability/public/components/shared/index.tsx b/x-pack/plugins/observability/public/components/shared/index.tsx
new file mode 100644
index 0000000000000..6e3835129beb2
--- /dev/null
+++ b/x-pack/plugins/observability/public/components/shared/index.tsx
@@ -0,0 +1,26 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { lazy, Suspense } from 'react';
+import { CoreVitalProps, HeaderMenuPortalProps } from './types';
+
+export function getCoreVitalsComponent(props: CoreVitalProps) {
+  const CoreVitalsLazy = lazy(() => import('./core_web_vitals/index'));
+  return (
+    <Suspense fallback={null}>
+      <CoreVitalsLazy {...props} />
+    </Suspense>
+  );
+}
+
+export function HeaderMenuPortal(props: HeaderMenuPortalProps) {
+  const HeaderMenuPortalLazy = lazy(() => import('./header_menu_portal'));
+  return (
+    <Suspense fallback={null}>
+      <HeaderMenuPortalLazy {...props} />
+    </Suspense>
+  );
+}
diff --git a/x-pack/plugins/observability/public/components/shared/types.ts b/x-pack/plugins/observability/public/components/shared/types.ts
new file mode 100644
index 0000000000000..9039f444f550f
--- /dev/null
+++ b/x-pack/plugins/observability/public/components/shared/types.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { ReactNode } from 'react';
+import { AppMountParameters } from '../../../../../../src/core/public';
+import { UXMetrics } from './core_web_vitals';
+
+export interface HeaderMenuPortalProps {
+  children: ReactNode;
+  setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
+}
+
+export interface CoreVitalProps {
+  loading: boolean;
+  data?: UXMetrics | null;
+  displayServiceName?: boolean;
+  serviceName?: string;
+  totalPageViews?: number;
+  displayTrafficMetric?: boolean;
+}
diff --git a/x-pack/plugins/observability/public/index.ts b/x-pack/plugins/observability/public/index.ts
index 22cc5faf23967..c052541956c13 100644
--- a/x-pack/plugins/observability/public/index.ts
+++ b/x-pack/plugins/observability/public/index.ts
@@ -6,8 +6,7 @@
 
 import { PluginInitializerContext, PluginInitializer } from 'kibana/public';
 import { Plugin, ObservabilityPluginSetup, ObservabilityPluginStart } from './plugin';
-export { HeaderMenuPortal } from './components/shared/header_menu_portal';
-export { ObservabilityPluginSetup, ObservabilityPluginStart };
+export type { ObservabilityPluginSetup, ObservabilityPluginStart };
 
 export const plugin: PluginInitializer<ObservabilityPluginSetup, ObservabilityPluginStart> = (
   context: PluginInitializerContext
@@ -17,7 +16,8 @@ export const plugin: PluginInitializer<ObservabilityPluginSetup, ObservabilityPl
 
 export * from './components/shared/action_menu/';
 
-export { UXMetrics, CoreVitals, formatToSec } from './components/shared/core_web_vitals/';
+export type { UXMetrics } from './components/shared/core_web_vitals/';
+export { getCoreVitalsComponent, HeaderMenuPortal } from './components/shared/';
 
 export {
   useTrackPageview,

From 4bc5f01428e152c2a06646dd64416f4687b1a76f Mon Sep 17 00:00:00 2001
From: Pete Harverson <peteharverson@users.noreply.github.com>
Date: Mon, 25 Jan 2021 16:20:14 +0000
Subject: [PATCH 28/46] [ML] Add ML deep links to navigational search (#88958)

* [ML] Add ML deep links to navigational search

* [ML] Refactor register helper files

* [ML] Edit import in search_deep_links

* [ML] Move register_feature out of register_helper

* [ML] Add comment about registerFeature

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
 x-pack/plugins/ml/public/plugin.ts            |  37 +++---
 .../ml/public/register_helper/index.ts        |  10 ++
 .../register_search_links/index.ts}           |   5 +-
 .../register_search_links.ts                  |  27 +++++
 .../search_deep_links.ts                      | 110 ++++++++++++++++++
 5 files changed, 172 insertions(+), 17 deletions(-)
 create mode 100644 x-pack/plugins/ml/public/register_helper/index.ts
 rename x-pack/plugins/ml/public/{register_helper.ts => register_helper/register_search_links/index.ts} (51%)
 create mode 100644 x-pack/plugins/ml/public/register_helper/register_search_links/register_search_links.ts
 create mode 100644 x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts

diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts
index 1cc69ac2239ab..7c32671be93c4 100644
--- a/x-pack/plugins/ml/public/plugin.ts
+++ b/x-pack/plugins/ml/public/plugin.ts
@@ -70,7 +70,7 @@ export interface MlSetupDependencies {
 export type MlCoreSetup = CoreSetup<MlStartDependencies, MlPluginStart>;
 
 export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
-  private appUpdater = new BehaviorSubject<AppUpdater>(() => ({}));
+  private appUpdater$ = new BehaviorSubject<AppUpdater>(() => ({}));
   private urlGenerator: undefined | UrlGeneratorContract<typeof ML_APP_URL_GENERATOR>;
 
   constructor(private initializerContext: PluginInitializerContext) {}
@@ -85,7 +85,7 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
       euiIconType: PLUGIN_ICON_SOLUTION,
       appRoute: '/app/ml',
       category: DEFAULT_APP_CATEGORIES.kibana,
-      updater$: this.appUpdater,
+      updater$: this.appUpdater$,
       mount: async (params: AppMountParameters) => {
         const [coreStart, pluginsStart] = await core.getStartServices();
         const kibanaVersion = this.initializerContext.env.packageInfo.version;
@@ -133,23 +133,34 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
         });
       } else {
         // if ml is disabled in elasticsearch, disable ML in kibana
-        this.appUpdater.next(() => ({
+        this.appUpdater$.next(() => ({
           status: AppStatus.inaccessible,
         }));
       }
 
       // register various ML plugin features which require a full license
-      const { registerEmbeddables, registerManagementSection, registerMlUiActions } = await import(
-        './register_helper'
-      );
-
-      if (isMlEnabled(license) && isFullLicense(license)) {
-        const canManageMLJobs = capabilities.management?.insightsAndAlerting?.jobsListLink ?? false;
-        if (canManageMLJobs && pluginsSetup.management !== undefined) {
-          registerManagementSection(pluginsSetup.management, core).enable();
+      // note including registerFeature in register_helper would cause the page bundle size to increase significantly
+      const {
+        registerEmbeddables,
+        registerManagementSection,
+        registerMlUiActions,
+        registerSearchLinks,
+      } = await import('./register_helper');
+
+      const mlEnabled = isMlEnabled(license);
+      const fullLicense = isFullLicense(license);
+      if (mlEnabled) {
+        registerSearchLinks(this.appUpdater$, fullLicense);
+
+        if (fullLicense) {
+          const canManageMLJobs =
+            capabilities.management?.insightsAndAlerting?.jobsListLink ?? false;
+          if (canManageMLJobs && pluginsSetup.management !== undefined) {
+            registerManagementSection(pluginsSetup.management, core).enable();
+          }
+          registerEmbeddables(pluginsSetup.embeddable, core);
+          registerMlUiActions(pluginsSetup.uiActions, core);
         }
-        registerEmbeddables(pluginsSetup.embeddable, core);
-        registerMlUiActions(pluginsSetup.uiActions, core);
       }
     });
 
diff --git a/x-pack/plugins/ml/public/register_helper/index.ts b/x-pack/plugins/ml/public/register_helper/index.ts
new file mode 100644
index 0000000000000..8e62b6562520a
--- /dev/null
+++ b/x-pack/plugins/ml/public/register_helper/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;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export { registerEmbeddables } from '../embeddables';
+export { registerManagementSection } from '../application/management';
+export { registerMlUiActions } from '../ui_actions';
+export { registerSearchLinks } from './register_search_links';
diff --git a/x-pack/plugins/ml/public/register_helper.ts b/x-pack/plugins/ml/public/register_helper/register_search_links/index.ts
similarity index 51%
rename from x-pack/plugins/ml/public/register_helper.ts
rename to x-pack/plugins/ml/public/register_helper/register_search_links/index.ts
index 50ec53a10ece9..e1912c7ebabeb 100644
--- a/x-pack/plugins/ml/public/register_helper.ts
+++ b/x-pack/plugins/ml/public/register_helper/register_search_links/index.ts
@@ -4,7 +4,4 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-export { registerEmbeddables } from './embeddables';
-export { registerFeature } from './register_feature';
-export { registerManagementSection } from './application/management';
-export { registerMlUiActions } from './ui_actions';
+export { registerSearchLinks } from './register_search_links';
diff --git a/x-pack/plugins/ml/public/register_helper/register_search_links/register_search_links.ts b/x-pack/plugins/ml/public/register_helper/register_search_links/register_search_links.ts
new file mode 100644
index 0000000000000..2df7e8140698a
--- /dev/null
+++ b/x-pack/plugins/ml/public/register_helper/register_search_links/register_search_links.ts
@@ -0,0 +1,27 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+import { BehaviorSubject } from 'rxjs';
+
+import { AppUpdater } from 'src/core/public';
+import { getSearchDeepLinks } from './search_deep_links';
+
+export function registerSearchLinks(
+  appUpdater: BehaviorSubject<AppUpdater>,
+  isFullLicense: boolean
+) {
+  appUpdater.next(() => ({
+    meta: {
+      keywords: [
+        i18n.translate('xpack.ml.keyword.ml', {
+          defaultMessage: 'ML',
+        }),
+      ],
+      searchDeepLinks: getSearchDeepLinks(isFullLicense),
+    },
+  }));
+}
diff --git a/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts b/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts
new file mode 100644
index 0000000000000..7108fb7af5670
--- /dev/null
+++ b/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts
@@ -0,0 +1,110 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+import type { AppSearchDeepLink } from 'src/core/public';
+import { ML_PAGES } from '../../../common/constants/ml_url_generator';
+
+const OVERVIEW_LINK_SEARCH_DEEP_LINK: AppSearchDeepLink = {
+  id: 'mlOverviewSearchDeepLink',
+  title: i18n.translate('xpack.ml.searchDeepLink.overview', {
+    defaultMessage: 'Overview',
+  }),
+  path: `/${ML_PAGES.OVERVIEW}`,
+};
+
+const ANOMALY_DETECTION_SEARCH_DEEP_LINK: AppSearchDeepLink = {
+  id: 'mlAnomalyDetectionSearchDeepLink',
+  title: i18n.translate('xpack.ml.searchDeepLink.anomalyDetection', {
+    defaultMessage: 'Anomaly Detection',
+  }),
+  path: `/${ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE}`,
+};
+
+const DATA_FRAME_ANALYTICS_SEARCH_DEEP_LINK: AppSearchDeepLink = {
+  id: 'mlDataFrameAnalyticsSearchDeepLink',
+  title: i18n.translate('xpack.ml.searchDeepLink.dataFrameAnalytics', {
+    defaultMessage: 'Data Frame Analytics',
+  }),
+  path: `/${ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE}`,
+  searchDeepLinks: [
+    {
+      id: 'mlTrainedModelsSearchDeepLink',
+      title: i18n.translate('xpack.ml.searchDeepLink.trainedModels', {
+        defaultMessage: 'Trained Models',
+      }),
+      path: `/${ML_PAGES.DATA_FRAME_ANALYTICS_MODELS_MANAGE}`,
+    },
+  ],
+};
+
+const DATA_VISUALIZER_SEARCH_DEEP_LINK: AppSearchDeepLink = {
+  id: 'mlDataVisualizerSearchDeepLink',
+  title: i18n.translate('xpack.ml.searchDeepLink.dataVisualizer', {
+    defaultMessage: 'Data Visualizer',
+  }),
+  path: `/${ML_PAGES.DATA_VISUALIZER}`,
+};
+
+const FILE_UPLOAD_SEARCH_DEEP_LINK: AppSearchDeepLink = {
+  id: 'mlFileUploadSearchDeepLink',
+  title: i18n.translate('xpack.ml.searchDeepLink.fileUpload', {
+    defaultMessage: 'File Upload',
+  }),
+  path: `/${ML_PAGES.DATA_VISUALIZER_FILE}`,
+};
+
+const INDEX_DATA_VISUALIZER_SEARCH_DEEP_LINK: AppSearchDeepLink = {
+  id: 'mlIndexDataVisualizerSearchDeepLink',
+  title: i18n.translate('xpack.ml.searchDeepLink.indexDataVisualizer', {
+    defaultMessage: 'Index Data Visualizer',
+  }),
+  path: `/${ML_PAGES.DATA_VISUALIZER_INDEX_SELECT}`,
+};
+
+const SETTINGS_SEARCH_DEEP_LINK: AppSearchDeepLink = {
+  id: 'mlSettingsSearchDeepLink',
+  title: i18n.translate('xpack.ml.searchDeepLink.settings', {
+    defaultMessage: 'Settings',
+  }),
+  path: `/${ML_PAGES.SETTINGS}`,
+  searchDeepLinks: [
+    {
+      id: 'mlCalendarSettingsSearchDeepLink',
+      title: i18n.translate('xpack.ml.searchDeepLink.calendarSettings', {
+        defaultMessage: 'Calendars',
+      }),
+      path: `/${ML_PAGES.CALENDARS_MANAGE}`,
+    },
+    {
+      id: 'mlFilterListsSettingsSearchDeepLink',
+      title: i18n.translate('xpack.ml.searchDeepLink.filterListsSettings', {
+        defaultMessage: 'Filter Lists',
+      }),
+      path: `/${ML_PAGES.SETTINGS}`, // Link to settings page as read only users cannot view filter lists.
+    },
+  ],
+};
+
+export function getSearchDeepLinks(isFullLicense: boolean) {
+  const deepLinks: AppSearchDeepLink[] = [
+    DATA_VISUALIZER_SEARCH_DEEP_LINK,
+    FILE_UPLOAD_SEARCH_DEEP_LINK,
+    INDEX_DATA_VISUALIZER_SEARCH_DEEP_LINK,
+  ];
+
+  if (isFullLicense === true) {
+    deepLinks.push(
+      OVERVIEW_LINK_SEARCH_DEEP_LINK,
+      ANOMALY_DETECTION_SEARCH_DEEP_LINK,
+      DATA_FRAME_ANALYTICS_SEARCH_DEEP_LINK,
+      SETTINGS_SEARCH_DEEP_LINK
+    );
+  }
+
+  return deepLinks;
+}

From 43db7e365ff53e9014934910c34ee647eac29955 Mon Sep 17 00:00:00 2001
From: Alison Goryachev <alison.goryachev@elastic.co>
Date: Mon, 25 Jan 2021 11:27:59 -0500
Subject: [PATCH 29/46] [Search Profiler] Migrate server to new es-js client
 (#88725)

---
 .../searchprofiler/server/routes/profile.ts   | 20 +++----
 x-pack/test/api_integration/apis/index.ts     |  1 +
 .../apis/searchprofiler/index.ts              | 13 +++++
 .../apis/searchprofiler/searchprofiler.ts     | 56 +++++++++++++++++++
 4 files changed, 80 insertions(+), 10 deletions(-)
 create mode 100644 x-pack/test/api_integration/apis/searchprofiler/index.ts
 create mode 100644 x-pack/test/api_integration/apis/searchprofiler/searchprofiler.ts

diff --git a/x-pack/plugins/searchprofiler/server/routes/profile.ts b/x-pack/plugins/searchprofiler/server/routes/profile.ts
index 914c688a080f8..87f2ec1df1c92 100644
--- a/x-pack/plugins/searchprofiler/server/routes/profile.ts
+++ b/x-pack/plugins/searchprofiler/server/routes/profile.ts
@@ -27,10 +27,6 @@ export const register = ({ router, getLicenseStatus, log }: RouteDependencies) =
         });
       }
 
-      const {
-        core: { elasticsearch },
-      } = ctx;
-
       const {
         body: { query, index },
       } = request;
@@ -46,21 +42,25 @@ export const register = ({ router, getLicenseStatus, log }: RouteDependencies) =
         body: JSON.stringify(parsed, null, 2),
       };
       try {
-        const resp = await elasticsearch.legacy.client.callAsCurrentUser('search', body);
+        const client = ctx.core.elasticsearch.client.asCurrentUser;
+        const resp = await client.search(body);
+
         return response.ok({
           body: {
             ok: true,
-            resp,
+            resp: resp.body,
           },
         });
       } catch (err) {
         log.error(err);
+        const { statusCode, body: errorBody } = err;
+
         return response.customError({
-          statusCode: err.status || 500,
-          body: err.body
+          statusCode: statusCode || 500,
+          body: errorBody
             ? {
-                message: err.message,
-                attributes: err.body,
+                message: errorBody.error?.reason,
+                attributes: errorBody,
               }
             : err,
         });
diff --git a/x-pack/test/api_integration/apis/index.ts b/x-pack/test/api_integration/apis/index.ts
index 6b6326df017aa..2cd2654cffe3e 100644
--- a/x-pack/test/api_integration/apis/index.ts
+++ b/x-pack/test/api_integration/apis/index.ts
@@ -33,5 +33,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
     loadTestFile(require.resolve('./transform'));
     loadTestFile(require.resolve('./lists'));
     loadTestFile(require.resolve('./upgrade_assistant'));
+    loadTestFile(require.resolve('./searchprofiler'));
   });
 }
diff --git a/x-pack/test/api_integration/apis/searchprofiler/index.ts b/x-pack/test/api_integration/apis/searchprofiler/index.ts
new file mode 100644
index 0000000000000..36794feb00d1b
--- /dev/null
+++ b/x-pack/test/api_integration/apis/searchprofiler/index.ts
@@ -0,0 +1,13 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export default function ({ loadTestFile }: FtrProviderContext) {
+  describe('Search Profiler', () => {
+    loadTestFile(require.resolve('./searchprofiler'));
+  });
+}
diff --git a/x-pack/test/api_integration/apis/searchprofiler/searchprofiler.ts b/x-pack/test/api_integration/apis/searchprofiler/searchprofiler.ts
new file mode 100644
index 0000000000000..041cfb82520b4
--- /dev/null
+++ b/x-pack/test/api_integration/apis/searchprofiler/searchprofiler.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;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import expect from '@kbn/expect';
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+const API_BASE_PATH = '/api/searchprofiler';
+
+export default function ({ getService }: FtrProviderContext) {
+  const supertest = getService('supertest');
+
+  describe('Profile', () => {
+    it('should return profile results for a valid index', async () => {
+      const payload = {
+        index: '_all',
+        query: {
+          query: {
+            match_all: {},
+          },
+        },
+      };
+
+      const { body } = await supertest
+        .post(`${API_BASE_PATH}/profile`)
+        .set('kbn-xsrf', 'xxx')
+        .set('Content-Type', 'application/json;charset=UTF-8')
+        .send(payload)
+        .expect(200);
+
+      expect(body.ok).to.eql(true);
+    });
+
+    it('should return error for invalid index', async () => {
+      const payloadWithInvalidIndex = {
+        index: 'index_does_not_exist',
+        query: {
+          query: {
+            match_all: {},
+          },
+        },
+      };
+
+      const { body } = await supertest
+        .post(`${API_BASE_PATH}/execute`)
+        .set('kbn-xsrf', 'xxx')
+        .set('Content-Type', 'application/json;charset=UTF-8')
+        .send(payloadWithInvalidIndex)
+        .expect(404);
+
+      expect(body.error).to.eql('Not Found');
+    });
+  });
+}

From 592d79c210f7a614c37f1773ae4d4feef003f0e8 Mon Sep 17 00:00:00 2001
From: Tiago Costa <tiagoffcc@hotmail.com>
Date: Mon, 25 Jan 2021 17:07:13 +0000
Subject: [PATCH 30/46] skip failing es promotion suite (#89180)

---
 .../cross_cluster_replication/feature_controls/ccr_security.ts | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/ccr_security.ts b/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/ccr_security.ts
index 6b4b9c61151ba..4e234dd8e5501 100644
--- a/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/ccr_security.ts
+++ b/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/ccr_security.ts
@@ -13,7 +13,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
   const appsMenu = getService('appsMenu');
   const managementMenu = getService('managementMenu');
 
-  describe('security', () => {
+  // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/89180
+  describe.skip('security', () => {
     before(async () => {
       await esArchiver.load('empty_kibana');
       await PageObjects.common.navigateToApp('home');

From 342f9fbac15ce118ad088d2908c263d36ec23be4 Mon Sep 17 00:00:00 2001
From: Tiago Costa <tiagoffcc@hotmail.com>
Date: Mon, 25 Jan 2021 17:11:52 +0000
Subject: [PATCH 31/46] skip failing es promotion suite (#89181)

---
 .../feature_controls/remote_clusters_security.ts               | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/x-pack/test/functional/apps/remote_clusters/feature_controls/remote_clusters_security.ts b/x-pack/test/functional/apps/remote_clusters/feature_controls/remote_clusters_security.ts
index b1edc74607161..e6b5a7ac77d7c 100644
--- a/x-pack/test/functional/apps/remote_clusters/feature_controls/remote_clusters_security.ts
+++ b/x-pack/test/functional/apps/remote_clusters/feature_controls/remote_clusters_security.ts
@@ -13,7 +13,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
   const appsMenu = getService('appsMenu');
   const managementMenu = getService('managementMenu');
 
-  describe('security', () => {
+  // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/89181
+  describe.skip('security', () => {
     before(async () => {
       await esArchiver.load('empty_kibana');
       await PageObjects.common.navigateToApp('home');

From edb5e35151a8fb5b168cb600cf3fdda94407177e Mon Sep 17 00:00:00 2001
From: Alison Goryachev <alison.goryachev@elastic.co>
Date: Mon, 25 Jan 2021 13:26:53 -0500
Subject: [PATCH 32/46] [Painless Lab] Update server to use new es-js client
 (#88704)

---
 .../errors/handle_es_error.ts                 | 12 ++++-
 .../painless_lab/server/routes/api/execute.ts | 23 ++++-----
 .../painless_lab/server/shared_imports.ts     |  2 +-
 x-pack/test/api_integration/apis/index.ts     |  1 +
 .../apis/painless_lab/index.ts                | 13 +++++
 .../apis/painless_lab/painless_lab.ts         | 49 +++++++++++++++++++
 6 files changed, 87 insertions(+), 13 deletions(-)
 create mode 100644 x-pack/test/api_integration/apis/painless_lab/index.ts
 create mode 100644 x-pack/test/api_integration/apis/painless_lab/painless_lab.ts

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<any>;
 }
 
 /*
  * 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/x-pack/plugins/painless_lab/server/routes/api/execute.ts b/x-pack/plugins/painless_lab/server/routes/api/execute.ts
index 7bb2d9f93c6fc..44529d7bacbd4 100644
--- a/x-pack/plugins/painless_lab/server/routes/api/execute.ts
+++ b/x-pack/plugins/painless_lab/server/routes/api/execute.ts
@@ -7,7 +7,7 @@ import { schema } from '@kbn/config-schema';
 
 import { API_BASE_PATH } from '../../../common/constants';
 import { RouteDependencies } from '../../types';
-import { isEsError } from '../../shared_imports';
+import { handleEsError } from '../../shared_imports';
 
 const bodySchema = schema.string();
 
@@ -23,23 +23,24 @@ export function registerExecuteRoute({ router, license }: RouteDependencies) {
       const body = req.body;
 
       try {
-        const callAsCurrentUser = ctx.core.elasticsearch.legacy.client.callAsCurrentUser;
-        const response = await callAsCurrentUser('scriptsPainlessExecute', {
+        const client = ctx.core.elasticsearch.client.asCurrentUser;
+        const response = await client.scriptsPainlessExecute({
           body,
         });
 
         return res.ok({
-          body: response,
+          body: response.body,
         });
-      } catch (e) {
-        if (isEsError(e)) {
-          // Assume invalid painless script was submitted
-          // Return 200 with error object
+      } catch (error) {
+        // Assume invalid painless script was submitted
+        // Return 200 with error object
+        const handleCustomError = () => {
           return res.ok({
-            body: e.body,
+            body: error.body,
           });
-        }
-        return res.internalError({ body: e });
+        };
+
+        return handleEsError({ error, response: res, handleCustomError });
       }
     })
   );
diff --git a/x-pack/plugins/painless_lab/server/shared_imports.ts b/x-pack/plugins/painless_lab/server/shared_imports.ts
index 454beda5394c7..068cddcee4c86 100644
--- a/x-pack/plugins/painless_lab/server/shared_imports.ts
+++ b/x-pack/plugins/painless_lab/server/shared_imports.ts
@@ -4,4 +4,4 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-export { isEsError } from '../../../../src/plugins/es_ui_shared/server';
+export { handleEsError } from '../../../../src/plugins/es_ui_shared/server';
diff --git a/x-pack/test/api_integration/apis/index.ts b/x-pack/test/api_integration/apis/index.ts
index 2cd2654cffe3e..ea42b583fc00e 100644
--- a/x-pack/test/api_integration/apis/index.ts
+++ b/x-pack/test/api_integration/apis/index.ts
@@ -34,5 +34,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
     loadTestFile(require.resolve('./lists'));
     loadTestFile(require.resolve('./upgrade_assistant'));
     loadTestFile(require.resolve('./searchprofiler'));
+    loadTestFile(require.resolve('./painless_lab'));
   });
 }
diff --git a/x-pack/test/api_integration/apis/painless_lab/index.ts b/x-pack/test/api_integration/apis/painless_lab/index.ts
new file mode 100644
index 0000000000000..7ccd7c5691e56
--- /dev/null
+++ b/x-pack/test/api_integration/apis/painless_lab/index.ts
@@ -0,0 +1,13 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export default function ({ loadTestFile }: FtrProviderContext) {
+  describe('Painless Lab', () => {
+    loadTestFile(require.resolve('./painless_lab'));
+  });
+}
diff --git a/x-pack/test/api_integration/apis/painless_lab/painless_lab.ts b/x-pack/test/api_integration/apis/painless_lab/painless_lab.ts
new file mode 100644
index 0000000000000..c78ceaaa81437
--- /dev/null
+++ b/x-pack/test/api_integration/apis/painless_lab/painless_lab.ts
@@ -0,0 +1,49 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import expect from '@kbn/expect';
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+const API_BASE_PATH = '/api/painless_lab';
+
+export default function ({ getService }: FtrProviderContext) {
+  const supertest = getService('supertest');
+
+  describe('Painless Lab', function () {
+    describe('Execute', () => {
+      it('should execute a valid painless script', async () => {
+        const script =
+          '"{\\n  \\"script\\": {\\n    \\"source\\": \\"return true;\\",\\n    \\"params\\": {\\n  \\"string_parameter\\": \\"string value\\",\\n  \\"number_parameter\\": 1.5,\\n  \\"boolean_parameter\\": true\\n}\\n  }\\n}"';
+
+        const { body } = await supertest
+          .post(`${API_BASE_PATH}/execute`)
+          .set('kbn-xsrf', 'xxx')
+          .set('Content-Type', 'application/json;charset=UTF-8')
+          .send(script)
+          .expect(200);
+
+        expect(body).to.eql({
+          result: 'true',
+        });
+      });
+
+      it('should return error response for invalid painless script', async () => {
+        const invalidScript =
+          '"{\\n  \\"script\\": {\\n    \\"source\\": \\"foobar\\",\\n    \\"params\\": {\\n  \\"string_parameter\\": \\"string value\\",\\n  \\"number_parameter\\": 1.5,\\n  \\"boolean_parameter\\": true\\n}\\n  }\\n}"';
+
+        const { body } = await supertest
+          .post(`${API_BASE_PATH}/execute`)
+          .set('kbn-xsrf', 'xxx')
+          .set('Content-Type', 'application/json;charset=UTF-8')
+          .send(invalidScript)
+          .expect(200);
+
+        expect(body.error).to.not.be(undefined);
+        expect(body.error.reason).to.eql('compile error');
+      });
+    });
+  });
+}

From 7d50fbbf6d8ab748e439503a554014144c6a7f4c Mon Sep 17 00:00:00 2001
From: Brian Seeders <brian.seeders@elastic.co>
Date: Mon, 25 Jan 2021 13:57:56 -0500
Subject: [PATCH 33/46] [CI] [TeamCity] Build master and 7.x hourly, others
 daily/on merge (#89184)

---
 .teamcity/src/Common.kt           |  8 +++++++
 .teamcity/src/builds/DailyCi.kt   | 37 +++++++++++++++++++++++++++++++
 .teamcity/src/builds/OnMergeCi.kt | 34 ++++++++++++++++++++++++++++
 .teamcity/src/projects/Kibana.kt  | 12 +++++++++-
 4 files changed, 90 insertions(+), 1 deletion(-)
 create mode 100644 .teamcity/src/builds/DailyCi.kt
 create mode 100644 .teamcity/src/builds/OnMergeCi.kt

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/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)
     }
 

From 15a45e8c38ac4b371f700285ca35591f918e3ddf Mon Sep 17 00:00:00 2001
From: Nathan Reese <reese.nathan@gmail.com>
Date: Mon, 25 Jan 2021 12:25:52 -0700
Subject: [PATCH 34/46] [Maps] fix users without access to Maps should not have
 the option to create them (#88830)

* [Maps] fix users without access to Maps should not have the option to create them

* fix test message

* wrap add geo field trigger in show capabilities check

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
 x-pack/plugins/maps/public/plugin.ts          | 10 ++-
 .../apps/maps/visualize_create_menu.js        | 82 ++++++++++++-------
 2 files changed, 62 insertions(+), 30 deletions(-)

diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts
index 8bffea3ce5a87..690002a771601 100644
--- a/x-pack/plugins/maps/public/plugin.ts
+++ b/x-pack/plugins/maps/public/plugin.ts
@@ -160,11 +160,19 @@ export class MapsPlugin
 
   public start(core: CoreStart, plugins: MapsPluginStartDependencies): MapsStartApi {
     setLicensingPluginStart(plugins.licensing);
-    plugins.uiActions.addTriggerAction(VISUALIZE_GEO_FIELD_TRIGGER, visualizeGeoFieldAction);
     setStartServices(core, plugins);
+
     // unregisters the OSS alias
     plugins.visualizations.unRegisterAlias(PLUGIN_ID_OSS);
 
+    if (core.application.capabilities.maps.show) {
+      plugins.uiActions.addTriggerAction(VISUALIZE_GEO_FIELD_TRIGGER, visualizeGeoFieldAction);
+    }
+
+    if (!core.application.capabilities.maps.save) {
+      plugins.visualizations.unRegisterAlias(APP_ID);
+    }
+
     return {
       createLayerDescriptors,
       registerLayerWizard,
diff --git a/x-pack/test/functional/apps/maps/visualize_create_menu.js b/x-pack/test/functional/apps/maps/visualize_create_menu.js
index 549901884d39b..f58856752dcd2 100644
--- a/x-pack/test/functional/apps/maps/visualize_create_menu.js
+++ b/x-pack/test/functional/apps/maps/visualize_create_menu.js
@@ -12,42 +12,66 @@ export default function ({ getService, getPageObjects }) {
   const security = getService('security');
 
   describe('visualize create menu', () => {
-    before(async () => {
-      await security.testUser.setRoles(
-        ['test_logstash_reader', 'global_maps_all', 'geoshape_data_reader', 'global_visualize_all'],
-        false
-      );
+    describe('maps visualize alias', () => {
+      describe('with write permission', () => {
+        before(async () => {
+          await security.testUser.setRoles(['global_maps_all', 'global_visualize_all'], false);
 
-      await PageObjects.visualize.navigateToNewVisualization();
-    });
+          await PageObjects.visualize.navigateToNewVisualization();
+        });
 
-    after(async () => {
-      await security.testUser.restoreDefaults();
-    });
+        it('should show maps application in create menu', async () => {
+          const hasMapsApp = await PageObjects.visualize.hasMapsApp();
+          expect(hasMapsApp).to.equal(true);
+        });
 
-    it('should show maps application in create menu', async () => {
-      const hasMapsApp = await PageObjects.visualize.hasMapsApp();
-      expect(hasMapsApp).to.equal(true);
-    });
+        it('should take users to Maps application when Maps is clicked', async () => {
+          await PageObjects.visualize.clickMapsApp();
+          await PageObjects.header.waitUntilLoadingHasFinished();
+          await PageObjects.maps.waitForLayersToLoad();
+          const doesLayerExist = await PageObjects.maps.doesLayerExist('Road map');
+          expect(doesLayerExist).to.equal(true);
+        });
+      });
 
-    it('should not show legacy region map visualizion in create menu', async () => {
-      await PageObjects.visualize.clickAggBasedVisualizations();
-      const hasLegecyViz = await PageObjects.visualize.hasRegionMap();
-      expect(hasLegecyViz).to.equal(false);
-    });
+      describe('without write permission', () => {
+        before(async () => {
+          await security.testUser.setRoles(['global_maps_read', 'global_visualize_all'], false);
+
+          await PageObjects.visualize.navigateToNewVisualization();
+        });
 
-    it('should not show legacy tilemap map visualizion in create menu', async () => {
-      const hasLegecyViz = await PageObjects.visualize.hasTileMap();
-      expect(hasLegecyViz).to.equal(false);
+        after(async () => {
+          await security.testUser.restoreDefaults();
+        });
+
+        it('should not show maps application in create menu', async () => {
+          const hasMapsApp = await PageObjects.visualize.hasMapsApp();
+          expect(hasMapsApp).to.equal(false);
+        });
+      });
     });
 
-    it('should take users to Maps application when Maps is clicked', async () => {
-      await PageObjects.visualize.goBackToGroups();
-      await PageObjects.visualize.clickMapsApp();
-      await PageObjects.header.waitUntilLoadingHasFinished();
-      await PageObjects.maps.waitForLayersToLoad();
-      const doesLayerExist = await PageObjects.maps.doesLayerExist('Road map');
-      expect(doesLayerExist).to.equal(true);
+    describe('aggregion based visualizations', () => {
+      before(async () => {
+        await security.testUser.setRoles(['global_visualize_all'], false);
+
+        await PageObjects.visualize.navigateToNewAggBasedVisualization();
+      });
+
+      after(async () => {
+        await security.testUser.restoreDefaults();
+      });
+
+      it('should not show legacy region map visualizion in create menu', async () => {
+        const hasLegecyViz = await PageObjects.visualize.hasRegionMap();
+        expect(hasLegecyViz).to.equal(false);
+      });
+
+      it('should not show legacy tilemap map visualizion in create menu', async () => {
+        const hasLegecyViz = await PageObjects.visualize.hasTileMap();
+        expect(hasLegecyViz).to.equal(false);
+      });
     });
   });
 }

From 62b32c0a2a0dc544a8c35462e00d5a278c11574b Mon Sep 17 00:00:00 2001
From: Devon Thomson <devon.thomson@hotmail.com>
Date: Mon, 25 Jan 2021 14:28:26 -0500
Subject: [PATCH 35/46] Remove onAppLeave in useEffectCleanup to prevent
 confirm when leaving from listing page (#89041)

---
 .../public/application/components/visualize_top_nav.tsx       | 4 ++++
 1 file changed, 4 insertions(+)

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,

From e5588a129b1a0b2796822d4773176cc712dd5318 Mon Sep 17 00:00:00 2001
From: Nathan L Smith <nathan.smith@elastic.co>
Date: Mon, 25 Jan 2021 13:48:35 -0600
Subject: [PATCH 36/46] Move EUI styled components integration to kibana_react
 (#86065)

...from xpack_legacy.

Remove the duplicated typings from the observability plugin and only use the ones from kibana_react.

Fixes #78248.
---
 .../common/eui_styled_components.tsx          |  6 ++-
 src/plugins/kibana_react/common/index.ts      |  9 ++++
 src/plugins/kibana_react/kibana.json          |  3 +-
 src/plugins/kibana_react/tsconfig.json        |  9 +---
 .../apm/common/service_health_status.ts       |  3 +-
 .../ExceptionStacktrace.stories.tsx           |  2 +-
 .../__stories__/MapTooltip.stories.tsx        |  2 +-
 .../app/RumDashboard/utils/test_helper.tsx    |  2 +-
 .../ServiceMap/Popover/Popover.stories.tsx    |  2 +-
 .../Popover/service_stats_list.stories.tsx    |  2 +-
 .../__stories__/Cytoscape.stories.tsx         |  2 +-
 .../cytoscape_example_data.stories.tsx        |  2 +-
 .../app/ServiceMap/cytoscape_options.ts       |  2 +-
 .../components/app/ServiceMap/index.test.tsx  |  4 +-
 .../use_cytoscape_event_handlers.test.tsx     |  3 +-
 .../use_cytoscape_event_handlers.ts           |  3 +-
 .../index.stories.tsx                         |  2 +-
 .../TransactionDetails/Distribution/index.tsx |  2 +-
 .../WaterfallContainer.stories.tsx            |  2 +-
 .../service_inventory.test.tsx                |  4 +-
 .../Timeline/Marker/AgentMarker.test.tsx      |  2 +-
 .../shared/time_comparison/index.test.tsx     |  2 +-
 x-pack/plugins/apm/public/hooks/use_theme.tsx |  2 +-
 .../selectors/latency_chart_selector.test.ts  |  2 +-
 .../selectors/latency_chart_selectors.ts      |  2 +-
 .../throuput_chart_selectors.test.ts          |  2 +-
 .../selectors/throuput_chart_selectors.ts     |  2 +-
 .../plugins/apm/public/utils/testHelpers.tsx  |  2 +-
 .../fleet/public/applications/fleet/app.tsx   |  2 +-
 .../inventory_models/aws_ec2/layout.tsx       |  3 +-
 .../inventory_models/aws_rds/layout.tsx       |  3 +-
 .../common/inventory_models/aws_s3/layout.tsx |  3 +-
 .../inventory_models/aws_sqs/layout.tsx       |  3 +-
 .../inventory_models/container/layout.tsx     |  3 +-
 .../common/inventory_models/host/layout.tsx   |  3 +-
 .../common/inventory_models/pod/layout.tsx    |  3 +-
 .../inventory_models/shared/layouts/aws.tsx   |  3 +-
 .../inventory_models/shared/layouts/nginx.tsx |  3 +-
 .../inventory/components/expression.tsx       |  2 +-
 .../components/expression_row.tsx             |  2 +-
 .../infra/public/apps/common_providers.tsx    |  2 +-
 .../autocomplete_field/autocomplete_field.tsx |  2 +-
 .../autocomplete_field/suggestion_item.tsx    |  2 +-
 .../components/centered_flyout_body.tsx       |  2 +-
 .../data_search_error_callout.stories.tsx     |  2 +-
 .../data_search_progress.stories.tsx          |  2 +-
 .../components/empty_states/no_data.tsx       |  2 +-
 .../components/empty_states/no_indices.tsx    |  2 +-
 .../infra/public/components/error_page.tsx    |  2 +-
 .../public/components/eui/toolbar/toolbar.tsx |  2 +-
 .../public/components/fixed_datepicker.tsx    |  2 +-
 .../infra/public/components/loading/index.tsx |  2 +-
 .../components/loading_overlay_wrapper.tsx    |  2 +-
 .../public/components/log_stream/index.tsx    |  2 +-
 .../log_stream/log_stream.stories.mdx         |  2 +-
 .../quality_warning_notices.stories.tsx       |  2 +-
 .../quality_warning_notices.tsx               |  2 +-
 .../initial_configuration_step.stories.tsx    |  2 +-
 .../missing_results_privileges_prompt.tsx     |  2 +-
 .../missing_setup_privileges_prompt.tsx       |  2 +-
 .../ml_unavailable_prompt.tsx                 |  2 +-
 .../logging/log_analysis_setup/setup_page.tsx |  2 +-
 .../setup_status_unknown_prompt.tsx           |  2 +-
 .../subscription_splash_content.tsx           |  2 +-
 .../logging/log_customization_menu.tsx        |  2 +-
 .../log_entry_examples/log_entry_examples.tsx |  2 +-
 .../logging/log_highlights_menu.tsx           |  2 +-
 .../logging/log_minimap/density_chart.tsx     |  2 +-
 .../log_minimap/highlighted_interval.tsx      |  2 +-
 .../logging/log_minimap/log_minimap.tsx       |  2 +-
 .../logging/log_minimap/search_marker.tsx     |  2 +-
 .../logging/log_minimap/time_ruler.tsx        |  2 +-
 .../log_search_controls/log_search_input.tsx  |  2 +-
 .../components/logging/log_statusbar.tsx      |  2 +-
 .../log_text_stream/column_headers.tsx        |  2 +-
 .../logging/log_text_stream/field_value.tsx   |  2 +-
 .../logging/log_text_stream/highlighting.tsx  |  2 +-
 .../logging/log_text_stream/jump_to_tail.tsx  |  2 +-
 .../log_text_stream/loading_item_view.tsx     |  2 +-
 .../log_text_stream/log_entry_column.tsx      |  2 +-
 .../log_entry_context_menu.tsx                |  2 +-
 .../log_entry_field_column.test.tsx           |  2 +-
 .../log_entry_field_column.tsx                |  2 +-
 .../log_entry_message_column.test.tsx         |  2 +-
 .../log_entry_message_column.tsx              |  2 +-
 .../logging/log_text_stream/log_entry_row.tsx |  3 +-
 .../log_entry_timestamp_column.tsx            |  2 +-
 .../scrollable_log_text_stream_view.tsx       |  2 +-
 .../logging/log_text_stream/text_styles.tsx   |  2 +-
 .../log_text_stream/vertical_scroll_panel.tsx |  2 +-
 .../components/navigation/app_navigation.tsx  |  2 +-
 .../components/navigation/routed_tabs.tsx     |  2 +-
 .../plugins/infra/public/components/page.tsx  |  2 +-
 .../infra/public/components/toolbar_panel.ts  |  2 +-
 x-pack/plugins/infra/public/pages/error.tsx   |  2 +-
 .../page_results_content.tsx                  |  3 +-
 .../top_categories/category_expression.tsx    |  2 +-
 .../sections/top_categories/datasets_list.tsx |  2 +-
 .../single_metric_comparison.tsx              |  2 +-
 .../top_categories/top_categories_table.tsx   |  2 +-
 .../log_entry_rate/page_results_content.tsx   |  3 +-
 .../sections/anomalies/expanded_row.tsx       |  2 +-
 .../sections/anomalies/index.tsx              |  2 +-
 .../sections/anomalies/log_entry_example.tsx  |  2 +-
 .../logs/settings/add_log_column_popover.tsx  |  2 +-
 .../pages/logs/stream/page_logs_content.tsx   |  2 +-
 .../logs/stream/page_view_log_in_context.tsx  |  2 +-
 .../components/bottom_drawer.tsx              |  4 +-
 .../components/dropdown_button.tsx            |  2 +-
 .../inventory_view/components/layout.tsx      |  2 +-
 .../subscription_splash_content.tsx           |  2 +-
 .../components/node_details/overlay.tsx       |  2 +-
 .../tabs/processes/process_row.tsx            |  2 +-
 .../tabs/processes/process_row_charts.tsx     |  2 +-
 .../tabs/processes/processes_table.tsx        |  2 +-
 .../tabs/processes/summary_table.tsx          |  2 +-
 .../node_details/tabs/properties/index.tsx    |  2 +-
 .../components/node_details/tabs/shared.tsx   |  2 +-
 .../components/nodes_overview.tsx             |  2 +-
 .../components/timeline/timeline.tsx          |  2 +-
 .../waffle/conditional_tooltip.test.tsx       |  2 +-
 .../components/waffle/conditional_tooltip.tsx |  2 +-
 .../components/waffle/gradient_legend.tsx     |  2 +-
 .../components/waffle/group_name.tsx          |  2 +-
 .../components/waffle/group_of_groups.tsx     |  2 +-
 .../components/waffle/group_of_nodes.tsx      |  2 +-
 .../components/waffle/legend.tsx              |  2 +-
 .../components/waffle/legend_controls.tsx     |  2 +-
 .../inventory_view/components/waffle/map.tsx  |  2 +-
 .../metric_control/custom_metric_form.tsx     |  2 +-
 .../metric_control/metrics_edit_mode.tsx      |  2 +-
 .../waffle/metric_control/mode_switcher.tsx   |  2 +-
 .../inventory_view/components/waffle/node.tsx |  2 +-
 .../components/waffle/node_context_menu.tsx   |  3 +-
 .../components/waffle/palette_preview.tsx     |  2 +-
 .../waffle/stepped_gradient_legend.tsx        |  2 +-
 .../components/waffle/steps_legend.tsx        |  2 +-
 .../waffle/waffle_group_by_controls.tsx       |  2 +-
 .../waffle/waffle_sort_controls.tsx           |  2 +-
 .../waffle/waffle_time_controls.tsx           |  2 +-
 .../components/gauges_section_vis.tsx         |  2 +-
 .../metric_detail/components/invalid_node.tsx |  2 +-
 .../components/layout_content.tsx             |  2 +-
 .../components/metadata_details.tsx           |  2 +-
 .../components/node_details_page.tsx          |  2 +-
 .../metric_detail/components/side_nav.tsx     |  2 +-
 .../components/time_controls.tsx              |  2 +-
 .../pages/metrics/metric_detail/index.tsx     |  6 ++-
 .../pages/metrics/metric_detail/types.ts      |  2 +-
 .../metrics_explorer/components/chart.tsx     |  2 +-
 .../public/application/index.tsx              |  2 +-
 .../__stories__/core_vitals.stories.tsx       |  2 +-
 .../observability/public/hooks/use_theme.tsx  |  2 +-
 .../pages/overview/overview.stories.tsx       |  2 +-
 .../public/typings/eui_styled_components.tsx  | 47 -------------------
 .../observability/public/typings/index.ts     |  1 -
 .../public/utils/test_helper.tsx              |  2 +-
 .../mock/endpoint/app_root_provider.tsx       |  2 +-
 .../pages/policy/view/vertical_divider.ts     |  2 +-
 .../plugins/uptime/public/apps/uptime_app.tsx |  2 +-
 .../ping_list/columns/ping_timestamp.tsx      |  3 +-
 .../synthetics/waterfall/components/styles.ts |  2 +-
 .../waterfall/components/waterfall.test.tsx   |  2 +-
 .../kuery_bar/typeahead/suggestion.tsx        |  2 +-
 .../kuery_bar/typeahead/suggestions.tsx       |  2 +-
 .../columns/monitor_status_column.test.tsx    |  2 +-
 .../columns/monitor_status_column.tsx         |  2 +-
 .../monitor_list/monitor_list.test.tsx        |  2 +-
 .../uptime/public/lib/helper/rtl_helpers.tsx  |  2 +-
 x-pack/plugins/xpack_legacy/common/index.ts   |  7 ---
 170 files changed, 193 insertions(+), 242 deletions(-)
 rename {x-pack/plugins/xpack_legacy => src/plugins/kibana_react}/common/eui_styled_components.tsx (86%)
 create mode 100644 src/plugins/kibana_react/common/index.ts
 delete mode 100644 x-pack/plugins/observability/public/typings/eui_styled_components.tsx
 delete mode 100644 x-pack/plugins/xpack_legacy/common/index.ts

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/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/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/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/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/fleet/public/applications/fleet/app.tsx b/x-pack/plugins/fleet/public/applications/fleet/app.tsx
index ed91c1cb1479c..21372508ee99d 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/app.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/app.tsx
@@ -36,7 +36,7 @@ import { ProtectedRoute } from './index';
 import { FleetConfigType, FleetStartServices } from '../../plugin';
 import { UIExtensionsStorage } from './types';
 import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
-import { EuiThemeProvider } from '../../../../xpack_legacy/common';
+import { EuiThemeProvider } from '../../../../../../src/plugins/kibana_react/common';
 import { UIExtensionsContext } from './hooks/use_ui_extension';
 
 const ErrorLayout = ({ children }: { children: JSX.Element }) => (
diff --git a/x-pack/plugins/infra/common/inventory_models/aws_ec2/layout.tsx b/x-pack/plugins/infra/common/inventory_models/aws_ec2/layout.tsx
index 9494e4aa396a5..348fb8b691b86 100644
--- a/x-pack/plugins/infra/common/inventory_models/aws_ec2/layout.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/aws_ec2/layout.tsx
@@ -15,8 +15,7 @@ import { SubSection } from '../../../public/pages/metrics/metric_detail/componen
 import { LayoutContent } from '../../../public/pages/metrics/metric_detail/components/layout_content';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { withTheme } from '../../../../observability/public';
+import { withTheme } from '../../../../../../src/plugins/kibana_react/common';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { MetadataDetails } from '../../../public/pages/metrics/metric_detail/components/metadata_details';
 
diff --git a/x-pack/plugins/infra/common/inventory_models/aws_rds/layout.tsx b/x-pack/plugins/infra/common/inventory_models/aws_rds/layout.tsx
index 08b865f01b06c..45395eff1f823 100644
--- a/x-pack/plugins/infra/common/inventory_models/aws_rds/layout.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/aws_rds/layout.tsx
@@ -13,8 +13,7 @@ import { Section } from '../../../public/pages/metrics/metric_detail/components/
 import { SubSection } from '../../../public/pages/metrics/metric_detail/components/sub_section';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { withTheme } from '../../../../observability/public';
+import { withTheme } from '../../../../../../src/plugins/kibana_react/common';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutContent } from '../../../public/pages/metrics/metric_detail/components/layout_content';
 
diff --git a/x-pack/plugins/infra/common/inventory_models/aws_s3/layout.tsx b/x-pack/plugins/infra/common/inventory_models/aws_s3/layout.tsx
index e16f8ef6addde..cd78ed56d4bc9 100644
--- a/x-pack/plugins/infra/common/inventory_models/aws_s3/layout.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/aws_s3/layout.tsx
@@ -13,8 +13,7 @@ import { Section } from '../../../public/pages/metrics/metric_detail/components/
 import { SubSection } from '../../../public/pages/metrics/metric_detail/components/sub_section';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { withTheme } from '../../../../observability/public';
+import { withTheme } from '../../../../../../src/plugins/kibana_react/common';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutContent } from '../../../public/pages/metrics/metric_detail/components/layout_content';
 
diff --git a/x-pack/plugins/infra/common/inventory_models/aws_sqs/layout.tsx b/x-pack/plugins/infra/common/inventory_models/aws_sqs/layout.tsx
index ff13f2db104de..7f3b4a6d73bf8 100644
--- a/x-pack/plugins/infra/common/inventory_models/aws_sqs/layout.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/aws_sqs/layout.tsx
@@ -13,8 +13,7 @@ import { Section } from '../../../public/pages/metrics/metric_detail/components/
 import { SubSection } from '../../../public/pages/metrics/metric_detail/components/sub_section';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { withTheme } from '../../../../observability/public';
+import { withTheme } from '../../../../../../src/plugins/kibana_react/common';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutContent } from '../../../public/pages/metrics/metric_detail/components/layout_content';
 
diff --git a/x-pack/plugins/infra/common/inventory_models/container/layout.tsx b/x-pack/plugins/infra/common/inventory_models/container/layout.tsx
index b9366a43e40c6..fc4293e698813 100644
--- a/x-pack/plugins/infra/common/inventory_models/container/layout.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/container/layout.tsx
@@ -15,8 +15,7 @@ import { SubSection } from '../../../public/pages/metrics/metric_detail/componen
 import { GaugesSectionVis } from '../../../public/pages/metrics/metric_detail/components/gauges_section_vis';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { withTheme } from '../../../../observability/public';
+import { withTheme } from '../../../../../../src/plugins/kibana_react/common';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutContent } from '../../../public/pages/metrics/metric_detail/components/layout_content';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
diff --git a/x-pack/plugins/infra/common/inventory_models/host/layout.tsx b/x-pack/plugins/infra/common/inventory_models/host/layout.tsx
index e23118c747a9b..c238c813c8691 100644
--- a/x-pack/plugins/infra/common/inventory_models/host/layout.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/host/layout.tsx
@@ -5,8 +5,7 @@
  */
 import React from 'react';
 import { i18n } from '@kbn/i18n';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { withTheme } from '../../../../observability/public';
+import { withTheme } from '../../../../../../src/plugins/kibana_react/common';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutPropsWithTheme } from '../../../public/pages/metrics/metric_detail/types';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
diff --git a/x-pack/plugins/infra/common/inventory_models/pod/layout.tsx b/x-pack/plugins/infra/common/inventory_models/pod/layout.tsx
index 271e32556ae28..255b9483f70c5 100644
--- a/x-pack/plugins/infra/common/inventory_models/pod/layout.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/pod/layout.tsx
@@ -15,8 +15,7 @@ import { SubSection } from '../../../public/pages/metrics/metric_detail/componen
 import { GaugesSectionVis } from '../../../public/pages/metrics/metric_detail/components/gauges_section_vis';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { withTheme } from '../../../../observability/public';
+import { withTheme } from '../../../../../../src/plugins/kibana_react/common';
 import * as Nginx from '../shared/layouts/nginx';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { MetadataDetails } from '../../../public/pages/metrics/metric_detail/components/metadata_details';
diff --git a/x-pack/plugins/infra/common/inventory_models/shared/layouts/aws.tsx b/x-pack/plugins/infra/common/inventory_models/shared/layouts/aws.tsx
index 6f2791534c17e..f8e20ee6404f6 100644
--- a/x-pack/plugins/infra/common/inventory_models/shared/layouts/aws.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/shared/layouts/aws.tsx
@@ -15,8 +15,7 @@ import { SubSection } from '../../../../public/pages/metrics/metric_detail/compo
 import { GaugesSectionVis } from '../../../../public/pages/metrics/metric_detail/components/gauges_section_vis';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ChartSectionVis } from '../../../../public/pages/metrics/metric_detail/components/chart_section_vis';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { withTheme } from '../../../../../observability/public';
+import { withTheme } from '../../../../../../../src/plugins/kibana_react/common';
 
 export const Layout = withTheme(({ metrics, onChangeRangeTime, theme }: LayoutPropsWithTheme) => (
   <React.Fragment>
diff --git a/x-pack/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx b/x-pack/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx
index cf3a06994cc96..3ec17d96043a5 100644
--- a/x-pack/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx
@@ -13,8 +13,7 @@ import { Section } from '../../../../public/pages/metrics/metric_detail/componen
 import { SubSection } from '../../../../public/pages/metrics/metric_detail/components/sub_section';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ChartSectionVis } from '../../../../public/pages/metrics/metric_detail/components/chart_section_vis';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { withTheme } from '../../../../../observability/public';
+import { withTheme } from '../../../../../../../src/plugins/kibana_react/common';
 
 export const Layout = withTheme(({ metrics, onChangeRangeTime, theme }: LayoutPropsWithTheme) => (
   <React.Fragment>
diff --git a/x-pack/plugins/infra/public/alerting/inventory/components/expression.tsx b/x-pack/plugins/infra/public/alerting/inventory/components/expression.tsx
index 3dc754822879d..f8102d9ead2cb 100644
--- a/x-pack/plugins/infra/public/alerting/inventory/components/expression.tsx
+++ b/x-pack/plugins/infra/public/alerting/inventory/components/expression.tsx
@@ -30,7 +30,7 @@ import {
   Comparator,
   // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 } from '../../../../server/lib/alerting/metric_threshold/types';
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import {
   ThresholdExpression,
   ForLastExpression,
diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx
index 1487557bde3a0..cdab53c92d32c 100644
--- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx
+++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx
@@ -14,7 +14,7 @@ import {
   ThresholdExpression,
   // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 } from '../../../../../triggers_actions_ui/public/common';
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { IErrorObject } from '../../../../../triggers_actions_ui/public/types';
 import { MetricExpression, AGGREGATION_TYPES } from '../types';
diff --git a/x-pack/plugins/infra/public/apps/common_providers.tsx b/x-pack/plugins/infra/public/apps/common_providers.tsx
index 41aa2fd898585..ebfa412410dd7 100644
--- a/x-pack/plugins/infra/public/apps/common_providers.tsx
+++ b/x-pack/plugins/infra/public/apps/common_providers.tsx
@@ -11,7 +11,7 @@ import {
   useUiSetting$,
   KibanaContextProvider,
 } from '../../../../../src/plugins/kibana_react/public';
-import { EuiThemeProvider } from '../../../observability/public';
+import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common';
 import { TriggersAndActionsUIPublicPluginStart } from '../../../triggers_actions_ui/public';
 import { createKibanaContextForPlugin } from '../hooks/use_kibana';
 import { InfraClientStartDeps } from '../types';
diff --git a/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx b/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx
index 08594c5ddba18..757a1a1e80ef3 100644
--- a/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx
+++ b/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx
@@ -7,7 +7,7 @@
 import { EuiFieldSearch, EuiOutsideClickDetector, EuiPanel } from '@elastic/eui';
 import React from 'react';
 import { QuerySuggestion } from '../../../../../../src/plugins/data/public';
-import { euiStyled } from '../../../../observability/public';
+import { euiStyled } from '../../../../../../src/plugins/kibana_react/common';
 import { composeStateUpdaters } from '../../utils/typed_react';
 import { SuggestionItem } from './suggestion_item';
 
diff --git a/x-pack/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx b/x-pack/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx
index 4bcb7a7ec8a02..78b36d4e047e6 100644
--- a/x-pack/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx
+++ b/x-pack/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx
@@ -7,7 +7,7 @@
 import { EuiIcon } from '@elastic/eui';
 import { transparentize } from 'polished';
 import React from 'react';
-import { euiStyled } from '../../../../observability/public';
+import { euiStyled } from '../../../../../../src/plugins/kibana_react/common';
 import { QuerySuggestion, QuerySuggestionTypes } from '../../../../../../src/plugins/data/public';
 
 interface Props {
diff --git a/x-pack/plugins/infra/public/components/centered_flyout_body.tsx b/x-pack/plugins/infra/public/components/centered_flyout_body.tsx
index ec762610f36c4..83d1d4de2b2fa 100644
--- a/x-pack/plugins/infra/public/components/centered_flyout_body.tsx
+++ b/x-pack/plugins/infra/public/components/centered_flyout_body.tsx
@@ -5,7 +5,7 @@
  */
 
 import { EuiFlyoutBody } from '@elastic/eui';
-import { euiStyled } from '../../../observability/public';
+import { euiStyled } from '../../../../../src/plugins/kibana_react/common';
 
 export const CenteredEuiFlyoutBody = euiStyled(EuiFlyoutBody)`
   & .euiFlyoutBody__overflow {
diff --git a/x-pack/plugins/infra/public/components/data_search_error_callout.stories.tsx b/x-pack/plugins/infra/public/components/data_search_error_callout.stories.tsx
index 4e46e5fdd3f45..32f9d86c1b904 100644
--- a/x-pack/plugins/infra/public/components/data_search_error_callout.stories.tsx
+++ b/x-pack/plugins/infra/public/components/data_search_error_callout.stories.tsx
@@ -7,7 +7,7 @@
 import { PropsOf } from '@elastic/eui';
 import { Meta, Story } from '@storybook/react/types-6-0';
 import React from 'react';
-import { EuiThemeProvider } from '../../../observability/public';
+import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common';
 import { DataSearchErrorCallout } from './data_search_error_callout';
 
 export default {
diff --git a/x-pack/plugins/infra/public/components/data_search_progress.stories.tsx b/x-pack/plugins/infra/public/components/data_search_progress.stories.tsx
index d5293a7282305..492aa865dfb61 100644
--- a/x-pack/plugins/infra/public/components/data_search_progress.stories.tsx
+++ b/x-pack/plugins/infra/public/components/data_search_progress.stories.tsx
@@ -7,7 +7,7 @@
 import { PropsOf } from '@elastic/eui';
 import { Meta, Story } from '@storybook/react/types-6-0';
 import React from 'react';
-import { EuiThemeProvider } from '../../../observability/public';
+import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common';
 import { DataSearchProgress } from './data_search_progress';
 
 export default {
diff --git a/x-pack/plugins/infra/public/components/empty_states/no_data.tsx b/x-pack/plugins/infra/public/components/empty_states/no_data.tsx
index 97dc7ac1f8520..e4a9d82fe8c36 100644
--- a/x-pack/plugins/infra/public/components/empty_states/no_data.tsx
+++ b/x-pack/plugins/infra/public/components/empty_states/no_data.tsx
@@ -7,7 +7,7 @@
 import { EuiButton, EuiEmptyPrompt } from '@elastic/eui';
 import React from 'react';
 
-import { euiStyled } from '../../../../observability/public';
+import { euiStyled } from '../../../../../../src/plugins/kibana_react/common';
 
 interface NoDataProps {
   titleText: string;
diff --git a/x-pack/plugins/infra/public/components/empty_states/no_indices.tsx b/x-pack/plugins/infra/public/components/empty_states/no_indices.tsx
index b453a4f0bc342..1e73c3abe338d 100644
--- a/x-pack/plugins/infra/public/components/empty_states/no_indices.tsx
+++ b/x-pack/plugins/infra/public/components/empty_states/no_indices.tsx
@@ -7,7 +7,7 @@
 import { EuiEmptyPrompt } from '@elastic/eui';
 import React from 'react';
 
-import { euiStyled } from '../../../../observability/public';
+import { euiStyled } from '../../../../../../src/plugins/kibana_react/common';
 
 interface NoIndicesProps {
   message: string;
diff --git a/x-pack/plugins/infra/public/components/error_page.tsx b/x-pack/plugins/infra/public/components/error_page.tsx
index 5c8b576c161d7..c800c75a77120 100644
--- a/x-pack/plugins/infra/public/components/error_page.tsx
+++ b/x-pack/plugins/infra/public/components/error_page.tsx
@@ -15,7 +15,7 @@ import {
 import { FormattedMessage } from '@kbn/i18n/react';
 import React from 'react';
 
-import { euiStyled } from '../../../observability/public';
+import { euiStyled } from '../../../../../src/plugins/kibana_react/common';
 import { FlexPage } from './page';
 
 interface Props {
diff --git a/x-pack/plugins/infra/public/components/eui/toolbar/toolbar.tsx b/x-pack/plugins/infra/public/components/eui/toolbar/toolbar.tsx
index 912550b90b9b9..9ac1996f94b97 100644
--- a/x-pack/plugins/infra/public/components/eui/toolbar/toolbar.tsx
+++ b/x-pack/plugins/infra/public/components/eui/toolbar/toolbar.tsx
@@ -6,7 +6,7 @@
 
 import { EuiPanel } from '@elastic/eui';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 
 export const Toolbar = euiStyled(EuiPanel).attrs(() => ({
   grow: false,
diff --git a/x-pack/plugins/infra/public/components/fixed_datepicker.tsx b/x-pack/plugins/infra/public/components/fixed_datepicker.tsx
index fcebb89561826..f4b34b65711cb 100644
--- a/x-pack/plugins/infra/public/components/fixed_datepicker.tsx
+++ b/x-pack/plugins/infra/public/components/fixed_datepicker.tsx
@@ -7,7 +7,7 @@
 import React from 'react';
 
 import { EuiDatePicker, EuiDatePickerProps } from '@elastic/eui';
-import { euiStyled } from '../../../observability/public';
+import { euiStyled } from '../../../../../src/plugins/kibana_react/common';
 
 export const FixedDatePicker = euiStyled(
   ({
diff --git a/x-pack/plugins/infra/public/components/loading/index.tsx b/x-pack/plugins/infra/public/components/loading/index.tsx
index 8c25dfc2380f9..e6a7aa88391f3 100644
--- a/x-pack/plugins/infra/public/components/loading/index.tsx
+++ b/x-pack/plugins/infra/public/components/loading/index.tsx
@@ -7,7 +7,7 @@
 import { EuiLoadingChart, EuiPanel, EuiText } from '@elastic/eui';
 import * as React from 'react';
 
-import { euiStyled } from '../../../../observability/public';
+import { euiStyled } from '../../../../../../src/plugins/kibana_react/common';
 
 interface InfraLoadingProps {
   text: string | JSX.Element;
diff --git a/x-pack/plugins/infra/public/components/loading_overlay_wrapper.tsx b/x-pack/plugins/infra/public/components/loading_overlay_wrapper.tsx
index 3b22ee24cee07..b849e24167697 100644
--- a/x-pack/plugins/infra/public/components/loading_overlay_wrapper.tsx
+++ b/x-pack/plugins/infra/public/components/loading_overlay_wrapper.tsx
@@ -8,7 +8,7 @@ import { EuiLoadingSpinner } from '@elastic/eui';
 import { transparentize } from 'polished';
 import React from 'react';
 
-import { euiStyled } from '../../../observability/public';
+import { euiStyled } from '../../../../../src/plugins/kibana_react/common';
 
 export const LoadingOverlayWrapper: React.FC<
   React.HTMLAttributes<HTMLDivElement> & {
diff --git a/x-pack/plugins/infra/public/components/log_stream/index.tsx b/x-pack/plugins/infra/public/components/log_stream/index.tsx
index 3d69b6a022987..b485a21221af2 100644
--- a/x-pack/plugins/infra/public/components/log_stream/index.tsx
+++ b/x-pack/plugins/infra/public/components/log_stream/index.tsx
@@ -6,7 +6,7 @@
 
 import React, { useMemo, useCallback, useEffect } from 'react';
 import { noop } from 'lodash';
-import { euiStyled } from '../../../../observability/public';
+import { euiStyled } from '../../../../../../src/plugins/kibana_react/common';
 
 import { LogEntryCursor } from '../../../common/log_entry';
 
diff --git a/x-pack/plugins/infra/public/components/log_stream/log_stream.stories.mdx b/x-pack/plugins/infra/public/components/log_stream/log_stream.stories.mdx
index d579432c6291f..bda52d9323eb6 100644
--- a/x-pack/plugins/infra/public/components/log_stream/log_stream.stories.mdx
+++ b/x-pack/plugins/infra/public/components/log_stream/log_stream.stories.mdx
@@ -2,7 +2,7 @@ import { Meta, Story, Canvas, ArgsTable } from '@storybook/addon-docs/blocks';
 import { Subject } from 'rxjs';
 
 import { I18nProvider } from '@kbn/i18n/react';
-import { EuiThemeProvider } from '../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../src/plugins/kibana_react/common';
 import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
 
 import { DEFAULT_SOURCE_CONFIGURATION } from '../../test_utils/source_configuration';
diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.stories.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.stories.tsx
index 7caf75417091a..d6c993b28514b 100644
--- a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.stories.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.stories.tsx
@@ -7,7 +7,7 @@
 import { action } from '@storybook/addon-actions';
 import { storiesOf } from '@storybook/react';
 import React from 'react';
-import { EuiThemeProvider } from '../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common';
 import { QualityWarning } from '../../../../common/log_analysis';
 import { CategoryQualityWarnings } from './quality_warning_notices';
 
diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.tsx
index 4bf618923a138..6503dc6110591 100644
--- a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.tsx
@@ -16,7 +16,7 @@ import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
 import { groupBy } from 'lodash';
 import React, { Fragment, useState } from 'react';
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import {
   CategoryQualityWarning,
   CategoryQualityWarningReason,
diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.stories.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.stories.tsx
index 79b3b9ce9bc6f..932b499430aa9 100644
--- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.stories.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.stories.tsx
@@ -7,7 +7,7 @@
 import { actions } from '@storybook/addon-actions';
 import { storiesOf } from '@storybook/react';
 import React from 'react';
-import { EuiThemeProvider } from '../../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common';
 import { InitialConfigurationStep } from './initial_configuration_step';
 
 storiesOf('infra/logAnalysis/SetupInitialConfigurationStep', module)
diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_results_privileges_prompt.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_results_privileges_prompt.tsx
index 3aa8b544b7b54..04414d43bfde4 100644
--- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_results_privileges_prompt.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_results_privileges_prompt.tsx
@@ -6,7 +6,7 @@
 
 import { EuiEmptyPrompt } from '@elastic/eui';
 import React from 'react';
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import {
   missingMlPrivilegesTitle,
   missingMlResultsPrivilegesDescription,
diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_setup_privileges_prompt.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_setup_privileges_prompt.tsx
index 6a5a1da890418..2535192681b8a 100644
--- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_setup_privileges_prompt.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_setup_privileges_prompt.tsx
@@ -6,7 +6,7 @@
 
 import { EuiEmptyPrompt } from '@elastic/eui';
 import React from 'react';
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import {
   missingMlPrivilegesTitle,
   missingMlSetupPrivilegesDescription,
diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx
index 4b15ce19ef096..f711e5100391d 100644
--- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx
@@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
 import React from 'react';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 
 export const MlUnavailablePrompt: React.FunctionComponent<{}> = () => (
   <EmptyPrompt
diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_page.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_page.tsx
index b4e1b7448640c..49039365d9ab8 100644
--- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_page.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_page.tsx
@@ -16,7 +16,7 @@ import {
 } from '@elastic/eui';
 import React from 'react';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 
 export const LogAnalysisSetupPage: React.FunctionComponent<CommonProps> = ({
   children,
diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx
index 334aaa251f524..615c78080c7ae 100644
--- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx
@@ -8,7 +8,7 @@ import React from 'react';
 import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
 import { EuiEmptyPrompt, EuiButton } from '@elastic/eui';
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 
 interface Props {
   retry: () => void;
diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/subscription_splash_content.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/subscription_splash_content.tsx
index 366d31897fe06..660f416e3b7b4 100644
--- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/subscription_splash_content.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/subscription_splash_content.tsx
@@ -24,7 +24,7 @@ import { HttpStart } from 'src/core/public';
 import { LoadingPage } from '../../loading_page';
 
 import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { useTrialStatus } from '../../../hooks/use_trial_status';
 
 export const SubscriptionSplashContent: React.FC = () => {
diff --git a/x-pack/plugins/infra/public/components/logging/log_customization_menu.tsx b/x-pack/plugins/infra/public/components/logging/log_customization_menu.tsx
index 84074568bcfef..5433c435d0647 100644
--- a/x-pack/plugins/infra/public/components/logging/log_customization_menu.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_customization_menu.tsx
@@ -8,7 +8,7 @@ import { EuiButtonEmpty, EuiPopover } from '@elastic/eui';
 import { FormattedMessage } from '@kbn/i18n/react';
 import * as React from 'react';
 
-import { euiStyled } from '../../../../observability/public';
+import { euiStyled } from '../../../../../../src/plugins/kibana_react/common';
 
 interface LogCustomizationMenuState {
   isShown: boolean;
diff --git a/x-pack/plugins/infra/public/components/logging/log_entry_examples/log_entry_examples.tsx b/x-pack/plugins/infra/public/components/logging/log_entry_examples/log_entry_examples.tsx
index 2d15068e51da5..e8ffd408087d0 100644
--- a/x-pack/plugins/infra/public/components/logging/log_entry_examples/log_entry_examples.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_entry_examples/log_entry_examples.tsx
@@ -6,7 +6,7 @@
 
 import React from 'react';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { LogEntryExampleMessagesEmptyIndicator } from './log_entry_examples_empty_indicator';
 import { LogEntryExampleMessagesFailureIndicator } from './log_entry_examples_failure_indicator';
 import { LogEntryExampleMessagesLoadingIndicator } from './log_entry_examples_loading_indicator';
diff --git a/x-pack/plugins/infra/public/components/logging/log_highlights_menu.tsx b/x-pack/plugins/infra/public/components/logging/log_highlights_menu.tsx
index 7beead461cb2e..00e9c412ddd3e 100644
--- a/x-pack/plugins/infra/public/components/logging/log_highlights_menu.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_highlights_menu.tsx
@@ -18,7 +18,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
 import { debounce } from 'lodash';
 import React, { useCallback, useMemo, useState } from 'react';
 
-import { euiStyled } from '../../../../observability/public';
+import { euiStyled } from '../../../../../../src/plugins/kibana_react/common';
 import { useVisibilityState } from '../../utils/use_visibility_state';
 
 interface LogHighlightsMenuProps {
diff --git a/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx
index 0528d59f0b3d5..311e25e7c6639 100644
--- a/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx
@@ -9,7 +9,7 @@ import { area, curveMonotoneY } from 'd3-shape';
 import { max } from 'lodash';
 import * as React from 'react';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { LogEntriesSummaryBucket } from '../../../../common/http_api';
 
 interface DensityChartProps {
diff --git a/x-pack/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx
index 2869f8d0087c2..53d9985c20dc1 100644
--- a/x-pack/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx
@@ -6,7 +6,7 @@
 
 import * as React from 'react';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 
 interface HighlightedIntervalProps {
   className?: string;
diff --git a/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx
index 496d4ebf924a3..a53ce9e3fabc6 100644
--- a/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx
@@ -7,7 +7,7 @@
 import { scaleLinear } from 'd3-scale';
 import * as React from 'react';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { LogEntryTime } from '../../../../common/log_entry';
 import { DensityChart } from './density_chart';
 import { HighlightedInterval } from './highlighted_interval';
diff --git a/x-pack/plugins/infra/public/components/logging/log_minimap/search_marker.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/search_marker.tsx
index 6271627589394..6c63654eef804 100644
--- a/x-pack/plugins/infra/public/components/logging/log_minimap/search_marker.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_minimap/search_marker.tsx
@@ -7,7 +7,7 @@
 import { FormattedMessage } from '@kbn/i18n/react';
 import * as React from 'react';
 
-import { euiStyled, keyframes } from '../../../../../observability/public';
+import { euiStyled, keyframes } from '../../../../../../../src/plugins/kibana_react/common';
 import { LogEntryTime } from '../../../../common/log_entry';
 import { SearchMarkerTooltip } from './search_marker_tooltip';
 import { LogEntriesSummaryHighlightsBucket } from '../../../../common/http_api';
diff --git a/x-pack/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx
index 458fdbfcd4916..8424e798128e2 100644
--- a/x-pack/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx
@@ -7,7 +7,7 @@
 import { scaleTime } from 'd3-scale';
 import * as React from 'react';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { getTimeLabelFormat } from './time_label_formatter';
 
 interface TimeRulerProps {
diff --git a/x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_input.tsx b/x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_input.tsx
index 248dce8f6bf8c..4c5237d1c9030 100644
--- a/x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_input.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_input.tsx
@@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
 import classNames from 'classnames';
 import * as React from 'react';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 
 interface LogSearchInputProps {
   className?: string;
diff --git a/x-pack/plugins/infra/public/components/logging/log_statusbar.tsx b/x-pack/plugins/infra/public/components/logging/log_statusbar.tsx
index 64dda6ce74d89..f0d062270f01b 100644
--- a/x-pack/plugins/infra/public/components/logging/log_statusbar.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_statusbar.tsx
@@ -6,7 +6,7 @@
 
 import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
 
-import { euiStyled } from '../../../../observability/public';
+import { euiStyled } from '../../../../../../src/plugins/kibana_react/common';
 
 export const LogStatusbar = euiStyled(EuiFlexGroup).attrs(() => ({
   alignItems: 'center',
diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx
index a38df73837416..23c3e9c4391e4 100644
--- a/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx
@@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n';
 import React, { useContext } from 'react';
 import { transparentize } from 'polished';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import {
   LogEntryColumn,
   LogEntryColumnContent,
diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx
index b13e3569c1e5b..884e26359ba45 100644
--- a/x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx
@@ -6,7 +6,7 @@
 
 import stringify from 'json-stable-stringify';
 import React from 'react';
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { JsonArray, JsonValue } from '../../../../../../../src/plugins/kibana_utils/common';
 import { ActiveHighlightMarker, highlightFieldValue, HighlightMarker } from './highlighting';
 
diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/highlighting.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/highlighting.tsx
index 2af99e30ce44f..159ed6d427926 100644
--- a/x-pack/plugins/infra/public/components/logging/log_text_stream/highlighting.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/highlighting.tsx
@@ -6,7 +6,7 @@
 
 import React from 'react';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { chooseLightOrDarkColor, tintOrShade } from '../../../utils/styles';
 
 export const ActiveHighlightMarker = euiStyled.mark`
diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx
index 50c26784bbdab..17b11205e806d 100644
--- a/x-pack/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx
@@ -8,7 +8,7 @@ import { EuiButtonEmpty, EuiText } from '@elastic/eui';
 import { FormattedMessage } from '@kbn/i18n/react';
 import * as React from 'react';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 
 interface LogTextStreamJumpToTailProps {
   onClickJump?: () => void;
diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx
index 1dd6e0b23e6bc..f176bad9e7a17 100644
--- a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx
@@ -16,7 +16,7 @@ import { FormattedMessage, FormattedTime, FormattedRelative } from '@kbn/i18n/re
 import * as React from 'react';
 import { Unit } from '@elastic/datemath';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { LogTextSeparator } from './log_text_separator';
 import { extendDatemath } from '../../../utils/datemath';
 
diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_column.tsx
index a82c2869baa93..d0f2fa7be6f96 100644
--- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_column.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_column.tsx
@@ -6,7 +6,7 @@
 
 import { useMemo } from 'react';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { TextScale } from '../../../../common/log_text_scale';
 import {
   LogColumnRenderConfiguration,
diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx
index 8582be008a44a..d2cb3baef350d 100644
--- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx
@@ -14,7 +14,7 @@ import {
   EuiContextMenuItem,
 } from '@elastic/eui';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { LogEntryColumnContent } from './log_entry_column';
 
 interface LogEntryContextMenuItem {
diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx
index 5813f08497a74..8de9e565b00be 100644
--- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx
@@ -6,7 +6,7 @@
 
 import { render } from '@testing-library/react';
 import React from 'react';
-import { EuiThemeProvider } from '../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common';
 import { LogFieldColumn } from '../../../../common/http_api';
 import { LogEntryFieldColumn } from './log_entry_field_column';
 
diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx
index fe5e7f305f60c..4a9b0d0906a76 100644
--- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx
@@ -6,7 +6,7 @@
 
 import React from 'react';
 import { JsonValue } from '../../../../../../../src/plugins/kibana_utils/common';
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { LogColumn } from '../../../../common/http_api';
 import { isFieldColumn, isHighlightFieldColumn } from '../../../utils/log_entry';
 import { FieldValue } from './field_value';
diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.test.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.test.tsx
index b9871cc3b36f4..5d36e5cd47c59 100644
--- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.test.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.test.tsx
@@ -6,7 +6,7 @@
 
 import { render } from '@testing-library/react';
 import React from 'react';
-import { EuiThemeProvider } from '../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common';
 import { LogMessageColumn } from '../../../../common/http_api';
 import { LogEntryMessageColumn } from './log_entry_message_column';
 
diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx
index 65e7b2bf2273d..bfc160ada2e6a 100644
--- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx
@@ -5,7 +5,7 @@
  */
 
 import React, { memo, useMemo } from 'react';
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { LogColumn, LogMessagePart } from '../../../../common/http_api';
 import {
   isConstantSegment,
diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx
index 5a653300217d7..93c657fbdda97 100644
--- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx
@@ -8,7 +8,8 @@ import React, { memo, useState, useCallback, useMemo } from 'react';
 import { i18n } from '@kbn/i18n';
 import { isEmpty } from 'lodash';
 
-import { euiStyled, useUiTracker } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
+import { useUiTracker } from '../../../../../observability/public';
 import { isTimestampColumn } from '../../../utils/log_entry';
 import { TextScale } from '../../../../common/log_text_scale';
 import { LogEntryColumn, LogEntryColumnWidths, iconColumnId } from './log_entry_column';
diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx
index 21568974463eb..d13fb4b6d7bb6 100644
--- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx
@@ -6,7 +6,7 @@
 
 import React, { memo } from 'react';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { TimeFormat, useFormattedTime } from '../../formatted_time';
 import { LogEntryColumnContent } from './log_entry_column';
 
diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx
index 3c86ce3e32526..d399e47a73562 100644
--- a/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx
@@ -9,7 +9,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
 import React, { Fragment } from 'react';
 import moment from 'moment';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { TextScale } from '../../../../common/log_text_scale';
 import { TimeKey, UniqueTimeKey } from '../../../../common/time';
 import { callWithoutRepeats } from '../../../utils/handlers';
diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx
index 7fc97f949f068..d7b94d4c02912 100644
--- a/x-pack/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx
@@ -6,7 +6,7 @@
 
 import React, { useMemo, useState, useCallback } from 'react';
 
-import { euiStyled, css } from '../../../../../observability/public';
+import { euiStyled, css } from '../../../../../../../src/plugins/kibana_react/common';
 import { TextScale } from '../../../../common/log_text_scale';
 
 export type WrapMode = 'none' | 'pre-wrapped' | 'long';
diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/vertical_scroll_panel.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/vertical_scroll_panel.tsx
index 9b8fecc7fb111..a4fed85a6a88e 100644
--- a/x-pack/plugins/infra/public/components/logging/log_text_stream/vertical_scroll_panel.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/vertical_scroll_panel.tsx
@@ -8,7 +8,7 @@ import { bisector } from 'd3-array';
 import { sortBy, throttle } from 'lodash';
 import * as React from 'react';
 
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { Rect } from './measurable_item_view';
 
 interface VerticalScrollPanelProps<Child> {
diff --git a/x-pack/plugins/infra/public/components/navigation/app_navigation.tsx b/x-pack/plugins/infra/public/components/navigation/app_navigation.tsx
index 9da892ec92ec1..68d523f5066a1 100644
--- a/x-pack/plugins/infra/public/components/navigation/app_navigation.tsx
+++ b/x-pack/plugins/infra/public/components/navigation/app_navigation.tsx
@@ -6,7 +6,7 @@
 
 import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
 import React from 'react';
-import { euiStyled } from '../../../../observability/public';
+import { euiStyled } from '../../../../../../src/plugins/kibana_react/common';
 
 interface AppNavigationProps {
   'aria-label': string;
diff --git a/x-pack/plugins/infra/public/components/navigation/routed_tabs.tsx b/x-pack/plugins/infra/public/components/navigation/routed_tabs.tsx
index d9340d804fb24..1db4e0ee70883 100644
--- a/x-pack/plugins/infra/public/components/navigation/routed_tabs.tsx
+++ b/x-pack/plugins/infra/public/components/navigation/routed_tabs.tsx
@@ -8,7 +8,7 @@ import { EuiLink, EuiTab, EuiTabs } from '@elastic/eui';
 import React from 'react';
 import { Route } from 'react-router-dom';
 
-import { euiStyled } from '../../../../observability/public';
+import { euiStyled } from '../../../../../../src/plugins/kibana_react/common';
 import { useLinkProps } from '../../hooks/use_link_props';
 import { LinkDescriptor } from '../../hooks/use_link_props';
 
diff --git a/x-pack/plugins/infra/public/components/page.tsx b/x-pack/plugins/infra/public/components/page.tsx
index 9636a5fc3a631..a76500a1d723f 100644
--- a/x-pack/plugins/infra/public/components/page.tsx
+++ b/x-pack/plugins/infra/public/components/page.tsx
@@ -6,7 +6,7 @@
 
 import { EuiPage } from '@elastic/eui';
 
-import { euiStyled } from '../../../observability/public';
+import { euiStyled } from '../../../../../src/plugins/kibana_react/common';
 
 export const ColumnarPage = euiStyled.div`
   display: flex;
diff --git a/x-pack/plugins/infra/public/components/toolbar_panel.ts b/x-pack/plugins/infra/public/components/toolbar_panel.ts
index 686b563068d60..a0039f6370f1f 100644
--- a/x-pack/plugins/infra/public/components/toolbar_panel.ts
+++ b/x-pack/plugins/infra/public/components/toolbar_panel.ts
@@ -5,7 +5,7 @@
  */
 
 import { EuiPanel } from '@elastic/eui';
-import { euiStyled } from '../../../observability/public';
+import { euiStyled } from '../../../../../src/plugins/kibana_react/common';
 
 export const ToolbarPanel = euiStyled(EuiPanel).attrs(() => ({
   grow: false,
diff --git a/x-pack/plugins/infra/public/pages/error.tsx b/x-pack/plugins/infra/public/pages/error.tsx
index c34af31ce28a7..55892c424f465 100644
--- a/x-pack/plugins/infra/public/pages/error.tsx
+++ b/x-pack/plugins/infra/public/pages/error.tsx
@@ -15,7 +15,7 @@ import {
 import { FormattedMessage } from '@kbn/i18n/react';
 import React from 'react';
 
-import { euiStyled } from '../../../observability/public';
+import { euiStyled } from '../../../../../src/plugins/kibana_react/common';
 import { Header } from '../components/header';
 import { ColumnarPage, PageContent } from '../components/page';
 
diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx
index 5c1e8f2bdcfcc..ecddd8a9aa5be 100644
--- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx
@@ -10,7 +10,8 @@ import { i18n } from '@kbn/i18n';
 import moment from 'moment';
 import React, { useCallback, useEffect, useMemo, useState } from 'react';
 import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
-import { euiStyled, useTrackPageview } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
+import { useTrackPageview } from '../../../../../observability/public';
 import { TimeRange } from '../../../../common/http_api/shared/time_range';
 import { CategoryJobNoticesSection } from '../../../components/logging/log_analysis_job_status';
 import { useLogEntryCategoriesModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_categories';
diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_expression.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_expression.tsx
index be8281ce54556..d5480977e7f9e 100644
--- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_expression.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_expression.tsx
@@ -7,7 +7,7 @@
 import { i18n } from '@kbn/i18n';
 import React, { memo } from 'react';
 
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 
 export const RegularExpressionRepresentation: React.FunctionComponent<{
   maximumSegmentCount?: number;
diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_list.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_list.tsx
index 748c82cc5cd5a..779ac3e8c3a07 100644
--- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_list.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_list.tsx
@@ -6,7 +6,7 @@
 
 import React from 'react';
 
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import { LogEntryCategoryDataset } from '../../../../../../common/http_api/log_analysis';
 import { getFriendlyNameForPartitionId } from '../../../../../../common/log_analysis';
 
diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_comparison.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_comparison.tsx
index d73f9f33fe5db..e4da367721c2d 100644
--- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_comparison.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_comparison.tsx
@@ -9,7 +9,7 @@ import numeral from '@elastic/numeral';
 import { i18n } from '@kbn/i18n';
 import React from 'react';
 
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 
 export const SingleMetricComparison: React.FunctionComponent<{
   currentValue: number;
diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx
index 96abe4ab42669..954b6a9ab3ed3 100644
--- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx
@@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
 import React, { useMemo, useCallback } from 'react';
 import useSet from 'react-use/lib/useSet';
 
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import {
   LogEntryCategory,
   LogEntryCategoryDataset,
diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx
index c4a464a4cffad..09d3746c6ace6 100644
--- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx
@@ -11,7 +11,8 @@ import { stringify } from 'query-string';
 import React, { useCallback, useEffect, useMemo, useState } from 'react';
 import { encode, RisonValue } from 'rison-node';
 import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
-import { euiStyled, useTrackPageview } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
+import { useTrackPageview } from '../../../../../observability/public';
 import { TimeRange } from '../../../../common/http_api/shared/time_range';
 import { bucketSpan } from '../../../../common/log_analysis';
 import { TimeKey } from '../../../../common/time';
diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx
index e6489417f2d07..37032a95e9640 100644
--- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx
@@ -9,7 +9,7 @@ import numeral from '@elastic/numeral';
 import { i18n } from '@kbn/i18n';
 import React from 'react';
 import useMount from 'react-use/lib/useMount';
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import { LogEntryAnomaly } from '../../../../../../common/http_api';
 import { TimeRange } from '../../../../../../common/http_api/shared/time_range';
 import { LogEntryExampleMessages } from '../../../../../components/logging/log_entry_examples/log_entry_examples';
diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx
index ff9b9db2e5087..c89f0329e9f2e 100644
--- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx
@@ -14,7 +14,7 @@ import {
 } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import React, { useMemo } from 'react';
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import { LogEntryRateResults } from '../../use_log_entry_rate_results';
 import { TimeRange } from '../../../../../../common/http_api/shared/time_range';
 import { getAnnotationsForAll, getLogEntryRateCombinedSeries } from '../helpers/data_formatters';
diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/log_entry_example.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/log_entry_example.tsx
index b639cecf676ad..ab3476cd78eb3 100644
--- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/log_entry_example.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/log_entry_example.tsx
@@ -8,7 +8,7 @@ import React, { useMemo, useCallback, useState } from 'react';
 import moment from 'moment';
 import { encode } from 'rison-node';
 import { i18n } from '@kbn/i18n';
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import { getFriendlyNameForPartitionId } from '../../../../../../common/log_analysis';
 import {
   LogEntryColumn,
diff --git a/x-pack/plugins/infra/public/pages/logs/settings/add_log_column_popover.tsx b/x-pack/plugins/infra/public/pages/logs/settings/add_log_column_popover.tsx
index 551d200f1895d..887e706c909ba 100644
--- a/x-pack/plugins/infra/public/pages/logs/settings/add_log_column_popover.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/settings/add_log_column_popover.tsx
@@ -15,7 +15,7 @@ import {
 import { FormattedMessage } from '@kbn/i18n/react';
 import React, { useCallback, useMemo } from 'react';
 import { v4 as uuidv4 } from 'uuid';
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { LogColumnConfiguration } from '../../../utils/source_configuration';
 import { useVisibilityState } from '../../../utils/use_visibility_state';
 
diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx
index 05bcd4d0984e8..02125807faf54 100644
--- a/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx
@@ -5,7 +5,7 @@
  */
 
 import React, { useContext, useCallback } from 'react';
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { AutoSizer } from '../../../components/auto_sizer';
 import { LogEntryFlyout } from '../../../components/logging/log_entry_flyout';
 import { LogMinimap } from '../../../components/logging/log_minimap';
diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx
index 8a4081288b283..3fa89da5b5e51 100644
--- a/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx
@@ -19,7 +19,7 @@ import React, { useCallback, useContext, useMemo } from 'react';
 import { LogEntry } from '../../../../common/http_api';
 import { ViewLogInContext } from '../../../containers/logs/view_log_in_context';
 import { useViewportDimensions } from '../../../utils/use_viewport_dimensions';
-import { euiStyled } from '../../../../../observability/public';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
 import { LogStream } from '../../../components/log_stream';
 
 const MODAL_MARGIN = 25;
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx
index 5c6e124914f39..6055b60719a68 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx
@@ -7,8 +7,8 @@
 import React, { useCallback, useState } from 'react';
 import { i18n } from '@kbn/i18n';
 import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiSpacer } from '@elastic/eui';
-
-import { euiStyled, useUiTracker } from '../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
+import { useUiTracker } from '../../../../../../observability/public';
 import { InfraFormatter } from '../../../../lib/lib';
 import { Timeline } from './timeline/timeline';
 
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/dropdown_button.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/dropdown_button.tsx
index 62b25d5a36870..b574347970df6 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/dropdown_button.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/dropdown_button.tsx
@@ -6,7 +6,7 @@
 
 import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui';
 import React, { ReactNode } from 'react';
-import { withTheme, EuiTheme } from '../../../../../../observability/public';
+import { withTheme, EuiTheme } from '../../../../../../../../src/plugins/kibana_react/common';
 
 interface Props {
   'data-test-subj'?: string;
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx
index 2f312d9ee64ac..f8b0bbf62d2b5 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx
@@ -19,7 +19,7 @@ import { useWaffleFiltersContext } from '../hooks/use_waffle_filters';
 import { DEFAULT_LEGEND, useWaffleOptionsContext } from '../hooks/use_waffle_options';
 import { useSourceContext } from '../../../../containers/source';
 import { InfraFormatterType } from '../../../../lib/lib';
-import { euiStyled } from '../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
 import { Toolbar } from './toolbars/toolbar';
 import { ViewSwitcher } from './waffle/view_switcher';
 import { IntervalLabel } from './waffle/interval_label';
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/subscription_splash_content.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/subscription_splash_content.tsx
index f07c37f5e7ea2..4e18880f1fd22 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/subscription_splash_content.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/subscription_splash_content.tsx
@@ -24,7 +24,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
 import { LoadingPage } from '../../../../../../components/loading_page';
 import { useTrialStatus } from '../../../../../../hooks/use_trial_status';
 import { useKibana } from '../../../../../../../../../../src/plugins/kibana_react/public';
-import { euiStyled } from '../../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../../src/plugins/kibana_react/common';
 import { HttpStart } from '../../../../../../../../../../src/core/public';
 
 export const SubscriptionSplashContent: React.FC = () => {
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx
index 661844a627a58..a053e2a72cea0 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx
@@ -10,7 +10,7 @@ import React, { useMemo, useState } from 'react';
 import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui';
 import { EuiOutsideClickDetector } from '@elastic/eui';
 import { EuiIcon, EuiButtonIcon } from '@elastic/eui';
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../../../../lib/lib';
 import { InventoryItemType } from '../../../../../../common/inventory_models/types';
 import { MetricsTab } from './tabs/metrics/metrics';
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row.tsx
index 3f0798c4a1670..bc03f9337813f 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row.tsx
@@ -21,7 +21,7 @@ import {
   EuiSpacer,
 } from '@elastic/eui';
 import { AutoSizer } from '../../../../../../../components/auto_sizer';
-import { euiStyled } from '../../../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../../../src/plugins/kibana_react/common';
 import { Process } from './types';
 import { ProcessRowCharts } from './process_row_charts';
 
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row_charts.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row_charts.tsx
index af515ae75854c..a3f75402df2ca 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row_charts.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row_charts.tsx
@@ -25,7 +25,7 @@ import { MetricsExplorerChartType } from '../../../../../metrics_explorer/hooks/
 import { MetricExplorerSeriesChart } from '../../../../../metrics_explorer/components/series_chart';
 import { MetricsExplorerAggregation } from '../../../../../../../../common/http_api';
 import { Color } from '../../../../../../../../common/color_palette';
-import { euiStyled } from '../../../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../../../src/plugins/kibana_react/common';
 import { useProcessListRowChart } from '../../../../hooks/use_process_list_row_chart';
 import { Process } from './types';
 
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/processes_table.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/processes_table.tsx
index 1ea6e397e7768..f420fd09e6e68 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/processes_table.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/processes_table.tsx
@@ -25,7 +25,7 @@ import {
 } from '@elastic/eui';
 import { ProcessListAPIResponse } from '../../../../../../../../common/http_api';
 import { FORMATTERS } from '../../../../../../../../common/formatters';
-import { euiStyled } from '../../../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../../../src/plugins/kibana_react/common';
 import { SortBy } from '../../../../hooks/use_process_list';
 import { Process } from './types';
 import { ProcessRow } from './process_row';
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/summary_table.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/summary_table.tsx
index 5bbba906b62f2..93d98853539b9 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/summary_table.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/summary_table.tsx
@@ -16,7 +16,7 @@ import {
   EuiDescriptionListDescription,
   EuiHorizontalRule,
 } from '@elastic/eui';
-import { euiStyled } from '../../../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../../../src/plugins/kibana_react/common';
 import { ProcessListAPIResponse } from '../../../../../../../../common/http_api';
 import { STATE_NAMES } from './states';
 
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx
index 46b63bc400a2b..f06cdd3cb0875 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx
@@ -15,7 +15,7 @@ import { useMetadata } from '../../../../../metric_detail/hooks/use_metadata';
 import { getFields } from './build_fields';
 import { useWaffleTimeContext } from '../../../../hooks/use_waffle_time';
 import { Table } from './table';
-import { euiStyled } from '../../../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../../../src/plugins/kibana_react/common';
 import { useWaffleFiltersContext } from '../../../../hooks/use_waffle_filters';
 
 const TabComponent = (props: TabProps) => {
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/shared.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/shared.tsx
index 6ff31e86c9d5e..670fc3673de25 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/shared.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/shared.tsx
@@ -6,7 +6,7 @@
 
 import { InventoryItemType } from '../../../../../../../common/inventory_models/types';
 import { InfraWaffleMapOptions, InfraWaffleMapNode } from '../../../../../../lib/lib';
-import { euiStyled } from '../../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../../src/plugins/kibana_react/common';
 
 export interface TabProps {
   options: InfraWaffleMapOptions;
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/nodes_overview.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/nodes_overview.tsx
index 9b6853dcdc751..de31e690e3659 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/nodes_overview.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/nodes_overview.tsx
@@ -9,7 +9,7 @@ import React, { useCallback } from 'react';
 import { getBreakpoint } from '@elastic/eui';
 
 import { InventoryItemType } from '../../../../../common/inventory_models/types';
-import { euiStyled } from '../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
 import { InfraWaffleMapBounds, InfraWaffleMapOptions, InfraFormatter } from '../../../../lib/lib';
 import { NoData } from '../../../../components/empty_states';
 import { InfraLoadingPanel } from '../../../../components/loading';
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx
index f1e796ef8ba18..44993f688ebc2 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx
@@ -38,7 +38,7 @@ import { MetricsExplorerChartType } from '../../../metrics_explorer/hooks/use_me
 import { getTimelineChartTheme } from '../../../metrics_explorer/components/helpers/get_chart_theme';
 import { calculateDomain } from '../../../metrics_explorer/components/helpers/calculate_domain';
 
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import { InfraFormatter } from '../../../../../lib/lib';
 import { useMetricsHostsAnomaliesResults } from '../../hooks/use_metrics_hosts_anomalies';
 import { useMetricsK8sAnomaliesResults } from '../../hooks/use_metrics_k8s_anomalies';
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx
index fbca85e2d4496..c69bdb7c4379e 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx
@@ -7,7 +7,7 @@
 import React from 'react';
 import { mount } from 'enzyme';
 // import { act } from 'react-dom/test-utils';
-import { EuiThemeProvider } from '../../../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../../../src/plugins/kibana_react/common';
 import { EuiToolTip } from '@elastic/eui';
 import { ConditionalToolTip } from './conditional_tooltip';
 import {
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx
index 7ec1ae905a640..a559882987bfa 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx
@@ -8,7 +8,7 @@ import { EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
 import { first } from 'lodash';
 import { getCustomMetricLabel } from '../../../../../../common/formatters/get_custom_metric_label';
 import { SnapshotCustomMetricInput } from '../../../../../../common/http_api';
-import { withTheme, EuiTheme } from '../../../../../../../observability/public';
+import { withTheme, EuiTheme } from '../../../../../../../../../src/plugins/kibana_react/common';
 import { useSourceContext } from '../../../../../containers/source';
 import { findInventoryModel } from '../../../../../../common/inventory_models';
 import {
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/gradient_legend.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/gradient_legend.tsx
index 953d94e51aad0..a900c46376ba6 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/gradient_legend.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/gradient_legend.tsx
@@ -6,7 +6,7 @@
 
 import React from 'react';
 
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import {
   InfraFormatter,
   InfraWaffleMapBounds,
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_name.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_name.tsx
index 75a023d49d4aa..1e592b846c320 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_name.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_name.tsx
@@ -6,7 +6,7 @@
 import { EuiLink, EuiToolTip } from '@elastic/eui';
 import React from 'react';
 
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import { InfraWaffleMapGroup, InfraWaffleMapOptions } from '../../../../../lib/lib';
 
 interface Props {
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_groups.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_groups.tsx
index 760dae169d000..6741cd0f851ea 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_groups.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_groups.tsx
@@ -6,7 +6,7 @@
 
 import React from 'react';
 
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import {
   InfraWaffleMapBounds,
   InfraWaffleMapGroupOfGroups,
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx
index 5fcee6193b357..056fc4dcb79fe 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx
@@ -6,7 +6,7 @@
 
 import React from 'react';
 
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import {
   InfraWaffleMapBounds,
   InfraWaffleMapGroupOfNodes,
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend.tsx
index ea7bb66e689d9..202d263084896 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend.tsx
@@ -5,7 +5,7 @@
  */
 import React, { useCallback } from 'react';
 
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import {
   InfraFormatter,
   InfraWaffleMapBounds,
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend_controls.tsx
index f4da68d9dead7..13fa3c86fa9aa 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend_controls.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend_controls.tsx
@@ -25,7 +25,7 @@ import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
 import React, { SyntheticEvent, useState, useCallback, useEffect } from 'react';
 import { first, last } from 'lodash';
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import { InfraWaffleMapBounds, InventoryColorPalette, PALETTES } from '../../../../../lib/lib';
 import { WaffleLegendOptions } from '../../hooks/use_waffle_options';
 import { getColorPalette } from '../../lib/get_color_palette';
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx
index 8023e3bf7da62..147876d3b377d 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx
@@ -5,7 +5,7 @@
  */
 import React from 'react';
 
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import { nodesToWaffleMap } from '../../lib/nodes_to_wafflemap';
 import { isWaffleMapGroupWithGroups, isWaffleMapGroupWithNodes } from '../../lib/type_guards';
 import { InfraWaffleMapBounds, InfraWaffleMapOptions } from '../../../../../lib/lib';
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx
index 262d94d8f3674..c1c923092726a 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx
@@ -27,7 +27,7 @@ import {
   SNAPSHOT_CUSTOM_AGGREGATIONS,
   SnapshotCustomAggregationRT,
 } from '../../../../../../../common/http_api/snapshot_api';
-import { EuiTheme, withTheme } from '../../../../../../../../xpack_legacy/common';
+import { EuiTheme, withTheme } from '../../../../../../../../../../src/plugins/kibana_react/common';
 
 interface SelectedOption {
   label: string;
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_edit_mode.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_edit_mode.tsx
index 831a0cde49cfb..2c37049a9a6f5 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_edit_mode.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_edit_mode.tsx
@@ -8,7 +8,7 @@ import { EuiFlexItem, EuiFlexGroup, EuiButtonIcon } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import { getCustomMetricLabel } from '../../../../../../../common/formatters/get_custom_metric_label';
 import { SnapshotCustomMetricInput } from '../../../../../../../common/http_api/snapshot_api';
-import { EuiTheme, withTheme } from '../../../../../../../../xpack_legacy/common';
+import { EuiTheme, withTheme } from '../../../../../../../../../../src/plugins/kibana_react/common';
 
 interface Props {
   theme: EuiTheme | undefined;
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/mode_switcher.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/mode_switcher.tsx
index 956241545e8be..c2076dca6d581 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/mode_switcher.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/mode_switcher.tsx
@@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
 import { CustomMetricMode } from './types';
 import { SnapshotCustomMetricInput } from '../../../../../../../common/http_api/snapshot_api';
-import { EuiTheme, withTheme } from '../../../../../../../../xpack_legacy/common';
+import { EuiTheme, withTheme } from '../../../../../../../../../../src/plugins/kibana_react/common';
 
 interface Props {
   theme: EuiTheme | undefined;
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx
index 4024f6b505c29..11d1b60034c78 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx
@@ -10,7 +10,7 @@ import React from 'react';
 import { i18n } from '@kbn/i18n';
 
 import { first } from 'lodash';
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import {
   InfraWaffleMapBounds,
   InfraWaffleMapNode,
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx
index 3179d4aa05268..0d0333ca27c64 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx
@@ -16,6 +16,7 @@ import { createUptimeLink } from '../../lib/create_uptime_link';
 import { findInventoryModel, findInventoryFields } from '../../../../../../common/inventory_models';
 import { useKibana } from '../../../../../../../../../src/plugins/kibana_react/public';
 import { InventoryItemType } from '../../../../../../common/inventory_models/types';
+import { withTheme, EuiTheme } from '../../../../../../../../../src/plugins/kibana_react/common';
 import {
   Section,
   SectionLinkProps,
@@ -24,8 +25,6 @@ import {
   SectionSubtitle,
   SectionLinks,
   SectionLink,
-  withTheme,
-  EuiTheme,
 } from '../../../../../../../observability/public';
 import { useLinkProps } from '../../../../../hooks/use_link_props';
 
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/palette_preview.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/palette_preview.tsx
index 09e323b55a71a..1d8fcb20a261c 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/palette_preview.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/palette_preview.tsx
@@ -5,7 +5,7 @@
  */
 
 import React from 'react';
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import { InventoryColorPalette } from '../../../../../lib/lib';
 import { getColorPalette } from '../../lib/get_color_palette';
 
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/stepped_gradient_legend.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/stepped_gradient_legend.tsx
index fadf6b139a3e8..ed34a32012bd2 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/stepped_gradient_legend.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/stepped_gradient_legend.tsx
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 import React from 'react';
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import {
   InfraWaffleMapBounds,
   InfraFormatter,
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/steps_legend.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/steps_legend.tsx
index 1ef0f2d0c4288..3ef76c6a70733 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/steps_legend.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/steps_legend.tsx
@@ -7,7 +7,7 @@
 import { darken } from 'polished';
 import React from 'react';
 
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import {
   InfraFormatter,
   InfraWaffleMapRuleOperator,
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx
index 8d2f289621b12..349f9a9b25e9b 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx
@@ -17,7 +17,7 @@ import React from 'react';
 import { IFieldType } from 'src/plugins/data/public';
 import { InfraGroupByOptions } from '../../../../../lib/lib';
 import { CustomFieldPanel } from './custom_field_panel';
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import { InventoryItemType } from '../../../../../../common/inventory_models/types';
 import { SnapshotGroupBy } from '../../../../../../common/http_api/snapshot_api';
 import { DropdownButton } from '../dropdown_button';
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_sort_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_sort_controls.tsx
index a45ac0cee72d9..55466049bc71a 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_sort_controls.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_sort_controls.tsx
@@ -7,7 +7,7 @@
 import React, { useCallback, useMemo, useState, ReactNode } from 'react';
 import { EuiSwitch, EuiContextMenuPanelDescriptor, EuiPopover, EuiContextMenu } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
-import { EuiTheme, withTheme } from '../../../../../../../observability/public';
+import { EuiTheme, withTheme } from '../../../../../../../../../src/plugins/kibana_react/common';
 import { WaffleSortOption } from '../../hooks/use_waffle_options';
 import { DropdownButton } from '../dropdown_button';
 
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_time_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_time_controls.tsx
index da044b1cf99ee..01e938b5e6a62 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_time_controls.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_time_controls.tsx
@@ -8,7 +8,7 @@ import { EuiButton, EuiDatePicker, EuiFlexGroup, EuiFlexItem } from '@elastic/eu
 import { FormattedMessage } from '@kbn/i18n/react';
 import moment, { Moment } from 'moment';
 import React, { useCallback } from 'react';
-import { withTheme, EuiTheme } from '../../../../../../../observability/public';
+import { withTheme, EuiTheme } from '../../../../../../../../../src/plugins/kibana_react/common';
 import { useWaffleTimeContext } from '../../hooks/use_waffle_time';
 
 interface Props {
diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/gauges_section_vis.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/gauges_section_vis.tsx
index 2d06713f7fe1a..be73c8a0b5e1c 100644
--- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/gauges_section_vis.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/gauges_section_vis.tsx
@@ -16,7 +16,7 @@ import {
 import { get, last, max } from 'lodash';
 import React, { ReactText } from 'react';
 
-import { euiStyled } from '../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
 import { createFormatter } from '../../../../../common/formatters';
 import { InventoryFormatterType } from '../../../../../common/inventory_models/types';
 import { SeriesOverrides, VisSectionProps } from '../types';
diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx
index b9b8e83b598f3..e81dedba64043 100644
--- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx
@@ -7,7 +7,7 @@
 import { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
 import { FormattedMessage } from '@kbn/i18n/react';
 import React from 'react';
-import { euiStyled } from '../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
 import { ViewSourceConfigurationButton } from '../../../../components/source_configuration';
 import { useLinkProps } from '../../../../hooks/use_link_props';
 
diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/layout_content.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/layout_content.tsx
index 4620102517549..e33785632555e 100644
--- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/layout_content.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/layout_content.tsx
@@ -5,7 +5,7 @@
  */
 
 import { EuiPageContent } from '@elastic/eui';
-import { euiStyled } from '../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
 
 export const LayoutContent = euiStyled(EuiPageContent)`
   position: relative;
diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/metadata_details.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/metadata_details.tsx
index 656378fbc0610..953659731407c 100644
--- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/metadata_details.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/metadata_details.tsx
@@ -9,7 +9,7 @@ import { EuiButtonIcon, EuiFlexGrid, EuiFlexItem, EuiTitle, EuiText } from '@ela
 import { i18n } from '@kbn/i18n';
 import { get } from 'lodash';
 import { InfraMetadata } from '../../../../../common/http_api';
-import { euiStyled } from '../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
 import { MetadataContext } from '../containers/metadata_context';
 
 interface FieldDef {
diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx
index 0d0bc8c82397e..a30b2c17d3517 100644
--- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx
@@ -21,7 +21,7 @@ import { AutoSizer } from '../../../../components/auto_sizer';
 import { MetricsTimeControls } from './time_controls';
 import { SideNavContext, NavItem } from '../lib/side_nav_context';
 import { PageBody } from './page_body';
-import { euiStyled } from '../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
 import { MetricsTimeInput } from '../hooks/use_metrics_time';
 import { InfraMetadata } from '../../../../../common/http_api/metadata_api';
 import { PageError } from './page_error';
diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/side_nav.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/side_nav.tsx
index 1cba3366acbbb..e1fb307d35f5f 100644
--- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/side_nav.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/side_nav.tsx
@@ -6,7 +6,7 @@
 
 import { EuiHideFor, EuiPageSideBar, EuiShowFor, EuiSideNav } from '@elastic/eui';
 import React, { useState, useCallback } from 'react';
-import { euiStyled } from '../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
 import { NavItem } from '../lib/side_nav_context';
 
 interface Props {
diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/time_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/time_controls.tsx
index afee0c0498187..ba9e71021b232 100644
--- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/time_controls.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/time_controls.tsx
@@ -7,7 +7,7 @@
 import { EuiSuperDatePicker, OnRefreshChangeProps, OnTimeChangeProps } from '@elastic/eui';
 import React, { useCallback } from 'react';
 import { UI_SETTINGS } from '../../../../../../../../src/plugins/data/public';
-import { euiStyled } from '../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
 import { MetricsTimeInput } from '../hooks/use_metrics_time';
 import { useKibanaUiSetting } from '../../../../utils/use_kibana_ui_setting';
 import { mapKibanaQuickRangesToDatePickerRanges } from '../../../../utils/map_timepicker_quickranges_to_datepicker_ranges';
diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx
index 60c8041fb5ef0..4220303d02cf4 100644
--- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx
@@ -5,7 +5,11 @@
  */
 import { i18n } from '@kbn/i18n';
 import React, { useContext, useState } from 'react';
-import { euiStyled, EuiTheme, withTheme } from '../../../../../observability/public';
+import {
+  euiStyled,
+  EuiTheme,
+  withTheme,
+} from '../../../../../../../src/plugins/kibana_react/common';
 import { DocumentTitle } from '../../../components/document_title';
 import { Header } from '../../../components/header';
 import { ColumnarPage, PageContent } from '../../../components/page';
diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/types.ts b/x-pack/plugins/infra/public/pages/metrics/metric_detail/types.ts
index 3ec57e23a425d..2331d69d460d1 100644
--- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/types.ts
+++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/types.ts
@@ -5,7 +5,7 @@
  */
 
 import rt from 'io-ts';
-import { EuiTheme } from '../../../../../observability/public';
+import { EuiTheme } from '../../../../../../../src/plugins/kibana_react/common';
 import { InventoryFormatterTypeRT } from '../../../../common/inventory_models/types';
 import { MetricsTimeInput } from './hooks/use_metrics_time';
 import { NodeDetailsMetricData } from '../../../../common/http_api/node_details_api';
diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart.tsx
index 41d8014b4a5c1..c228f09cbb645 100644
--- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart.tsx
@@ -25,7 +25,7 @@ import {
   MetricsExplorerYAxisMode,
   MetricsExplorerChartOptions,
 } from '../hooks/use_metrics_explorer_options';
-import { euiStyled } from '../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
 import { createFormatterForMetric } from './helpers/create_formatter_for_metric';
 import { MetricExplorerSeriesChart } from './series_chart';
 import { MetricsExplorerChartContextMenu } from './chart_context_menu';
diff --git a/x-pack/plugins/observability/public/application/index.tsx b/x-pack/plugins/observability/public/application/index.tsx
index ea84a417c20eb..d4dfd265ecda6 100644
--- a/x-pack/plugins/observability/public/application/index.tsx
+++ b/x-pack/plugins/observability/public/application/index.tsx
@@ -7,12 +7,12 @@ import { i18n } from '@kbn/i18n';
 import React, { useEffect } from 'react';
 import ReactDOM from 'react-dom';
 import { Route, Router, Switch } from 'react-router-dom';
+import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common';
 import { AppMountParameters, CoreStart } from '../../../../../src/core/public';
 import {
   KibanaContextProvider,
   RedirectAppLinks,
 } from '../../../../../src/plugins/kibana_react/public';
-import { EuiThemeProvider } from '../../../xpack_legacy/common';
 import { PluginContext } from '../context/plugin_context';
 import { usePluginContext } from '../hooks/use_plugin_context';
 import { useRouteParams } from '../hooks/use_route_params';
diff --git a/x-pack/plugins/observability/public/components/shared/core_web_vitals/__stories__/core_vitals.stories.tsx b/x-pack/plugins/observability/public/components/shared/core_web_vitals/__stories__/core_vitals.stories.tsx
index 208c840b403e9..b16a560899b52 100644
--- a/x-pack/plugins/observability/public/components/shared/core_web_vitals/__stories__/core_vitals.stories.tsx
+++ b/x-pack/plugins/observability/public/components/shared/core_web_vitals/__stories__/core_vitals.stories.tsx
@@ -8,10 +8,10 @@ import React, { ComponentType } from 'react';
 import { IntlProvider } from 'react-intl';
 import { Observable } from 'rxjs';
 import { CoreStart } from 'src/core/public';
+import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common';
 import { createKibanaReactContext } from '../../../../../../../../src/plugins/kibana_react/public';
 import { CoreVitalItem } from '../core_vital_item';
 import { LCP_HELP_LABEL, LCP_LABEL } from '../translations';
-import { EuiThemeProvider } from '../../../../typings';
 
 const KibanaReactContext = createKibanaReactContext(({
   uiSettings: { get: () => {}, get$: () => new Observable() },
diff --git a/x-pack/plugins/observability/public/hooks/use_theme.tsx b/x-pack/plugins/observability/public/hooks/use_theme.tsx
index 51a1ad5029538..ccb8bf6d86be3 100644
--- a/x-pack/plugins/observability/public/hooks/use_theme.tsx
+++ b/x-pack/plugins/observability/public/hooks/use_theme.tsx
@@ -5,7 +5,7 @@
  */
 import { useContext } from 'react';
 import { ThemeContext } from 'styled-components';
-import { EuiTheme } from '../../../xpack_legacy/common';
+import { EuiTheme } from '../../../../../src/plugins/kibana_react/common';
 
 export function useTheme() {
   const theme: EuiTheme = useContext(ThemeContext);
diff --git a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx
index a28e34e7d4dcb..14cf141c7cd38 100644
--- a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx
+++ b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx
@@ -9,12 +9,12 @@ import { storiesOf } from '@storybook/react';
 import { AppMountParameters, CoreStart } from 'kibana/public';
 import React from 'react';
 import { MemoryRouter } from 'react-router-dom';
+import { EuiThemeProvider } from '../../../../../../src/plugins/kibana_react/common';
 import { UI_SETTINGS } from '../../../../../../src/plugins/data/public';
 import { HasDataContextProvider } from '../../context/has_data_context';
 import { PluginContext } from '../../context/plugin_context';
 import { registerDataHandler, unregisterDataHandler } from '../../data_handler';
 import { ObservabilityPluginSetupDeps } from '../../plugin';
-import { EuiThemeProvider } from '../../typings';
 import { OverviewPage } from './';
 import { alertsFetchData } from './mock/alerts.mock';
 import { emptyResponse as emptyAPMResponse, fetchApmData } from './mock/apm.mock';
diff --git a/x-pack/plugins/observability/public/typings/eui_styled_components.tsx b/x-pack/plugins/observability/public/typings/eui_styled_components.tsx
deleted file mode 100644
index 9e547b58bc736..0000000000000
--- a/x-pack/plugins/observability/public/typings/eui_styled_components.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import * as styledComponents from 'styled-components';
-import { ThemedStyledComponentsModule, ThemeProvider, ThemeProviderProps } from 'styled-components';
-
-import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
-import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
-
-export interface EuiTheme {
-  eui: typeof euiLightVars | typeof euiDarkVars;
-  darkMode: boolean;
-}
-
-function EuiThemeProvider<
-  OuterTheme extends styledComponents.DefaultTheme = styledComponents.DefaultTheme
->({
-  darkMode = false,
-  ...otherProps
-}: Omit<ThemeProviderProps<OuterTheme, OuterTheme & EuiTheme>, 'theme'> & {
-  darkMode?: boolean;
-}) {
-  return (
-    <ThemeProvider
-      {...otherProps}
-      theme={(outerTheme?: OuterTheme) => ({
-        ...outerTheme,
-        eui: darkMode ? euiDarkVars : euiLightVars,
-        darkMode,
-      })}
-    />
-  );
-}
-
-const {
-  default: euiStyled,
-  css,
-  createGlobalStyle,
-  keyframes,
-  withTheme,
-} = (styledComponents as unknown) as ThemedStyledComponentsModule<EuiTheme>;
-
-export { css, euiStyled, EuiThemeProvider, createGlobalStyle, keyframes, withTheme };
diff --git a/x-pack/plugins/observability/public/typings/index.ts b/x-pack/plugins/observability/public/typings/index.ts
index 5cc2c613881df..a54d514fde18f 100644
--- a/x-pack/plugins/observability/public/typings/index.ts
+++ b/x-pack/plugins/observability/public/typings/index.ts
@@ -5,5 +5,4 @@
  */
 
 export * from './eui_draggable';
-export * from './eui_styled_components';
 export * from './fetch_overview_data';
diff --git a/x-pack/plugins/observability/public/utils/test_helper.tsx b/x-pack/plugins/observability/public/utils/test_helper.tsx
index 10158d7aedd0e..bd48acf2ab991 100644
--- a/x-pack/plugins/observability/public/utils/test_helper.tsx
+++ b/x-pack/plugins/observability/public/utils/test_helper.tsx
@@ -12,7 +12,7 @@ import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/p
 import translations from '../../../translations/translations/ja-JP.json';
 import { PluginContext } from '../context/plugin_context';
 import { ObservabilityPluginSetupDeps } from '../plugin';
-import { EuiThemeProvider } from '../typings';
+import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common';
 
 const appMountParameters = ({ setHeaderActionMenu: () => {} } as unknown) as AppMountParameters;
 
diff --git a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_root_provider.tsx b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_root_provider.tsx
index 149d948a53fc4..49cc345a631dd 100644
--- a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_root_provider.tsx
+++ b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_root_provider.tsx
@@ -11,7 +11,7 @@ import { Router } from 'react-router-dom';
 import { History } from 'history';
 import useObservable from 'react-use/lib/useObservable';
 import { Store } from 'redux';
-import { EuiThemeProvider } from '../../../../../xpack_legacy/common';
+import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common';
 import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public';
 import { RouteCapture } from '../../components/endpoint/route_capture';
 import { StartPlugins } from '../../../types';
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/vertical_divider.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/vertical_divider.ts
index b6f5c9b7421b5..e989077e1b8cb 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/vertical_divider.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/vertical_divider.ts
@@ -5,7 +5,7 @@
  */
 
 import styled from 'styled-components';
-import { EuiTheme } from '../../../../../../xpack_legacy/common';
+import { EuiTheme } from '../../../../../../../../src/plugins/kibana_react/common';
 
 type SpacingOptions = keyof EuiTheme['eui']['spacerSizes'];
 
diff --git a/x-pack/plugins/uptime/public/apps/uptime_app.tsx b/x-pack/plugins/uptime/public/apps/uptime_app.tsx
index 061398b25e452..13d4e1538dc4a 100644
--- a/x-pack/plugins/uptime/public/apps/uptime_app.tsx
+++ b/x-pack/plugins/uptime/public/apps/uptime_app.tsx
@@ -29,7 +29,7 @@ import { UptimeAlertsFlyoutWrapper } from '../components/overview/alerts';
 import { store } from '../state';
 import { kibanaService } from '../state/kibana_service';
 import { ActionMenu } from '../components/common/header/action_menu';
-import { EuiThemeProvider } from '../../../observability/public';
+import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common';
 
 export interface UptimeAppColors {
   danger: string;
diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp.tsx
index 2a35587a1960f..be4f0fc62271d 100644
--- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp.tsx
@@ -20,7 +20,8 @@ import styled from 'styled-components';
 import { FormattedMessage } from '@kbn/i18n/react';
 import { Ping } from '../../../../../common/runtime_types/ping';
 import { getShortTimeStamp } from '../../../overview/monitor_list/columns/monitor_status_column';
-import { euiStyled, FETCH_STATUS, useFetcher } from '../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
+import { useFetcher, FETCH_STATUS } from '../../../../../../observability/public';
 import { getJourneyScreenshot } from '../../../../state/api/journey';
 import { UptimeSettingsContext } from '../../../../contexts';
 
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts
index 7bf5100730f5e..312014dfb35d7 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts
@@ -6,7 +6,7 @@
 
 import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
 import { rgba } from 'polished';
-import { euiStyled } from '../../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
 import { FIXED_AXIS_HEIGHT } from './constants';
 
 interface WaterfallChartOuterContainerProps {
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall.test.tsx
index 44e63f04f7bec..e9694cde61307 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall.test.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall.test.tsx
@@ -13,7 +13,7 @@ import {
   renderLegendItem,
   renderSidebarItem,
 } from '../../step_detail/waterfall/waterfall_chart_wrapper';
-import { EuiThemeProvider } from '../../../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../../../src/plugins/kibana_react/common';
 import { WaterfallChartOuterContainer } from './styles';
 
 describe('waterfall', () => {
diff --git a/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.tsx b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.tsx
index 851da79314552..27fdd1f8071bd 100644
--- a/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.tsx
@@ -8,7 +8,7 @@ import React, { useRef, useEffect, RefObject } from 'react';
 import { EuiSuggestItem } from '@elastic/eui';
 
 import { QuerySuggestion } from '../../../../../../../../src/plugins/data/public';
-import { euiStyled } from '../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
 
 const SuggestionItem = euiStyled.div<{ selected: boolean }>`
   background: ${(props) => (props.selected ? props.theme.eui.euiColorLightestShade : 'initial')};
diff --git a/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.tsx b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.tsx
index 4f8fb712de679..817845307c40a 100644
--- a/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.tsx
@@ -9,7 +9,7 @@ import { isEmpty } from 'lodash';
 import { rgba } from 'polished';
 import { Suggestion } from './suggestion';
 import { QuerySuggestion } from '../../../../../../../../src/plugins/data/public';
-import { euiStyled } from '../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
 
 export const unit = 16;
 
diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.test.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.test.tsx
index 41a74fc0aec5c..e882fc730e420 100644
--- a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.test.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.test.tsx
@@ -8,7 +8,7 @@ import React from 'react';
 import { getLocationStatus, MonitorListStatusColumn } from './monitor_status_column';
 import { Ping } from '../../../../../common/runtime_types';
 import { STATUS } from '../../../../../common/constants';
-import { EuiThemeProvider } from '../../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common';
 import { mockDate, mockMoment } from '../../../../lib/helper/test_helpers';
 import { render } from '../../../../lib/helper/rtl_helpers';
 import { fireEvent, screen, waitFor } from '@testing-library/react';
diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.tsx
index 81e486bb467bb..ce446db17dcd4 100644
--- a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.tsx
@@ -19,7 +19,7 @@ import {
 } from '../../../../../common/constants';
 
 import { UptimeThemeContext } from '../../../../contexts';
-import { euiStyled } from '../../../../../../observability/public';
+import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
 import { STATUS_DOWN_LABEL, STATUS_UP_LABEL } from '../../../common/translations';
 
 interface MonitorListStatusColumnProps {
diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.test.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.test.tsx
index 82a7100a48081..bec90191d1105 100644
--- a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.test.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.test.tsx
@@ -19,7 +19,7 @@ import * as redux from 'react-redux';
 import moment from 'moment';
 import { IHttpFetchError } from '../../../../../../../src/core/public';
 import { mockMoment } from '../../../lib/helper/test_helpers';
-import { EuiThemeProvider } from '../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common';
 
 jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => {
   return {
diff --git a/x-pack/plugins/uptime/public/lib/helper/rtl_helpers.tsx b/x-pack/plugins/uptime/public/lib/helper/rtl_helpers.tsx
index 97bd63614141b..78f553121a3fd 100644
--- a/x-pack/plugins/uptime/public/lib/helper/rtl_helpers.tsx
+++ b/x-pack/plugins/uptime/public/lib/helper/rtl_helpers.tsx
@@ -13,7 +13,7 @@ import { I18nProvider } from '@kbn/i18n/react';
 import { coreMock } from 'src/core/public/mocks';
 import { configure } from '@testing-library/dom';
 import { mockState } from '../__mocks__/uptime_store.mock';
-import { EuiThemeProvider } from '../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../src/plugins/kibana_react/common';
 import {
   KibanaContextProvider,
   KibanaServices,
diff --git a/x-pack/plugins/xpack_legacy/common/index.ts b/x-pack/plugins/xpack_legacy/common/index.ts
deleted file mode 100644
index 8c0dace27faf4..0000000000000
--- a/x-pack/plugins/xpack_legacy/common/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export * from './eui_styled_components';

From 474af9f3ebcf506a3f2076a1491c250bc5e0810f Mon Sep 17 00:00:00 2001
From: Constance <constancecchen@users.noreply.github.com>
Date: Mon, 25 Jan 2021 11:56:42 -0800
Subject: [PATCH 37/46] [Enterprise Search] Add Kea test helper for directly
 accessing listeners (#89061)

* Add getListeners to Kea test helpers

* Update TelemetryLogic to use new getListeners helper

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
 .../public/applications/__mocks__/kea.mock.ts | 25 +++++++++++++++++++
 .../shared/telemetry/telemetry_logic.test.ts  | 16 ++++--------
 2 files changed, 30 insertions(+), 11 deletions(-)

diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea.mock.ts
index 78ffbcfa3526f..5ac8cce04181e 100644
--- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea.mock.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea.mock.ts
@@ -120,4 +120,29 @@ export class LogicMounter {
   public unmount = () => {
     this.unmountFn();
   };
+
+  /**
+   * Some tests (e.g. async tests, tests that expect thrown errors) need to access
+   * listener functions directly instead of calling `SomeLogic.actions.someListener`,
+   * due to how Kea invokes/wraps action fns by design.
+   *
+   * Example usage:
+   *
+   * const { mount, getListeners } = new LogicMounter(SomeLogic);
+   *
+   * it('some test', async () => {
+   *   mount();
+   *   const { someListener } = getListeners({ values: { someMockValue: false } });
+   *
+   *   const mockBreakpoint = jest.fn();
+   *   await someListener({ someMockArgument: true }, mockBreakpoint);
+   * });
+   */
+  public getListeners = (listenersArgs: object = {}) => {
+    const { listeners } = this.logicFile.inputs[0];
+
+    return typeof listeners === 'function'
+      ? (listeners as Function)(listenersArgs) // e.g., listeners({ values, actions, props }) => ({ ... })
+      : listeners; // handles simpler logic files that just define listeners: { ... }
+  };
 }
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/telemetry_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/telemetry_logic.test.ts
index 6d4e4f4fe649c..770dcf074f163 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/telemetry_logic.test.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/telemetry_logic.test.ts
@@ -4,20 +4,18 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { resetContext } from 'kea';
-
 import { JSON_HEADER as headers } from '../../../../common/constants';
-import { mockHttpValues } from '../../__mocks__/http_logic.mock';
+import { LogicMounter, mockHttpValues } from '../../__mocks__';
 
-import { TelemetryLogic } from './';
+import { TelemetryLogic } from './telemetry_logic';
 
 describe('Telemetry logic', () => {
+  const { mount, getListeners } = new LogicMounter(TelemetryLogic);
   const { http } = mockHttpValues;
 
   beforeEach(() => {
     jest.clearAllMocks();
-    resetContext({});
-    TelemetryLogic.mount();
+    mount();
   });
 
   describe('sendTelemetry', () => {
@@ -36,11 +34,7 @@ describe('Telemetry logic', () => {
 
     it('throws an error if the telemetry endpoint fails', async () => {
       http.put.mockImplementationOnce(() => Promise.reject());
-
-      // To capture thrown errors, we have to call the listener fn directly
-      // instead of using `TelemetryLogic.actions.sendTelemetry` - this is
-      // due to how Kea invokes/wraps action fns by design.
-      const { sendTelemetry } = (TelemetryLogic.inputs[0] as any).listeners({ actions: {} });
+      const { sendTelemetry } = getListeners();
 
       await expect(sendTelemetry({ action: '', metric: '', product: '' })).rejects.toThrow(
         'Unable to send telemetry'

From a31c3eba13f4fb7951a7865f9b2efd4220b30bc8 Mon Sep 17 00:00:00 2001
From: Constance <constancecchen@users.noreply.github.com>
Date: Mon, 25 Jan 2021 12:24:04 -0800
Subject: [PATCH 38/46] [App Search] Add AnalyticsCards & AnalyticsChart
 components to analytics pages (#88930)

* Create reusable AnalyticsCards component

* Update EngineOverview to use new AnalyticsCards component

* Update Analytics overview with AnalyticsCards + data

* Update QueryDetail with AnalyticsCards + data

* Update Analytics overview with AnalyticsChart + data

- turns out we do need startDate after all for charts, so I added it back to types

* Update QueryDetail with AnalyticsChart + data

* [Polish] Dash click and no result lines to match standalone UI

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
 .../analytics/analytics_logic.test.ts         | 25 +++++++-
 .../components/analytics/analytics_logic.ts   | 55 ++++++++++++++++
 .../components/analytics_cards.test.tsx       | 38 ++++++++++++
 .../analytics/components/analytics_cards.tsx  | 32 ++++++++++
 .../components/analytics_chart.test.tsx       |  8 +++
 .../analytics/components/analytics_chart.tsx  |  4 +-
 .../components/analytics/components/index.ts  |  1 +
 .../components/analytics/constants.ts         |  4 ++
 .../app_search/components/analytics/index.ts  |  2 +-
 .../app_search/components/analytics/types.ts  |  3 +-
 .../analytics/views/analytics.test.tsx        | 15 ++++-
 .../components/analytics/views/analytics.tsx  | 62 ++++++++++++++++++-
 .../analytics/views/query_detail.test.tsx     | 10 +++
 .../analytics/views/query_detail.tsx          | 35 +++++++++++
 .../components/total_stats.test.tsx           | 36 ++---------
 .../components/total_stats.tsx                | 38 ++++++------
 16 files changed, 312 insertions(+), 56 deletions(-)
 create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.test.tsx
 create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.tsx

diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts
index 30a64219403b5..0901ff2737803 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts
@@ -29,6 +29,15 @@ describe('AnalyticsLogic', () => {
     dataLoading: true,
     analyticsUnavailable: false,
     allTags: [],
+    totalQueries: 0,
+    totalQueriesNoResults: 0,
+    totalClicks: 0,
+    totalQueriesForQuery: 0,
+    queriesPerDay: [],
+    queriesNoResultsPerDay: [],
+    clicksPerDay: [],
+    queriesPerDayForQuery: [],
+    startDate: '',
   };
 
   const MOCK_TOP_QUERIES = [
@@ -66,6 +75,7 @@ describe('AnalyticsLogic', () => {
   const MOCK_ANALYTICS_RESPONSE = {
     analyticsUnavailable: false,
     allTags: ['some-tag'],
+    startDate: '1970-01-01',
     recentQueries: MOCK_RECENT_QUERIES,
     topQueries: MOCK_TOP_QUERIES,
     topQueriesNoResults: MOCK_TOP_QUERIES,
@@ -81,6 +91,7 @@ describe('AnalyticsLogic', () => {
   const MOCK_QUERY_RESPONSE = {
     analyticsUnavailable: false,
     allTags: ['some-tag'],
+    startDate: '1970-01-01',
     totalQueriesForQuery: 50,
     queriesPerDayForQuery: [25, 0, 25],
     topClicksForQuery: MOCK_TOP_CLICKS,
@@ -120,7 +131,14 @@ describe('AnalyticsLogic', () => {
           dataLoading: false,
           analyticsUnavailable: false,
           allTags: ['some-tag'],
-          // TODO: more state will get set here in future PRs
+          startDate: '1970-01-01',
+          totalClicks: 1000,
+          totalQueries: 5000,
+          totalQueriesNoResults: 500,
+          queriesPerDay: [10, 50, 100],
+          queriesNoResultsPerDay: [1, 2, 3],
+          clicksPerDay: [0, 10, 50],
+          // TODO: Replace this with ...MOCK_ANALYTICS_RESPONSE once all data is set
         });
       });
     });
@@ -135,7 +153,10 @@ describe('AnalyticsLogic', () => {
           dataLoading: false,
           analyticsUnavailable: false,
           allTags: ['some-tag'],
-          // TODO: more state will get set here in future PRs
+          startDate: '1970-01-01',
+          totalQueriesForQuery: 50,
+          queriesPerDayForQuery: [25, 0, 25],
+          // TODO: Replace this with ...MOCK_QUERY_RESPONSE once all data is set
         });
       });
     });
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.ts
index 4d8603f4bb6d6..537de02a0fee5 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.ts
@@ -62,6 +62,61 @@ export const AnalyticsLogic = kea<MakeLogicType<AnalyticsValues, AnalyticsAction
         onQueryDataLoad: (_, { allTags }) => allTags,
       },
     ],
+    totalQueries: [
+      0,
+      {
+        onAnalyticsDataLoad: (_, { totalQueries }) => totalQueries,
+      },
+    ],
+    totalQueriesNoResults: [
+      0,
+      {
+        onAnalyticsDataLoad: (_, { totalQueriesNoResults }) => totalQueriesNoResults,
+      },
+    ],
+    totalClicks: [
+      0,
+      {
+        onAnalyticsDataLoad: (_, { totalClicks }) => totalClicks,
+      },
+    ],
+    queriesPerDay: [
+      [],
+      {
+        onAnalyticsDataLoad: (_, { queriesPerDay }) => queriesPerDay,
+      },
+    ],
+    queriesNoResultsPerDay: [
+      [],
+      {
+        onAnalyticsDataLoad: (_, { queriesNoResultsPerDay }) => queriesNoResultsPerDay,
+      },
+    ],
+    clicksPerDay: [
+      [],
+      {
+        onAnalyticsDataLoad: (_, { clicksPerDay }) => clicksPerDay,
+      },
+    ],
+    totalQueriesForQuery: [
+      0,
+      {
+        onQueryDataLoad: (_, { totalQueriesForQuery }) => totalQueriesForQuery,
+      },
+    ],
+    queriesPerDayForQuery: [
+      [],
+      {
+        onQueryDataLoad: (_, { queriesPerDayForQuery }) => queriesPerDayForQuery,
+      },
+    ],
+    startDate: [
+      '',
+      {
+        onAnalyticsDataLoad: (_, { startDate }) => startDate,
+        onQueryDataLoad: (_, { startDate }) => startDate,
+      },
+    ],
   }),
   listeners: ({ actions }) => ({
     loadAnalyticsData: async () => {
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.test.tsx
new file mode 100644
index 0000000000000..eee6517d5b480
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.test.tsx
@@ -0,0 +1,38 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+import { EuiStat } from '@elastic/eui';
+
+import { AnalyticsCards } from './';
+
+describe('AnalyticsCards', () => {
+  it('renders', () => {
+    const wrapper = shallow(
+      <AnalyticsCards
+        stats={[
+          {
+            stat: 100,
+            text: 'Red fish',
+            dataTestSubj: 'RedFish',
+          },
+          {
+            stat: 2000,
+            text: 'Blue fish',
+            dataTestSubj: 'BlueFish',
+          },
+        ]}
+      />
+    );
+
+    expect(wrapper.find(EuiStat)).toHaveLength(2);
+    expect(wrapper.find('[data-test-subj="RedFish"]').prop('title')).toEqual(100);
+    expect(wrapper.find('[data-test-subj="RedFish"]').prop('description')).toEqual('Red fish');
+    expect(wrapper.find('[data-test-subj="BlueFish"]').prop('title')).toEqual(2000);
+    expect(wrapper.find('[data-test-subj="BlueFish"]').prop('description')).toEqual('Blue fish');
+  });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.tsx
new file mode 100644
index 0000000000000..0359283059171
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.tsx
@@ -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;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiStat } from '@elastic/eui';
+
+interface Props {
+  stats: Array<{
+    text: string;
+    stat: number;
+    dataTestSubj?: string;
+  }>;
+}
+export const AnalyticsCards: React.FC<Props> = ({ stats }) => (
+  <EuiFlexGroup>
+    {stats.map(({ text, stat, dataTestSubj }) => (
+      <EuiFlexItem key={text}>
+        <EuiPanel>
+          <EuiStat
+            title={stat}
+            description={text}
+            titleColor="primary"
+            data-test-subj={dataTestSubj}
+          />
+        </EuiPanel>
+      </EuiFlexItem>
+    ))}
+  </EuiFlexGroup>
+);
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.test.tsx
index 4e071ac7982bd..b2e3e615e4817 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.test.tsx
@@ -54,6 +54,14 @@ describe('AnalyticsChart', () => {
     expect(wrapper.find(LineSeries)).toHaveLength(3);
   });
 
+  it('renders dashed lines', () => {
+    const wrapper = shallow(
+      <AnalyticsChart lines={[{ id: 'dashed 1', data: MOCK_DATA, isDashed: true }]} />
+    );
+
+    expect(wrapper.find(LineSeries).prop('lineSeriesStyle')?.line?.dash).toBeTruthy();
+  });
+
   it('formats x-axis dates correctly', () => {
     const wrapper = shallow(<AnalyticsChart lines={[{ id: 'test', data: MOCK_DATA }]} />);
     const dateFormatter: Function = wrapper.find('#bottom-axis').prop('tickFormat');
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.tsx
index 02ad2dff92827..a73d3c9054ed6 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.tsx
@@ -25,6 +25,7 @@ interface Props {
   lines: Array<{
     id: string;
     data: ChartData;
+    isDashed?: boolean;
   }>;
 }
 export const AnalyticsChart: React.FC<Props> = ({ height = 300, lines }) => {
@@ -39,7 +40,7 @@ export const AnalyticsChart: React.FC<Props> = ({ height = 300, lines }) => {
           headerFormatter: (tooltip) => moment(tooltip.value).format(TOOLTIP_DATE_FORMAT),
         }}
       />
-      {lines.map(({ id, data }) => (
+      {lines.map(({ id, data, isDashed }) => (
         <LineSeries
           key={id}
           id={id}
@@ -47,6 +48,7 @@ export const AnalyticsChart: React.FC<Props> = ({ height = 300, lines }) => {
           xAccessor={'x'}
           yAccessors={['y']}
           curve={CurveType.CURVE_MONOTONE_X}
+          lineSeriesStyle={isDashed ? { line: { dash: [5, 5] } } : undefined}
         />
       ))}
       <Axis
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/index.ts
index 8834a87a5f743..ae9c9ca450638 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/index.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/index.ts
@@ -4,6 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+export { AnalyticsCards } from './analytics_cards';
 export { AnalyticsChart } from './analytics_chart';
 export { AnalyticsHeader } from './analytics_header';
 export { AnalyticsUnavailable } from './analytics_unavailable';
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts
index 354ca6ff409e0..aaef38657d797 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts
@@ -25,6 +25,10 @@ export const TOTAL_QUERIES = i18n.translate(
   'xpack.enterpriseSearch.appSearch.engine.analytics.totalQueries',
   { defaultMessage: 'Total queries' }
 );
+export const TOTAL_QUERIES_NO_RESULTS = i18n.translate(
+  'xpack.enterpriseSearch.appSearch.engine.analytics.totalQueriesNoResults',
+  { defaultMessage: 'Total queries with no results' }
+);
 export const TOTAL_CLICKS = i18n.translate(
   'xpack.enterpriseSearch.appSearch.engine.analytics.totalClicks',
   { defaultMessage: 'Total clicks' }
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/index.ts
index 4dbc2d930ddf0..614a199877756 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/index.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/index.ts
@@ -7,5 +7,5 @@
 export { ANALYTICS_TITLE } from './constants';
 export { AnalyticsLogic } from './analytics_logic';
 export { AnalyticsRouter } from './analytics_router';
-export { AnalyticsChart } from './components';
+export { AnalyticsCards, AnalyticsChart } from './components';
 export { convertToChartData } from './utils';
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/types.ts
index 5cc14038507b9..a3977a0c07a80 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/types.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/types.ts
@@ -34,8 +34,9 @@ interface RecentQuery {
 interface BaseData {
   analyticsUnavailable: boolean;
   allTags: string[];
+  startDate: string;
   // NOTE: The API sends us back even more data than this (e.g.,
-  // startDate, endDate, currentTag, logRetentionSettings, query),
+  // endDate, currentTag, logRetentionSettings, query),
   // but we currently don't need that data in our front-end code,
   // so I'm opting not to list them in our types
 }
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/analytics.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/analytics.test.tsx
index 54365192943b5..06bf77d35372f 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/analytics.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/analytics.test.tsx
@@ -4,15 +4,28 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { setMockValues } from '../../../../__mocks__';
+
 import React from 'react';
 import { shallow } from 'enzyme';
 
+import { AnalyticsCards, AnalyticsChart } from '../components';
 import { Analytics } from './';
 
 describe('Analytics overview', () => {
   it('renders', () => {
+    setMockValues({
+      totalQueries: 3,
+      totalQueriesNoResults: 2,
+      totalClicks: 1,
+      queriesPerDay: [10, 20, 30],
+      queriesNoResultsPerDay: [1, 2, 3],
+      clicksPerDay: [0, 1, 5],
+      startDate: '1970-01-01',
+    });
     const wrapper = shallow(<Analytics />);
 
-    expect(wrapper.isEmptyRender()).toBe(false); // TODO
+    expect(wrapper.find(AnalyticsCards)).toHaveLength(1);
+    expect(wrapper.find(AnalyticsChart)).toHaveLength(1);
   });
 });
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/analytics.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/analytics.tsx
index 5febeae203aba..d3c3bff5a2947 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/analytics.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/analytics.tsx
@@ -5,13 +5,73 @@
  */
 
 import React from 'react';
+import { useValues } from 'kea';
 
-import { ANALYTICS_TITLE } from '../constants';
+import { EuiSpacer } from '@elastic/eui';
+
+import {
+  ANALYTICS_TITLE,
+  TOTAL_QUERIES,
+  TOTAL_QUERIES_NO_RESULTS,
+  TOTAL_CLICKS,
+} from '../constants';
 import { AnalyticsLayout } from '../analytics_layout';
+import { AnalyticsLogic, AnalyticsCards, AnalyticsChart, convertToChartData } from '../';
 
 export const Analytics: React.FC = () => {
+  const {
+    totalQueries,
+    totalQueriesNoResults,
+    totalClicks,
+    queriesPerDay,
+    queriesNoResultsPerDay,
+    clicksPerDay,
+    startDate,
+  } = useValues(AnalyticsLogic);
+
   return (
     <AnalyticsLayout isAnalyticsView title={ANALYTICS_TITLE}>
+      <AnalyticsCards
+        stats={[
+          {
+            stat: totalQueries,
+            text: TOTAL_QUERIES,
+            dataTestSubj: 'TotalQueriesCard',
+          },
+          {
+            stat: totalQueriesNoResults,
+            text: TOTAL_QUERIES_NO_RESULTS,
+            dataTestSubj: 'TotalQueriesNoResultsCard',
+          },
+          {
+            stat: totalClicks,
+            text: TOTAL_CLICKS,
+            dataTestSubj: 'TotalClicksCard',
+          },
+        ]}
+      />
+      <EuiSpacer />
+
+      <AnalyticsChart
+        lines={[
+          {
+            id: TOTAL_QUERIES,
+            data: convertToChartData({ startDate, data: queriesPerDay }),
+          },
+          {
+            id: TOTAL_QUERIES_NO_RESULTS,
+            data: convertToChartData({ startDate, data: queriesNoResultsPerDay }),
+            isDashed: true,
+          },
+          {
+            id: TOTAL_CLICKS,
+            data: convertToChartData({ startDate, data: clicksPerDay }),
+            isDashed: true,
+          },
+        ]}
+      />
+      <EuiSpacer />
+
       <p>TODO: Analytics overview</p>
     </AnalyticsLayout>
   );
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.test.tsx
index 2e4bd36d79382..99485340f6b88 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.test.tsx
@@ -5,6 +5,7 @@
  */
 
 import '../../../../__mocks__/react_router_history.mock';
+import { setMockValues } from '../../../../__mocks__';
 
 import React from 'react';
 import { useParams } from 'react-router-dom';
@@ -12,6 +13,7 @@ import { shallow } from 'enzyme';
 
 import { SetAppSearchChrome as SetPageChrome } from '../../../../shared/kibana_chrome';
 
+import { AnalyticsCards, AnalyticsChart } from '../components';
 import { QueryDetail } from './';
 
 describe('QueryDetail', () => {
@@ -19,6 +21,11 @@ describe('QueryDetail', () => {
 
   beforeEach(() => {
     (useParams as jest.Mock).mockReturnValueOnce({ query: 'some-query' });
+
+    setMockValues({
+      totalQueriesForQuery: 100,
+      queriesPerDayForQuery: [0, 5, 10],
+    });
   });
 
   it('renders', () => {
@@ -31,5 +38,8 @@ describe('QueryDetail', () => {
       'Query',
       'some-query',
     ]);
+
+    expect(wrapper.find(AnalyticsCards)).toHaveLength(1);
+    expect(wrapper.find(AnalyticsChart)).toHaveLength(1);
   });
 });
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.tsx
index b3b7e5258c536..53c1dc8b845b1 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.tsx
@@ -6,12 +6,16 @@
 
 import React from 'react';
 import { useParams } from 'react-router-dom';
+import { useValues } from 'kea';
+
 import { i18n } from '@kbn/i18n';
+import { EuiSpacer } from '@elastic/eui';
 
 import { SetAppSearchChrome as SetPageChrome } from '../../../../shared/kibana_chrome';
 import { BreadcrumbTrail } from '../../../../shared/kibana_chrome/generate_breadcrumbs';
 
 import { AnalyticsLayout } from '../analytics_layout';
+import { AnalyticsLogic, AnalyticsCards, AnalyticsChart, convertToChartData } from '../';
 
 const QUERY_DETAIL_TITLE = i18n.translate(
   'xpack.enterpriseSearch.appSearch.engine.analytics.queryDetail.title',
@@ -24,10 +28,41 @@ interface Props {
 export const QueryDetail: React.FC<Props> = ({ breadcrumbs }) => {
   const { query } = useParams() as { query: string };
 
+  const { totalQueriesForQuery, queriesPerDayForQuery, startDate } = useValues(AnalyticsLogic);
+
   return (
     <AnalyticsLayout isQueryView title={`"${query}"`}>
       <SetPageChrome trail={[...breadcrumbs, QUERY_DETAIL_TITLE, query]} />
 
+      <AnalyticsCards
+        stats={[
+          {
+            stat: totalQueriesForQuery,
+            text: i18n.translate(
+              'xpack.enterpriseSearch.appSearch.engine.analytics.queryDetail.cardDescription',
+              {
+                defaultMessage: 'Queries for {queryTitle}',
+                values: { queryTitle: query },
+              }
+            ),
+          },
+        ]}
+      />
+      <EuiSpacer />
+
+      <AnalyticsChart
+        lines={[
+          {
+            id: i18n.translate(
+              'xpack.enterpriseSearch.appSearch.engine.analytics.queryDetail.chartTooltip',
+              { defaultMessage: 'Queries per day' }
+            ),
+            data: convertToChartData({ startDate, data: queriesPerDayForQuery }),
+          },
+        ]}
+      />
+      <EuiSpacer />
+
       <p>TODO: Query detail page</p>
     </AnalyticsLayout>
   );
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx
index 6cb47e8b419f3..2464eb258452e 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx
@@ -7,45 +7,19 @@
 import { setMockValues } from '../../../../__mocks__/kea.mock';
 
 import React from 'react';
-import { shallow, ShallowWrapper } from 'enzyme';
-import { EuiStat } from '@elastic/eui';
+import { shallow } from 'enzyme';
 
+import { AnalyticsCards } from '../../analytics';
 import { TotalStats } from './total_stats';
 
 describe('TotalStats', () => {
-  let wrapper: ShallowWrapper;
-
-  beforeAll(() => {
-    jest.clearAllMocks();
+  it('renders', () => {
     setMockValues({
       totalQueries: 11,
       documentCount: 22,
       totalClicks: 33,
     });
-    wrapper = shallow(<TotalStats />);
-  });
-
-  it('renders the total queries stat', () => {
-    expect(wrapper.find('[data-test-subj="TotalQueriesCard"]')).toHaveLength(1);
-
-    const card = wrapper.find(EuiStat).at(0);
-    expect(card.prop('title')).toEqual(11);
-    expect(card.prop('description')).toEqual('Total queries');
-  });
-
-  it('renders the total documents stat', () => {
-    expect(wrapper.find('[data-test-subj="TotalDocumentsCard"]')).toHaveLength(1);
-
-    const card = wrapper.find(EuiStat).at(1);
-    expect(card.prop('title')).toEqual(22);
-    expect(card.prop('description')).toEqual('Total documents');
-  });
-
-  it('renders the total clicks stat', () => {
-    expect(wrapper.find('[data-test-subj="TotalClicksCard"]')).toHaveLength(1);
-
-    const card = wrapper.find(EuiStat).at(2);
-    expect(card.prop('title')).toEqual(33);
-    expect(card.prop('description')).toEqual('Total clicks');
+    const wrapper = shallow(<TotalStats />);
+    expect(wrapper.find(AnalyticsCards)).toHaveLength(1);
   });
 });
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx
index a27142938f558..a7f5923498455 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx
@@ -6,9 +6,9 @@
 
 import React from 'react';
 import { useValues } from 'kea';
-import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiStat } from '@elastic/eui';
 
 import { TOTAL_QUERIES, TOTAL_DOCUMENTS, TOTAL_CLICKS } from '../../analytics/constants';
+import { AnalyticsCards } from '../../analytics';
 
 import { EngineOverviewLogic } from '../';
 
@@ -16,22 +16,24 @@ export const TotalStats: React.FC = () => {
   const { totalQueries, documentCount, totalClicks } = useValues(EngineOverviewLogic);
 
   return (
-    <EuiFlexGroup>
-      <EuiFlexItem>
-        <EuiPanel data-test-subj="TotalQueriesCard">
-          <EuiStat title={totalQueries} description={TOTAL_QUERIES} titleColor="primary" />
-        </EuiPanel>
-      </EuiFlexItem>
-      <EuiFlexItem>
-        <EuiPanel data-test-subj="TotalDocumentsCard">
-          <EuiStat title={documentCount} description={TOTAL_DOCUMENTS} titleColor="primary" />
-        </EuiPanel>
-      </EuiFlexItem>
-      <EuiFlexItem>
-        <EuiPanel data-test-subj="TotalClicksCard">
-          <EuiStat title={totalClicks} description={TOTAL_CLICKS} titleColor="primary" />
-        </EuiPanel>
-      </EuiFlexItem>
-    </EuiFlexGroup>
+    <AnalyticsCards
+      stats={[
+        {
+          stat: totalQueries,
+          text: TOTAL_QUERIES,
+          dataTestSubj: 'TotalQueriesCard',
+        },
+        {
+          stat: documentCount,
+          text: TOTAL_DOCUMENTS,
+          dataTestSubj: 'TotalDocumentsCard',
+        },
+        {
+          stat: totalClicks,
+          text: TOTAL_CLICKS,
+          dataTestSubj: 'TotalClicksCard',
+        },
+      ]}
+    />
   );
 };

From ac06965cb089d058cc1e7de74d39b5dcf3ad9f74 Mon Sep 17 00:00:00 2001
From: Kaarina Tungseth <kaarina.tungseth@elastic.co>
Date: Mon, 25 Jan 2021 14:52:12 -0600
Subject: [PATCH 39/46] [DOCS] Fixes the version file (#89220)

---
 docs/index.asciidoc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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}

From 5d68b101067ab80aa36e2a85bfd2b2b6df422e91 Mon Sep 17 00:00:00 2001
From: Chandler Prall <chandler.prall@gmail.com>
Date: Mon, 25 Jan 2021 14:39:20 -0700
Subject: [PATCH 40/46] Upgrade EUI to v31.3.0 (#88881)

* Bump EUI to v31.3.0

* jest snapshot updates

* Fixed space issue in kbnQueryBar date picker

* Removed unecessary space in query bar scss

Co-authored-by: miukimiu <elizabet.oliveira@elastic.co>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
 package.json                                  |  2 +-
 .../ui/query_string_input/_query_bar.scss     |  3 +-
 .../__snapshots__/cron_editor.test.tsx.snap   | 76 +++++++++++++++++++
 .../sample_data_view_data_button.test.js.snap |  1 +
 .../share_context_menu.test.tsx.snap          |  3 +
 .../extended_template.stories.storyshot       |  2 +
 .../date_format.stories.storyshot             |  3 +
 .../number_format.stories.storyshot           |  3 +
 .../saved_elements_modal.stories.storyshot    |  3 +
 .../__snapshots__/tag.stories.storyshot       |  4 +-
 .../__snapshots__/tag_list.stories.storyshot  |  6 +-
 .../text_style_picker.stories.storyshot       |  2 +
 .../workpad_templates.stories.storyshot       |  9 ++-
 .../extended_template.stories.storyshot       |  6 ++
 .../extended_template.stories.storyshot       |  4 +
 .../__snapshots__/tools_control.test.tsx.snap |  2 +
 .../toc_entry_actions_popover.test.tsx.snap   |  4 +
 .../remote_cluster_form.test.js.snap          |  1 +
 .../__snapshots__/index.test.tsx.snap         |  1 +
 .../__snapshots__/cert_status.test.tsx.snap   |  2 +-
 .../__snapshots__/donut_chart.test.tsx.snap   |  8 +-
 yarn.lock                                     |  8 +-
 22 files changed, 133 insertions(+), 20 deletions(-)

diff --git a/package.json b/package.json
index 24297011ccc63..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",
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/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`] = `
                   </select>
                 </EuiValidatableControl>
                 <EuiFormControlLayoutIcons
+                  compressed={false}
                   icon={
                     Object {
                       "side": "right",
@@ -182,6 +183,7 @@ exports[`CronEditor is rendered with a DAY frequency 1`] = `
                     className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                   >
                     <EuiFormControlLayoutCustomIcon
+                      size="m"
                       type="arrowDown"
                     >
                       <span
@@ -190,12 +192,14 @@ exports[`CronEditor is rendered with a DAY frequency 1`] = `
                         <EuiIcon
                           aria-hidden="true"
                           className="euiFormControlLayoutCustomIcon__icon"
+                          size="m"
                           type="arrowDown"
                         >
                           <span
                             aria-hidden="true"
                             className="euiFormControlLayoutCustomIcon__icon"
                             data-euiicon-type="arrowDown"
+                            size="m"
                           />
                         </EuiIcon>
                       </span>
@@ -914,6 +918,7 @@ exports[`CronEditor is rendered with a DAY frequency 1`] = `
                             </select>
                           </EuiValidatableControl>
                           <EuiFormControlLayoutIcons
+                            compressed={false}
                             icon={
                               Object {
                                 "side": "right",
@@ -926,6 +931,7 @@ exports[`CronEditor is rendered with a DAY frequency 1`] = `
                               className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                             >
                               <EuiFormControlLayoutCustomIcon
+                                size="m"
                                 type="arrowDown"
                               >
                                 <span
@@ -934,12 +940,14 @@ exports[`CronEditor is rendered with a DAY frequency 1`] = `
                                   <EuiIcon
                                     aria-hidden="true"
                                     className="euiFormControlLayoutCustomIcon__icon"
+                                    size="m"
                                     type="arrowDown"
                                   >
                                     <span
                                       aria-hidden="true"
                                       className="euiFormControlLayoutCustomIcon__icon"
                                       data-euiicon-type="arrowDown"
+                                      size="m"
                                     />
                                   </EuiIcon>
                                 </span>
@@ -1607,6 +1615,7 @@ exports[`CronEditor is rendered with a DAY frequency 1`] = `
                             </select>
                           </EuiValidatableControl>
                           <EuiFormControlLayoutIcons
+                            compressed={false}
                             icon={
                               Object {
                                 "side": "right",
@@ -1619,6 +1628,7 @@ exports[`CronEditor is rendered with a DAY frequency 1`] = `
                               className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                             >
                               <EuiFormControlLayoutCustomIcon
+                                size="m"
                                 type="arrowDown"
                               >
                                 <span
@@ -1627,12 +1637,14 @@ exports[`CronEditor is rendered with a DAY frequency 1`] = `
                                   <EuiIcon
                                     aria-hidden="true"
                                     className="euiFormControlLayoutCustomIcon__icon"
+                                    size="m"
                                     type="arrowDown"
                                   >
                                     <span
                                       aria-hidden="true"
                                       className="euiFormControlLayoutCustomIcon__icon"
                                       data-euiicon-type="arrowDown"
+                                      size="m"
                                     />
                                   </EuiIcon>
                                 </span>
@@ -1824,6 +1836,7 @@ exports[`CronEditor is rendered with a HOUR frequency 1`] = `
                   </select>
                 </EuiValidatableControl>
                 <EuiFormControlLayoutIcons
+                  compressed={false}
                   icon={
                     Object {
                       "side": "right",
@@ -1836,6 +1849,7 @@ exports[`CronEditor is rendered with a HOUR frequency 1`] = `
                     className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                   >
                     <EuiFormControlLayoutCustomIcon
+                      size="m"
                       type="arrowDown"
                     >
                       <span
@@ -1844,12 +1858,14 @@ exports[`CronEditor is rendered with a HOUR frequency 1`] = `
                         <EuiIcon
                           aria-hidden="true"
                           className="euiFormControlLayoutCustomIcon__icon"
+                          size="m"
                           type="arrowDown"
                         >
                           <span
                             aria-hidden="true"
                             className="euiFormControlLayoutCustomIcon__icon"
                             data-euiicon-type="arrowDown"
+                            size="m"
                           />
                         </EuiIcon>
                       </span>
@@ -2816,6 +2832,7 @@ exports[`CronEditor is rendered with a HOUR frequency 1`] = `
                     </select>
                   </EuiValidatableControl>
                   <EuiFormControlLayoutIcons
+                    compressed={false}
                     icon={
                       Object {
                         "side": "right",
@@ -2828,6 +2845,7 @@ exports[`CronEditor is rendered with a HOUR frequency 1`] = `
                       className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                     >
                       <EuiFormControlLayoutCustomIcon
+                        size="m"
                         type="arrowDown"
                       >
                         <span
@@ -2836,12 +2854,14 @@ exports[`CronEditor is rendered with a HOUR frequency 1`] = `
                           <EuiIcon
                             aria-hidden="true"
                             className="euiFormControlLayoutCustomIcon__icon"
+                            size="m"
                             type="arrowDown"
                           >
                             <span
                               aria-hidden="true"
                               className="euiFormControlLayoutCustomIcon__icon"
                               data-euiicon-type="arrowDown"
+                              size="m"
                             />
                           </EuiIcon>
                         </span>
@@ -3029,6 +3049,7 @@ exports[`CronEditor is rendered with a MINUTE frequency 1`] = `
                   </select>
                 </EuiValidatableControl>
                 <EuiFormControlLayoutIcons
+                  compressed={false}
                   icon={
                     Object {
                       "side": "right",
@@ -3041,6 +3062,7 @@ exports[`CronEditor is rendered with a MINUTE frequency 1`] = `
                     className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                   >
                     <EuiFormControlLayoutCustomIcon
+                      size="m"
                       type="arrowDown"
                     >
                       <span
@@ -3049,12 +3071,14 @@ exports[`CronEditor is rendered with a MINUTE frequency 1`] = `
                         <EuiIcon
                           aria-hidden="true"
                           className="euiFormControlLayoutCustomIcon__icon"
+                          size="m"
                           type="arrowDown"
                         >
                           <span
                             aria-hidden="true"
                             className="euiFormControlLayoutCustomIcon__icon"
                             data-euiicon-type="arrowDown"
+                            size="m"
                           />
                         </EuiIcon>
                       </span>
@@ -3241,6 +3265,7 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = `
                   </select>
                 </EuiValidatableControl>
                 <EuiFormControlLayoutIcons
+                  compressed={false}
                   icon={
                     Object {
                       "side": "right",
@@ -3253,6 +3278,7 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = `
                     className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                   >
                     <EuiFormControlLayoutCustomIcon
+                      size="m"
                       type="arrowDown"
                     >
                       <span
@@ -3261,12 +3287,14 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = `
                         <EuiIcon
                           aria-hidden="true"
                           className="euiFormControlLayoutCustomIcon__icon"
+                          size="m"
                           type="arrowDown"
                         >
                           <span
                             aria-hidden="true"
                             className="euiFormControlLayoutCustomIcon__icon"
                             data-euiicon-type="arrowDown"
+                            size="m"
                           />
                         </EuiIcon>
                       </span>
@@ -4173,6 +4201,7 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = `
                     </select>
                   </EuiValidatableControl>
                   <EuiFormControlLayoutIcons
+                    compressed={false}
                     icon={
                       Object {
                         "side": "right",
@@ -4185,6 +4214,7 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = `
                       className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                     >
                       <EuiFormControlLayoutCustomIcon
+                        size="m"
                         type="arrowDown"
                       >
                         <span
@@ -4193,12 +4223,14 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = `
                           <EuiIcon
                             aria-hidden="true"
                             className="euiFormControlLayoutCustomIcon__icon"
+                            size="m"
                             type="arrowDown"
                           >
                             <span
                               aria-hidden="true"
                               className="euiFormControlLayoutCustomIcon__icon"
                               data-euiicon-type="arrowDown"
+                              size="m"
                             />
                           </EuiIcon>
                         </span>
@@ -4568,6 +4600,7 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = `
                             </select>
                           </EuiValidatableControl>
                           <EuiFormControlLayoutIcons
+                            compressed={false}
                             icon={
                               Object {
                                 "side": "right",
@@ -4580,6 +4613,7 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = `
                               className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                             >
                               <EuiFormControlLayoutCustomIcon
+                                size="m"
                                 type="arrowDown"
                               >
                                 <span
@@ -4588,12 +4622,14 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = `
                                   <EuiIcon
                                     aria-hidden="true"
                                     className="euiFormControlLayoutCustomIcon__icon"
+                                    size="m"
                                     type="arrowDown"
                                   >
                                     <span
                                       aria-hidden="true"
                                       className="euiFormControlLayoutCustomIcon__icon"
                                       data-euiicon-type="arrowDown"
+                                      size="m"
                                     />
                                   </EuiIcon>
                                 </span>
@@ -5261,6 +5297,7 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = `
                             </select>
                           </EuiValidatableControl>
                           <EuiFormControlLayoutIcons
+                            compressed={false}
                             icon={
                               Object {
                                 "side": "right",
@@ -5273,6 +5310,7 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = `
                               className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                             >
                               <EuiFormControlLayoutCustomIcon
+                                size="m"
                                 type="arrowDown"
                               >
                                 <span
@@ -5281,12 +5319,14 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = `
                                   <EuiIcon
                                     aria-hidden="true"
                                     className="euiFormControlLayoutCustomIcon__icon"
+                                    size="m"
                                     type="arrowDown"
                                   >
                                     <span
                                       aria-hidden="true"
                                       className="euiFormControlLayoutCustomIcon__icon"
                                       data-euiicon-type="arrowDown"
+                                      size="m"
                                     />
                                   </EuiIcon>
                                 </span>
@@ -5478,6 +5518,7 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = `
                   </select>
                 </EuiValidatableControl>
                 <EuiFormControlLayoutIcons
+                  compressed={false}
                   icon={
                     Object {
                       "side": "right",
@@ -5490,6 +5531,7 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = `
                     className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                   >
                     <EuiFormControlLayoutCustomIcon
+                      size="m"
                       type="arrowDown"
                     >
                       <span
@@ -5498,12 +5540,14 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = `
                         <EuiIcon
                           aria-hidden="true"
                           className="euiFormControlLayoutCustomIcon__icon"
+                          size="m"
                           type="arrowDown"
                         >
                           <span
                             aria-hidden="true"
                             className="euiFormControlLayoutCustomIcon__icon"
                             data-euiicon-type="arrowDown"
+                            size="m"
                           />
                         </EuiIcon>
                       </span>
@@ -6074,6 +6118,7 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = `
                     </select>
                   </EuiValidatableControl>
                   <EuiFormControlLayoutIcons
+                    compressed={false}
                     icon={
                       Object {
                         "side": "right",
@@ -6086,6 +6131,7 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = `
                       className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                     >
                       <EuiFormControlLayoutCustomIcon
+                        size="m"
                         type="arrowDown"
                       >
                         <span
@@ -6094,12 +6140,14 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = `
                           <EuiIcon
                             aria-hidden="true"
                             className="euiFormControlLayoutCustomIcon__icon"
+                            size="m"
                             type="arrowDown"
                           >
                             <span
                               aria-hidden="true"
                               className="euiFormControlLayoutCustomIcon__icon"
                               data-euiicon-type="arrowDown"
+                              size="m"
                             />
                           </EuiIcon>
                         </span>
@@ -6469,6 +6517,7 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = `
                             </select>
                           </EuiValidatableControl>
                           <EuiFormControlLayoutIcons
+                            compressed={false}
                             icon={
                               Object {
                                 "side": "right",
@@ -6481,6 +6530,7 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = `
                               className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                             >
                               <EuiFormControlLayoutCustomIcon
+                                size="m"
                                 type="arrowDown"
                               >
                                 <span
@@ -6489,12 +6539,14 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = `
                                   <EuiIcon
                                     aria-hidden="true"
                                     className="euiFormControlLayoutCustomIcon__icon"
+                                    size="m"
                                     type="arrowDown"
                                   >
                                     <span
                                       aria-hidden="true"
                                       className="euiFormControlLayoutCustomIcon__icon"
                                       data-euiicon-type="arrowDown"
+                                      size="m"
                                     />
                                   </EuiIcon>
                                 </span>
@@ -7162,6 +7214,7 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = `
                             </select>
                           </EuiValidatableControl>
                           <EuiFormControlLayoutIcons
+                            compressed={false}
                             icon={
                               Object {
                                 "side": "right",
@@ -7174,6 +7227,7 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = `
                               className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                             >
                               <EuiFormControlLayoutCustomIcon
+                                size="m"
                                 type="arrowDown"
                               >
                                 <span
@@ -7182,12 +7236,14 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = `
                                   <EuiIcon
                                     aria-hidden="true"
                                     className="euiFormControlLayoutCustomIcon__icon"
+                                    size="m"
                                     type="arrowDown"
                                   >
                                     <span
                                       aria-hidden="true"
                                       className="euiFormControlLayoutCustomIcon__icon"
                                       data-euiicon-type="arrowDown"
+                                      size="m"
                                     />
                                   </EuiIcon>
                                 </span>
@@ -7379,6 +7435,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
                   </select>
                 </EuiValidatableControl>
                 <EuiFormControlLayoutIcons
+                  compressed={false}
                   icon={
                     Object {
                       "side": "right",
@@ -7391,6 +7448,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
                     className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                   >
                     <EuiFormControlLayoutCustomIcon
+                      size="m"
                       type="arrowDown"
                     >
                       <span
@@ -7399,12 +7457,14 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
                         <EuiIcon
                           aria-hidden="true"
                           className="euiFormControlLayoutCustomIcon__icon"
+                          size="m"
                           type="arrowDown"
                         >
                           <span
                             aria-hidden="true"
                             className="euiFormControlLayoutCustomIcon__icon"
                             data-euiicon-type="arrowDown"
+                            size="m"
                           />
                         </EuiIcon>
                       </span>
@@ -8174,6 +8234,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
                     </select>
                   </EuiValidatableControl>
                   <EuiFormControlLayoutIcons
+                    compressed={false}
                     icon={
                       Object {
                         "side": "right",
@@ -8186,6 +8247,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
                       className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                     >
                       <EuiFormControlLayoutCustomIcon
+                        size="m"
                         type="arrowDown"
                       >
                         <span
@@ -8194,12 +8256,14 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
                           <EuiIcon
                             aria-hidden="true"
                             className="euiFormControlLayoutCustomIcon__icon"
+                            size="m"
                             type="arrowDown"
                           >
                             <span
                               aria-hidden="true"
                               className="euiFormControlLayoutCustomIcon__icon"
                               data-euiicon-type="arrowDown"
+                              size="m"
                             />
                           </EuiIcon>
                         </span>
@@ -8628,6 +8692,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
                     </select>
                   </EuiValidatableControl>
                   <EuiFormControlLayoutIcons
+                    compressed={false}
                     icon={
                       Object {
                         "side": "right",
@@ -8640,6 +8705,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
                       className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                     >
                       <EuiFormControlLayoutCustomIcon
+                        size="m"
                         type="arrowDown"
                       >
                         <span
@@ -8648,12 +8714,14 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
                           <EuiIcon
                             aria-hidden="true"
                             className="euiFormControlLayoutCustomIcon__icon"
+                            size="m"
                             type="arrowDown"
                           >
                             <span
                               aria-hidden="true"
                               className="euiFormControlLayoutCustomIcon__icon"
                               data-euiicon-type="arrowDown"
+                              size="m"
                             />
                           </EuiIcon>
                         </span>
@@ -9023,6 +9091,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
                             </select>
                           </EuiValidatableControl>
                           <EuiFormControlLayoutIcons
+                            compressed={false}
                             icon={
                               Object {
                                 "side": "right",
@@ -9035,6 +9104,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
                               className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                             >
                               <EuiFormControlLayoutCustomIcon
+                                size="m"
                                 type="arrowDown"
                               >
                                 <span
@@ -9043,12 +9113,14 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
                                   <EuiIcon
                                     aria-hidden="true"
                                     className="euiFormControlLayoutCustomIcon__icon"
+                                    size="m"
                                     type="arrowDown"
                                   >
                                     <span
                                       aria-hidden="true"
                                       className="euiFormControlLayoutCustomIcon__icon"
                                       data-euiicon-type="arrowDown"
+                                      size="m"
                                     />
                                   </EuiIcon>
                                 </span>
@@ -9716,6 +9788,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
                             </select>
                           </EuiValidatableControl>
                           <EuiFormControlLayoutIcons
+                            compressed={false}
                             icon={
                               Object {
                                 "side": "right",
@@ -9728,6 +9801,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
                               className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
                             >
                               <EuiFormControlLayoutCustomIcon
+                                size="m"
                                 type="arrowDown"
                               >
                                 <span
@@ -9736,12 +9810,14 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
                                   <EuiIcon
                                     aria-hidden="true"
                                     className="euiFormControlLayoutCustomIcon__icon"
+                                    size="m"
                                     type="arrowDown"
                                   >
                                     <span
                                       aria-hidden="true"
                                       className="euiFormControlLayoutCustomIcon__icon"
                                       data-euiicon-type="arrowDown"
+                                      size="m"
                                     />
                                   </EuiIcon>
                                 </span>
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"
   />
 </EuiPopover>
 `;
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"
   />
 </I18nProvider>
 `;
@@ -78,6 +79,7 @@ exports[`should only render permalink panel when there are no other panels 1`] =
         },
       ]
     }
+    size="m"
   />
 </I18nProvider>
 `;
@@ -130,6 +132,7 @@ exports[`should render context menu panel when there are more than one panel 1`]
         },
       ]
     }
+    size="m"
   />
 </I18nProvider>
 `;
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"
               />
             </span>
           </div>
@@ -139,6 +140,7 @@ exports[`Storyshots arguments/AxisConfig/components extended 1`] = `
                 aria-hidden="true"
                 className="euiFormControlLayoutCustomIcon__icon"
                 data-euiicon-type="arrowDown"
+                size="s"
               />
             </span>
           </div>
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"
           />
         </span>
       </div>
@@ -118,6 +119,7 @@ Array [
             aria-hidden="true"
             className="euiFormControlLayoutCustomIcon__icon"
             data-euiicon-type="arrowDown"
+            size="s"
           />
         </span>
       </div>
@@ -189,6 +191,7 @@ exports[`Storyshots arguments/DateFormat with preset format 1`] = `
           aria-hidden="true"
           className="euiFormControlLayoutCustomIcon__icon"
           data-euiicon-type="arrowDown"
+          size="s"
         />
       </span>
     </div>
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"
           />
         </span>
       </div>
@@ -138,6 +139,7 @@ Array [
             aria-hidden="true"
             className="euiFormControlLayoutCustomIcon__icon"
             data-euiicon-type="arrowDown"
+            size="s"
           />
         </span>
       </div>
@@ -219,6 +221,7 @@ exports[`Storyshots arguments/NumberFormat with preset format 1`] = `
           aria-hidden="true"
           className="euiFormControlLayoutCustomIcon__icon"
           data-euiicon-type="arrowDown"
+          size="s"
         />
       </span>
     </div>
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"
                   />
                 </span>
               </div>
@@ -222,6 +223,7 @@ exports[`Storyshots components/SavedElementsModal with custom elements 1`] = `
                     aria-hidden="true"
                     className="euiFormControlLayoutCustomIcon__icon"
                     data-euiicon-type="search"
+                    size="m"
                   />
                 </span>
               </div>
@@ -669,6 +671,7 @@ exports[`Storyshots components/SavedElementsModal with text filter 1`] = `
                     aria-hidden="true"
                     className="euiFormControlLayoutCustomIcon__icon"
                     data-euiicon-type="search"
+                    size="m"
                   />
                 </span>
               </div>
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`] = `
 <div
-  className="euiHealth"
+  className="euiHealth euiHealth--textSizeS"
 >
   <div
     className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
@@ -70,7 +70,7 @@ exports[`Storyshots components/Tags/Tag as health 1`] = `
 
 exports[`Storyshots components/Tags/Tag as health with color 1`] = `
 <div
-  className="euiHealth"
+  className="euiHealth euiHealth--textSizeS"
 >
   <div
     className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
diff --git a/x-pack/plugins/canvas/public/components/tag_list/__stories__/__snapshots__/tag_list.stories.storyshot b/x-pack/plugins/canvas/public/components/tag_list/__stories__/__snapshots__/tag_list.stories.storyshot
index 848b684795a82..0298c5155fa35 100644
--- a/x-pack/plugins/canvas/public/components/tag_list/__stories__/__snapshots__/tag_list.stories.storyshot
+++ b/x-pack/plugins/canvas/public/components/tag_list/__stories__/__snapshots__/tag_list.stories.storyshot
@@ -67,7 +67,7 @@ Array [
 exports[`Storyshots components/Tags/TagList with health tags 1`] = `
 Array [
   <div
-    className="euiHealth"
+    className="euiHealth euiHealth--textSizeS"
   >
     <div
       className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
@@ -88,7 +88,7 @@ Array [
     </div>
   </div>,
   <div
-    className="euiHealth"
+    className="euiHealth euiHealth--textSizeS"
   >
     <div
       className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
@@ -109,7 +109,7 @@ Array [
     </div>
   </div>,
   <div
-    className="euiHealth"
+    className="euiHealth euiHealth--textSizeS"
   >
     <div
       className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
diff --git a/x-pack/plugins/canvas/public/components/text_style_picker/__stories__/__snapshots__/text_style_picker.stories.storyshot b/x-pack/plugins/canvas/public/components/text_style_picker/__stories__/__snapshots__/text_style_picker.stories.storyshot
index 2da2ba677c73a..10852e511ea29 100644
--- a/x-pack/plugins/canvas/public/components/text_style_picker/__stories__/__snapshots__/text_style_picker.stories.storyshot
+++ b/x-pack/plugins/canvas/public/components/text_style_picker/__stories__/__snapshots__/text_style_picker.stories.storyshot
@@ -151,6 +151,7 @@ exports[`Storyshots components/TextStylePicker default 1`] = `
                   aria-hidden="true"
                   className="euiFormControlLayoutCustomIcon__icon"
                   data-euiicon-type="arrowDown"
+                  size="s"
                 />
               </span>
             </div>
@@ -587,6 +588,7 @@ exports[`Storyshots components/TextStylePicker interactive 1`] = `
                   aria-hidden="true"
                   className="euiFormControlLayoutCustomIcon__icon"
                   data-euiicon-type="arrowDown"
+                  size="s"
                 />
               </span>
             </div>
diff --git a/x-pack/plugins/canvas/public/components/workpad_templates/examples/__snapshots__/workpad_templates.stories.storyshot b/x-pack/plugins/canvas/public/components/workpad_templates/examples/__snapshots__/workpad_templates.stories.storyshot
index d267ba07078fe..489827246e998 100644
--- a/x-pack/plugins/canvas/public/components/workpad_templates/examples/__snapshots__/workpad_templates.stories.storyshot
+++ b/x-pack/plugins/canvas/public/components/workpad_templates/examples/__snapshots__/workpad_templates.stories.storyshot
@@ -38,6 +38,7 @@ exports[`Storyshots components/WorkpadTemplates default 1`] = `
                 aria-hidden="true"
                 className="euiFormControlLayoutCustomIcon__icon"
                 data-euiicon-type="search"
+                size="m"
               />
             </span>
           </div>
@@ -319,7 +320,7 @@ exports[`Storyshots components/WorkpadTemplates default 1`] = `
                 className="euiTableCellContent euiTableCellContent--overflowingContent"
               >
                 <div
-                  className="euiHealth"
+                  className="euiHealth euiHealth--textSizeS"
                 >
                   <div
                     className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
@@ -340,7 +341,7 @@ exports[`Storyshots components/WorkpadTemplates default 1`] = `
                   </div>
                 </div>
                 <div
-                  className="euiHealth"
+                  className="euiHealth euiHealth--textSizeS"
                 >
                   <div
                     className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
@@ -441,7 +442,7 @@ exports[`Storyshots components/WorkpadTemplates default 1`] = `
                 className="euiTableCellContent euiTableCellContent--overflowingContent"
               >
                 <div
-                  className="euiHealth"
+                  className="euiHealth euiHealth--textSizeS"
                 >
                   <div
                     className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
@@ -462,7 +463,7 @@ exports[`Storyshots components/WorkpadTemplates default 1`] = `
                   </div>
                 </div>
                 <div
-                  className="euiHealth"
+                  className="euiHealth euiHealth--textSizeS"
                 >
                   <div
                     className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
diff --git a/x-pack/plugins/canvas/public/expression_types/arg_types/container_style/__stories__/__snapshots__/extended_template.stories.storyshot b/x-pack/plugins/canvas/public/expression_types/arg_types/container_style/__stories__/__snapshots__/extended_template.stories.storyshot
index e87a78e87fe46..69aa40f388e45 100644
--- a/x-pack/plugins/canvas/public/expression_types/arg_types/container_style/__stories__/__snapshots__/extended_template.stories.storyshot
+++ b/x-pack/plugins/canvas/public/expression_types/arg_types/container_style/__stories__/__snapshots__/extended_template.stories.storyshot
@@ -142,6 +142,7 @@ exports[`Storyshots arguments/ContainerStyle extended 1`] = `
                       aria-hidden="true"
                       className="euiFormControlLayoutCustomIcon__icon"
                       data-euiicon-type="arrowDown"
+                      size="s"
                     />
                   </span>
                 </div>
@@ -206,6 +207,7 @@ exports[`Storyshots arguments/ContainerStyle extended 1`] = `
                       aria-hidden="true"
                       className="euiFormControlLayoutCustomIcon__icon"
                       data-euiicon-type="arrowDown"
+                      size="s"
                     />
                   </span>
                 </div>
@@ -547,6 +549,7 @@ exports[`Storyshots arguments/ContainerStyle/components appearance form 1`] = `
                     aria-hidden="true"
                     className="euiFormControlLayoutCustomIcon__icon"
                     data-euiicon-type="arrowDown"
+                    size="s"
                   />
                 </span>
               </div>
@@ -611,6 +614,7 @@ exports[`Storyshots arguments/ContainerStyle/components appearance form 1`] = `
                     aria-hidden="true"
                     className="euiFormControlLayoutCustomIcon__icon"
                     data-euiicon-type="arrowDown"
+                    size="s"
                   />
                 </span>
               </div>
@@ -962,6 +966,7 @@ exports[`Storyshots arguments/ContainerStyle/components extended template 1`] =
                       aria-hidden="true"
                       className="euiFormControlLayoutCustomIcon__icon"
                       data-euiicon-type="arrowDown"
+                      size="s"
                     />
                   </span>
                 </div>
@@ -1026,6 +1031,7 @@ exports[`Storyshots arguments/ContainerStyle/components extended template 1`] =
                       aria-hidden="true"
                       className="euiFormControlLayoutCustomIcon__icon"
                       data-euiicon-type="arrowDown"
+                      size="s"
                     />
                   </span>
                 </div>
diff --git a/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/__stories__/__snapshots__/extended_template.stories.storyshot b/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/__stories__/__snapshots__/extended_template.stories.storyshot
index 5d666282f9d0e..05add24cf775c 100644
--- a/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/__stories__/__snapshots__/extended_template.stories.storyshot
+++ b/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/__stories__/__snapshots__/extended_template.stories.storyshot
@@ -69,6 +69,7 @@ exports[`Storyshots arguments/SeriesStyle extended 1`] = `
                   aria-hidden="true"
                   className="euiFormControlLayoutCustomIcon__icon"
                   data-euiicon-type="arrowDown"
+                  size="s"
                 />
               </span>
             </div>
@@ -161,6 +162,7 @@ exports[`Storyshots arguments/SeriesStyle extended 1`] = `
                       aria-hidden="true"
                       className="euiFormControlLayoutCustomIcon__icon"
                       data-euiicon-type="arrowDown"
+                      size="s"
                     />
                   </span>
                 </div>
@@ -245,6 +247,7 @@ exports[`Storyshots arguments/SeriesStyle extended 1`] = `
                       aria-hidden="true"
                       className="euiFormControlLayoutCustomIcon__icon"
                       data-euiicon-type="arrowDown"
+                      size="s"
                     />
                   </span>
                 </div>
@@ -329,6 +332,7 @@ exports[`Storyshots arguments/SeriesStyle extended 1`] = `
                       aria-hidden="true"
                       className="euiFormControlLayoutCustomIcon__icon"
                       data-euiicon-type="arrowDown"
+                      size="s"
                     />
                   </span>
                 </div>
diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/__snapshots__/tools_control.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/__snapshots__/tools_control.test.tsx.snap
index a57fcaa393b89..69faf78524326 100644
--- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/__snapshots__/tools_control.test.tsx.snap
+++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/__snapshots__/tools_control.test.tsx.snap
@@ -110,6 +110,7 @@ exports[`Should render cancel button when drawing 1`] = `
             },
           ]
         }
+        size="m"
       />
     </EuiPopover>
   </EuiFlexItem>
@@ -235,6 +236,7 @@ exports[`renders 1`] = `
         },
       ]
     }
+    size="m"
   />
 </EuiPopover>
 `;
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap
index ea37e76bc8494..994e152c2ac4d 100644
--- a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap
+++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap
@@ -109,6 +109,7 @@ exports[`TOCEntryActionsPopover is rendered 1`] = `
         },
       ]
     }
+    size="m"
   />
 </EuiPopover>
 `;
@@ -222,6 +223,7 @@ exports[`TOCEntryActionsPopover should disable fit to data when supportsFitToBou
         },
       ]
     }
+    size="m"
   />
 </EuiPopover>
 `;
@@ -336,6 +338,7 @@ exports[`TOCEntryActionsPopover should have "show layer" action when layer is no
         },
       ]
     }
+    size="m"
   />
 </EuiPopover>
 `;
@@ -418,6 +421,7 @@ exports[`TOCEntryActionsPopover should not show edit actions in read only mode 1
         },
       ]
     }
+    size="m"
   />
 </EuiPopover>
 `;
diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap
index 177ef8b3c6cad..5f09193be90c2 100644
--- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap
+++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap
@@ -936,6 +936,7 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u
                                   />
                                 </EuiValidatableControl>
                                 <EuiFormControlLayoutIcons
+                                  compressed={false}
                                   isLoading={false}
                                 />
                               </div>
diff --git a/x-pack/plugins/security_solution/public/common/components/paginated_table/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/paginated_table/__snapshots__/index.test.tsx.snap
index a2a9c30ca4e1c..778916ad2d07a 100644
--- a/x-pack/plugins/security_solution/public/common/components/paginated_table/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/common/components/paginated_table/__snapshots__/index.test.tsx.snap
@@ -355,6 +355,7 @@ exports[`Paginated Table Component rendering it renders the default load more ta
           "small": "14px",
           "xSmall": "12px",
         },
+        "euiMarkdownEditorMinHeight": "150px",
         "euiPageBackgroundColor": "#1a1b20",
         "euiPaletteColorBlind": Object {
           "euiColorVis0": Object {
diff --git a/x-pack/plugins/uptime/public/components/certificates/__snapshots__/cert_status.test.tsx.snap b/x-pack/plugins/uptime/public/components/certificates/__snapshots__/cert_status.test.tsx.snap
index 0a84278c32018..79f58ee76d18f 100644
--- a/x-pack/plugins/uptime/public/components/certificates/__snapshots__/cert_status.test.tsx.snap
+++ b/x-pack/plugins/uptime/public/components/certificates/__snapshots__/cert_status.test.tsx.snap
@@ -7,7 +7,7 @@ exports[`CertStatus renders expected elements for valid props 1`] = `
 }
 
 <div
-  class="euiHealth"
+  class="euiHealth euiHealth--textSizeS"
 >
   <div
     class="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
diff --git a/x-pack/plugins/uptime/public/components/common/charts/__snapshots__/donut_chart.test.tsx.snap b/x-pack/plugins/uptime/public/components/common/charts/__snapshots__/donut_chart.test.tsx.snap
index 21d65f63783c5..967d078bde210 100644
--- a/x-pack/plugins/uptime/public/components/common/charts/__snapshots__/donut_chart.test.tsx.snap
+++ b/x-pack/plugins/uptime/public/components/common/charts/__snapshots__/donut_chart.test.tsx.snap
@@ -469,7 +469,7 @@ exports[`DonutChart component renders a donut chart 1`] = `
           class="euiFlexItem euiFlexItem--flexGrowZero c1"
         >
           <div
-            class="euiHealth"
+            class="euiHealth euiHealth--textSizeS"
           >
             <div
               class="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
@@ -511,7 +511,7 @@ exports[`DonutChart component renders a donut chart 1`] = `
           class="euiFlexItem euiFlexItem--flexGrowZero c1"
         >
           <div
-            class="euiHealth"
+            class="euiHealth euiHealth--textSizeS"
           >
             <div
               class="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
@@ -624,7 +624,7 @@ exports[`DonutChart component renders a green check when all monitors are up 1`]
           class="euiFlexItem euiFlexItem--flexGrowZero c2"
         >
           <div
-            class="euiHealth"
+            class="euiHealth euiHealth--textSizeS"
           >
             <div
               class="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
@@ -666,7 +666,7 @@ exports[`DonutChart component renders a green check when all monitors are up 1`]
           class="euiFlexItem euiFlexItem--flexGrowZero c2"
         >
           <div
-            class="euiHealth"
+            class="euiHealth euiHealth--textSizeS"
           >
             <div
               class="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
diff --git a/yarn.lock b/yarn.lock
index e7870415b0dda..208231bfa6ed7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1439,10 +1439,10 @@
   resolved "https://registry.yarnpkg.com/@elastic/eslint-plugin-eui/-/eslint-plugin-eui-0.0.2.tgz#56b9ef03984a05cc213772ae3713ea8ef47b0314"
   integrity sha512-IoxURM5zraoQ7C8f+mJb9HYSENiZGgRVcG4tLQxE61yHNNRDXtGDWTZh8N1KIHcsqN1CEPETjuzBXkJYF/fDiQ==
 
-"@elastic/eui@31.0.0":
-  version "31.0.0"
-  resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-31.0.0.tgz#7d17386c04a0ad343d70c3652902fcd3f46ed337"
-  integrity sha512-oj63HpQQKg/Cgwz5B0ZBQCkcgZiEdQzBT9PbmEiR/VRz5P0WqJpgZPyIF7jiFaFlGP1a9hPjkUTo+ramWNCpiw==
+"@elastic/eui@31.3.0":
+  version "31.3.0"
+  resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-31.3.0.tgz#f39eecc09d588e4b22150faceb67e5e169afbbd8"
+  integrity sha512-1Sjhf5HVakx7VGWQkKP8wzGUf7HzyoNnAxjg5P3NH8k+ctJFagS1Wlz9zogwClEuj3FMTMC4tzbJyo06OgHECw==
   dependencies:
     "@types/chroma-js" "^2.0.0"
     "@types/lodash" "^4.14.160"

From 14fa82dc54a3bd96ef68767f78cb8b6a8367fbae Mon Sep 17 00:00:00 2001
From: Patrick Mueller <pmuellr@gmail.com>
Date: Mon, 25 Jan 2021 16:43:46 -0500
Subject: [PATCH 41/46] [alerts] adds support for index threshold index param
 string type (#88540)

resolves https://github.com/elastic/kibana/issues/68575

The index threshold alert defines an `index` parameter which is
typed as `string | string[]`.  However the UI for this alert has
been typing it as only `string[]`.

This PR changes the UI to work with an incoming string value for
this parameter.  If the parameter is edited in the UI, it will always
be set as an array, even if there is only one element.
---
 .../alert_types/threshold/expression.tsx      | 21 ++++++++++++-------
 .../public/alert_types/threshold/types.ts     |  2 +-
 .../alert_types/threshold/validation.test.ts  |  2 +-
 .../apps/triggers_actions_ui/details.ts       |  2 +-
 4 files changed, 17 insertions(+), 10 deletions(-)

diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx
index 35b62d1f73f20..8348a797972ae 100644
--- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx
+++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx
@@ -75,6 +75,12 @@ function isString(value: unknown): value is string {
   return typeof value === 'string';
 }
 
+// normalize the `index` parameter to be a string array
+function indexParamToArray(index: string | string[]): string[] {
+  if (!index) return [];
+  return isString(index) ? [index] : index;
+}
+
 export const IndexThresholdAlertTypeExpression: React.FunctionComponent<
   AlertTypeParamsExpressionProps<IndexThresholdAlertParams>
 > = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, charts, data }) => {
@@ -92,6 +98,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent<
     timeWindowUnit,
   } = alertParams;
 
+  const indexArray = indexParamToArray(index);
   const { http } = useKibana<KibanaDeps>().services;
 
   const [indexPopoverOpen, setIndexPopoverOpen] = useState(false);
@@ -131,8 +138,8 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent<
       threshold: threshold ?? DEFAULT_VALUES.THRESHOLD,
     });
 
-    if (index && index.length > 0) {
-      const currentEsFields = await getFields(http, index);
+    if (indexArray.length > 0) {
+      const currentEsFields = await getFields(http, indexArray);
       const timeFields = getTimeFieldOptions(currentEsFields);
 
       setEsFields(currentEsFields);
@@ -170,7 +177,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent<
             defaultMessage="Indices to query"
           />
         }
-        isInvalid={errors.index.length > 0 && index !== undefined}
+        isInvalid={errors.index.length > 0 && indexArray.length > 0}
         error={errors.index}
         helpText={
           <FormattedMessage
@@ -183,11 +190,11 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent<
           fullWidth
           async
           isLoading={isIndiciesLoading}
-          isInvalid={errors.index.length > 0 && index !== undefined}
+          isInvalid={errors.index.length > 0 && indexArray.length > 0}
           noSuggestions={!indexOptions.length}
           options={indexOptions}
           data-test-subj="thresholdIndexesComboBox"
-          selectedOptions={(index || []).map((anIndex: string) => {
+          selectedOptions={indexArray.map((anIndex: string) => {
             return {
               label: anIndex,
               value: anIndex,
@@ -306,12 +313,12 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent<
             description={i18n.translate('xpack.stackAlerts.threshold.ui.alertParams.indexLabel', {
               defaultMessage: 'index',
             })}
-            value={index && index.length > 0 ? renderIndices(index) : firstFieldOption.text}
+            value={indexArray.length > 0 ? renderIndices(indexArray) : firstFieldOption.text}
             isActive={indexPopoverOpen}
             onClick={() => {
               setIndexPopoverOpen(true);
             }}
-            isInvalid={!(index && index.length > 0 && timeField !== '')}
+            isInvalid={!(indexArray.length > 0 && timeField !== '')}
           />
         }
         isOpen={indexPopoverOpen}
diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts b/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts
index 4868b92feaeb9..32460c19ebdf0 100644
--- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts
+++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts
@@ -27,7 +27,7 @@ export interface GroupByType {
 }
 
 export interface IndexThresholdAlertParams extends AlertTypeParams {
-  index: string[];
+  index: string | string[];
   timeField?: string;
   aggType: string;
   aggField?: string;
diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/validation.test.ts b/x-pack/plugins/stack_alerts/public/alert_types/threshold/validation.test.ts
index 1f24a094d0ece..d8f2d24b106f5 100644
--- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/validation.test.ts
+++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/validation.test.ts
@@ -32,7 +32,7 @@ describe('expression params validation', () => {
   });
   test('if aggField property is invalid should return proper error message', () => {
     const initialParams: IndexThresholdAlertParams = {
-      index: ['test'],
+      index: 'test',
       aggType: 'avg',
       threshold: [],
       timeWindowSize: 1,
diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts
index 1ed0b38a238b8..35e2e03969023 100644
--- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts
+++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts
@@ -228,7 +228,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
             timeWindowUnit: 'm',
             groupBy: 'all',
             threshold: [1000, 5000],
-            index: ['.kibana_1'],
+            index: '.kibana_1',
             timeField: 'alert',
           },
           actions: [

From 2ae2692f2babc2a13165cff0fb69a625acd017cc Mon Sep 17 00:00:00 2001
From: Phillip Burch <phillip.burch@live.com>
Date: Mon, 25 Jan 2021 16:04:34 -0600
Subject: [PATCH 42/46] Add null check for empty process data (#89187)

---
 .../infra/server/lib/host_details/process_list.ts        | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/x-pack/plugins/infra/server/lib/host_details/process_list.ts b/x-pack/plugins/infra/server/lib/host_details/process_list.ts
index e9d35f3601634..267245098b17a 100644
--- a/x-pack/plugins/infra/server/lib/host_details/process_list.ts
+++ b/x-pack/plugins/infra/server/lib/host_details/process_list.ts
@@ -125,9 +125,12 @@ export const getProcessList = async (
         command: bucket.key,
       };
     });
-    const {
-      summary,
-    } = result.aggregations!.summaryEvent.summary.hits.hits[0]._source.system.process;
+
+    let summary: { [p: string]: number } = {};
+    if (result.aggregations!.summaryEvent.summary.hits.hits.length) {
+      summary = result.aggregations!.summaryEvent.summary.hits.hits[0]._source.system.process
+        .summary;
+    }
 
     return {
       processList,

From fb536f54286a20766a87a88698ca038716e767cb Mon Sep 17 00:00:00 2001
From: Andrea Del Rio <delrio.andre@gmail.com>
Date: Mon, 25 Jan 2021 14:14:47 -0800
Subject: [PATCH 43/46] Make toolbar_button a shared component (#88386)

---
 src/plugins/kibana_react/public/index.ts      |   1 +
 .../toolbar_button.test.tsx.snap              | 199 ++++++++++++++++++
 .../public/toolbar_button/index.ts            |   9 +
 .../toolbar_button}/toolbar_button.scss       |  19 +-
 .../toolbar_button/toolbar_button.test.tsx    |  47 +++++
 .../public/toolbar_button}/toolbar_button.tsx |  32 ++-
 .../config_panel/layer_settings.tsx           |   2 +-
 .../workspace_panel/chart_switch.tsx          |   2 +-
 .../change_indexpattern.tsx                   |   2 +-
 .../lens/public/shared_components/index.ts    |   1 -
 .../shared_components/toolbar_popover.tsx     |   2 +-
 .../axis_settings_popover.tsx                 |   3 +-
 12 files changed, 292 insertions(+), 27 deletions(-)
 create mode 100644 src/plugins/kibana_react/public/toolbar_button/__snapshots__/toolbar_button.test.tsx.snap
 create mode 100644 src/plugins/kibana_react/public/toolbar_button/index.ts
 rename {x-pack/plugins/lens/public/shared_components => src/plugins/kibana_react/public/toolbar_button}/toolbar_button.scss (81%)
 create mode 100644 src/plugins/kibana_react/public/toolbar_button/toolbar_button.test.tsx
 rename {x-pack/plugins/lens/public/shared_components => src/plugins/kibana_react/public/toolbar_button}/toolbar_button.tsx (61%)

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`] = `
+<EuiButton
+  className="kbnToolbarButton kbnToolbarButton--bold kbnToolbarButton--m"
+  color="text"
+  contentProps={
+    Object {
+      "className": "kbnToolbarButton__content",
+    }
+  }
+  data-test-subj=""
+  iconSide="right"
+  iconType="arrowDown"
+  onClick={[Function]}
+  size="m"
+  textProps={
+    Object {
+      "className": "kbnToolbarButton__text",
+    }
+  }
+/>
+`;
+
+exports[`font weights normal is applied 1`] = `
+<EuiButton
+  className="kbnToolbarButton kbnToolbarButton--normal kbnToolbarButton--m"
+  color="text"
+  contentProps={
+    Object {
+      "className": "kbnToolbarButton__content",
+    }
+  }
+  data-test-subj=""
+  iconSide="right"
+  iconType="arrowDown"
+  onClick={[Function]}
+  size="m"
+  textProps={
+    Object {
+      "className": "kbnToolbarButton__text",
+    }
+  }
+/>
+`;
+
+exports[`hasArrow is rendered 1`] = `
+<EuiButton
+  className="kbnToolbarButton kbnToolbarButton--normal kbnToolbarButton--m"
+  color="text"
+  contentProps={
+    Object {
+      "className": "kbnToolbarButton__content",
+    }
+  }
+  data-test-subj=""
+  iconSide="right"
+  iconType="arrowDown"
+  onClick={[Function]}
+  size="m"
+  textProps={
+    Object {
+      "className": "kbnToolbarButton__text",
+    }
+  }
+/>
+`;
+
+exports[`positions center is applied 1`] = `
+<EuiButton
+  className="kbnToolbarButton toolbarButton--groupCenter kbnToolbarButton--normal kbnToolbarButton--m"
+  color="text"
+  contentProps={
+    Object {
+      "className": "kbnToolbarButton__content",
+    }
+  }
+  data-test-subj=""
+  iconSide="right"
+  iconType="arrowDown"
+  onClick={[Function]}
+  size="m"
+  textProps={
+    Object {
+      "className": "kbnToolbarButton__text",
+    }
+  }
+/>
+`;
+
+exports[`positions left is applied 1`] = `
+<EuiButton
+  className="kbnToolbarButton toolbarButton--groupLeft kbnToolbarButton--normal kbnToolbarButton--m"
+  color="text"
+  contentProps={
+    Object {
+      "className": "kbnToolbarButton__content",
+    }
+  }
+  data-test-subj=""
+  iconSide="right"
+  iconType="arrowDown"
+  onClick={[Function]}
+  size="m"
+  textProps={
+    Object {
+      "className": "kbnToolbarButton__text",
+    }
+  }
+/>
+`;
+
+exports[`positions none is applied 1`] = `
+<EuiButton
+  className="kbnToolbarButton kbnToolbarButton--normal kbnToolbarButton--m"
+  color="text"
+  contentProps={
+    Object {
+      "className": "kbnToolbarButton__content",
+    }
+  }
+  data-test-subj=""
+  iconSide="right"
+  iconType="arrowDown"
+  onClick={[Function]}
+  size="m"
+  textProps={
+    Object {
+      "className": "kbnToolbarButton__text",
+    }
+  }
+/>
+`;
+
+exports[`positions right is applied 1`] = `
+<EuiButton
+  className="kbnToolbarButton toolbarButton--groupRight kbnToolbarButton--normal kbnToolbarButton--m"
+  color="text"
+  contentProps={
+    Object {
+      "className": "kbnToolbarButton__content",
+    }
+  }
+  data-test-subj=""
+  iconSide="right"
+  iconType="arrowDown"
+  onClick={[Function]}
+  size="m"
+  textProps={
+    Object {
+      "className": "kbnToolbarButton__text",
+    }
+  }
+/>
+`;
+
+exports[`sizes m is applied 1`] = `
+<EuiButton
+  className="kbnToolbarButton kbnToolbarButton--normal kbnToolbarButton--m"
+  color="text"
+  contentProps={
+    Object {
+      "className": "kbnToolbarButton__content",
+    }
+  }
+  data-test-subj=""
+  iconSide="right"
+  iconType="arrowDown"
+  onClick={[Function]}
+  size="m"
+  textProps={
+    Object {
+      "className": "kbnToolbarButton__text",
+    }
+  }
+/>
+`;
+
+exports[`sizes s is applied 1`] = `
+<EuiButton
+  className="kbnToolbarButton kbnToolbarButton--normal kbnToolbarButton--s"
+  color="text"
+  contentProps={
+    Object {
+      "className": "kbnToolbarButton__content",
+    }
+  }
+  data-test-subj=""
+  iconSide="right"
+  iconType="arrowDown"
+  onClick={[Function]}
+  size="s"
+  textProps={
+    Object {
+      "className": "kbnToolbarButton__text",
+    }
+  }
+/>
+`;
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(<ToolbarButton onClick={noop} size={size} />);
+      expect(component).toMatchSnapshot();
+    });
+  });
+});
+
+describe('positions', () => {
+  POSITIONS.forEach((position) => {
+    test(`${position} is applied`, () => {
+      const component = shallow(<ToolbarButton onClick={noop} groupPosition={position} />);
+      expect(component).toMatchSnapshot();
+    });
+  });
+});
+
+describe('font weights', () => {
+  WEIGHTS.forEach((weight) => {
+    test(`${weight} is applied`, () => {
+      const component = shallow(<ToolbarButton onClick={noop} fontWeight={weight} />);
+      expect(component).toMatchSnapshot();
+    });
+  });
+});
+
+describe('hasArrow', () => {
+  it('is rendered', () => {
+    const component = shallow(<ToolbarButton onClick={noop} hasArrow />);
+    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<EuiButtonProps['size']> = ['s', 'm'];
+
 export type ToolbarButtonProps = PropsOf<typeof EuiButton> & {
   /**
    * Determines prominence
    */
-  fontWeight?: 'normal' | 'bold';
+  fontWeight?: Weights;
   /**
    * Smaller buttons also remove extra shadow for less prominence
    */
@@ -32,7 +42,7 @@ export type ToolbarButtonProps = PropsOf<typeof EuiButton> & {
   /**
    * Adjusts the borders for groupings
    */
-  groupPosition?: 'none' | 'left' | 'center' | 'right';
+  groupPosition?: ButtonPositions;
   dataTestSubj?: string;
 };
 
@@ -47,9 +57,9 @@ export const ToolbarButton: React.FunctionComponent<ToolbarButtonProps> = ({
   ...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<ToolbarButtonProps> = ({
       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/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx
index bc537e5a7d689..d3ca72a02940d 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx
@@ -9,7 +9,7 @@ import { EuiPopover, EuiToolTip } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import { NativeRenderer } from '../../../native_renderer';
 import { Visualization, VisualizationLayerWidgetProps } from '../../../types';
-import { ToolbarButton } from '../../../shared_components';
+import { ToolbarButton } from '../../../../../../../src/plugins/kibana_react/public';
 
 export function LayerSettings({
   layerId,
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx
index 659626149aef2..474720e638939 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx
@@ -24,7 +24,7 @@ import { Visualization, FramePublicAPI, Datasource } from '../../../types';
 import { Action } from '../state_management';
 import { getSuggestions, switchToSuggestion, Suggestion } from '../suggestion_helpers';
 import { trackUiEvent } from '../../../lens_ui_telemetry';
-import { ToolbarButton } from '../../../shared_components';
+import { ToolbarButton } from '../../../../../../../src/plugins/kibana_react/public';
 
 interface VisualizationSelection {
   visualizationId: string;
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/change_indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/change_indexpattern.tsx
index 25cb34d19beb8..f2d72079c2185 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/change_indexpattern.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/change_indexpattern.tsx
@@ -9,7 +9,7 @@ import React, { useState } from 'react';
 import { EuiPopover, EuiPopoverTitle, EuiSelectable, EuiSelectableProps } from '@elastic/eui';
 import { IndexPatternRef } from './types';
 import { trackUiEvent } from '../lens_ui_telemetry';
-import { ToolbarButtonProps, ToolbarButton } from '../shared_components';
+import { ToolbarButton, ToolbarButtonProps } from '../../../../../src/plugins/kibana_react/public';
 
 export type ChangeIndexPatternTriggerProps = ToolbarButtonProps & {
   label: string;
diff --git a/x-pack/plugins/lens/public/shared_components/index.ts b/x-pack/plugins/lens/public/shared_components/index.ts
index 622bf5397c935..c4e79c3f1bcb8 100644
--- a/x-pack/plugins/lens/public/shared_components/index.ts
+++ b/x-pack/plugins/lens/public/shared_components/index.ts
@@ -6,6 +6,5 @@
 
 export * from './empty_placeholder';
 export { ToolbarPopoverProps, ToolbarPopover } from './toolbar_popover';
-export { ToolbarButtonProps, ToolbarButton } from './toolbar_button';
 export { LegendSettingsPopover } from './legend_settings_popover';
 export { PalettePicker } from './palette_picker';
diff --git a/x-pack/plugins/lens/public/shared_components/toolbar_popover.tsx b/x-pack/plugins/lens/public/shared_components/toolbar_popover.tsx
index cf2268c6eadf2..adc6d082f514f 100644
--- a/x-pack/plugins/lens/public/shared_components/toolbar_popover.tsx
+++ b/x-pack/plugins/lens/public/shared_components/toolbar_popover.tsx
@@ -6,8 +6,8 @@
 
 import React, { useState } from 'react';
 import { EuiFlexItem, EuiPopover, EuiIcon, EuiPopoverTitle, IconType } from '@elastic/eui';
-import { ToolbarButton, ToolbarButtonProps } from './toolbar_button';
 import { EuiIconLegend } from '../assets/legend';
+import { ToolbarButton, ToolbarButtonProps } from '../../../../../src/plugins/kibana_react/public';
 
 const typeToIconMap: { [type: string]: string | IconType } = {
   legend: EuiIconLegend as IconType,
diff --git a/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx b/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx
index 45ec7098aa639..931e62ea1d13f 100644
--- a/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx
+++ b/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx
@@ -16,12 +16,13 @@ import {
 } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import { LayerConfig, AxesSettingsConfig } from './types';
-import { ToolbarPopover, ToolbarButtonProps } from '../shared_components';
+import { ToolbarPopover } from '../shared_components';
 import { isHorizontalChart } from './state_helpers';
 import { EuiIconAxisBottom } from '../assets/axis_bottom';
 import { EuiIconAxisLeft } from '../assets/axis_left';
 import { EuiIconAxisRight } from '../assets/axis_right';
 import { EuiIconAxisTop } from '../assets/axis_top';
+import { ToolbarButtonProps } from '../../../../../src/plugins/kibana_react/public';
 
 type AxesSettingsConfigKeys = keyof AxesSettingsConfig;
 export interface AxisSettingsPopoverProps {

From 58e629ed7690feb11076a02eff461368cbe8ff32 Mon Sep 17 00:00:00 2001
From: Brian Seeders <brian.seeders@elastic.co>
Date: Mon, 25 Jan 2021 17:30:40 -0500
Subject: [PATCH 44/46] [CI] [TeamCity] Bump security_solution agent size to
 match other ciGroups, and build missing default plugin (#89241)

---
 .ci/teamcity/default/build.sh                         | 1 +
 .teamcity/src/builds/default/DefaultFunctionalBase.kt | 4 ++++
 2 files changed, 5 insertions(+)

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/.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()
   }

From b93b3a4fc19a3d7c43f3d9b26dc6ce43d1bca73c Mon Sep 17 00:00:00 2001
From: Stacey Gammon <gammon@elastic.co>
Date: Mon, 25 Jan 2021 18:06:09 -0500
Subject: [PATCH 45/46] fix bad link (#89222)

---
 dev_docs/kibana_platform_plugin_intro.mdx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dev_docs/kibana_platform_plugin_intro.mdx b/dev_docs/kibana_platform_plugin_intro.mdx
index 412b8eb93b00f..3303561fae069 100644
--- a/dev_docs/kibana_platform_plugin_intro.mdx
+++ b/dev_docs/kibana_platform_plugin_intro.mdx
@@ -76,7 +76,7 @@ We recognize the need to better clarify the relationship between core functional
 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 [how plugins interact with eachother and core](#how-plugins-interact-with-each-other-and-core) for more information.
+section on <DocLink id="kibPlatformIntro" section="how-plugins-interact-with-each-other-and-core" text="how plugins interact with eachother and core"/> for more information.
 
 ## The anatomy of a plugin
 

From 0c9c2c91f5f51d29d355622c0c5c314433c63662 Mon Sep 17 00:00:00 2001
From: Spencer <email@spalger.com>
Date: Mon, 25 Jan 2021 16:30:18 -0700
Subject: [PATCH 46/46] [browserslist] remove unnecessary browsers (#89186)

Co-authored-by: spalger <spalger@users.noreply.github.com>
---
 .browserslistrc                                |  8 ++++----
 .../basic_optimization.test.ts.snap            |  2 +-
 yarn.lock                                      | 18 ++++--------------
 3 files changed, 9 insertions(+), 19 deletions(-)

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/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<chunkIds.length;i++){chunkId=chunkIds[i];if(Object.prototype.hasOwnProperty.call(installedChunks,chunkId)&&installedChunks[chunkId]){resolves.push(installedChunks[chunkId][0])}installedChunks[chunkId]=0}for(moduleId in moreModules){if(Object.prototype.hasOwnProperty.call(moreModules,moduleId)){modules[moduleId]=moreModules[moduleId]}}if(parentJsonpFunction)parentJsonpFunction(data);while(resolves.length){resolves.shift()()}}var installedModules={};var installedChunks={0:0};function jsonpScriptSrc(chunkId){return __webpack_require__.p+\\"foo.chunk.\\"+chunkId+\\".js\\"}function __webpack_require__(moduleId){if(installedModules[moduleId]){return installedModules[moduleId].exports}var module=installedModules[moduleId]={i:moduleId,l:false,exports:{}};modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);module.l=true;return module.exports}__webpack_require__.e=function requireEnsure(chunkId){var promises=[];var installedChunkData=installedChunks[chunkId];if(installedChunkData!==0){if(installedChunkData){promises.push(installedChunkData[2])}else{var promise=new Promise((function(resolve,reject){installedChunkData=installedChunks[chunkId]=[resolve,reject]}));promises.push(installedChunkData[2]=promise);var script=document.createElement(\\"script\\");var onScriptComplete;script.charset=\\"utf-8\\";script.timeout=120;if(__webpack_require__.nc){script.setAttribute(\\"nonce\\",__webpack_require__.nc)}script.src=jsonpScriptSrc(chunkId);var error=new Error;onScriptComplete=function(event){script.onerror=script.onload=null;clearTimeout(timeout);var chunk=installedChunks[chunkId];if(chunk!==0){if(chunk){var errorType=event&&(event.type===\\"load\\"?\\"missing\\":event.type);var realSrc=event&&event.target&&event.target.src;error.message=\\"Loading chunk \\"+chunkId+\\" failed.\\\\n(\\"+errorType+\\": \\"+realSrc+\\")\\";error.name=\\"ChunkLoadError\\";error.type=errorType;error.request=realSrc;chunk[1](error)}installedChunks[chunkId]=undefined}};var timeout=setTimeout((function(){onScriptComplete({type:\\"timeout\\",target:script})}),12e4);script.onerror=script.onload=onScriptComplete;document.head.appendChild(script)}}return Promise.all(promises)};__webpack_require__.m=modules;__webpack_require__.c=installedModules;__webpack_require__.d=function(exports,name,getter){if(!__webpack_require__.o(exports,name)){Object.defineProperty(exports,name,{enumerable:true,get:getter})}};__webpack_require__.r=function(exports){if(typeof Symbol!==\\"undefined\\"&&Symbol.toStringTag){Object.defineProperty(exports,Symbol.toStringTag,{value:\\"Module\\"})}Object.defineProperty(exports,\\"__esModule\\",{value:true})};__webpack_require__.t=function(value,mode){if(mode&1)value=__webpack_require__(value);if(mode&8)return value;if(mode&4&&typeof value===\\"object\\"&&value&&value.__esModule)return value;var ns=Object.create(null);__webpack_require__.r(ns);Object.defineProperty(ns,\\"default\\",{enumerable:true,value:value});if(mode&2&&typeof value!=\\"string\\")for(var key in value)__webpack_require__.d(ns,key,function(key){return value[key]}.bind(null,key));return ns};__webpack_require__.n=function(module){var getter=module&&module.__esModule?function getDefault(){return module[\\"default\\"]}:function getModuleExports(){return module};__webpack_require__.d(getter,\\"a\\",getter);return getter};__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)};__webpack_require__.p=\\"\\";__webpack_require__.oe=function(err){console.error(err);throw err};var jsonpArray=window[\\"foo_bundle_jsonpfunction\\"]=window[\\"foo_bundle_jsonpfunction\\"]||[];var oldJsonpFunction=jsonpArray.push.bind(jsonpArray);jsonpArray.push=webpackJsonpCallback;jsonpArray=jsonpArray.slice();for(var i=0;i<jsonpArray.length;i++)webpackJsonpCallback(jsonpArray[i]);var parentJsonpFunction=oldJsonpFunction;return __webpack_require__(__webpack_require__.s=0)})([function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_val_loader_dist_cjs_js_key_foo_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(1);var _node_modules_val_loader_dist_cjs_js_key_foo_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_val_loader_dist_cjs_js_key_foo_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0__);__kbnBundles__.define(\\"plugin/foo/public\\",__webpack_require__,2)},function(module,exports,__webpack_require__){__webpack_require__.p=window.__kbnPublicPath__[\\"foo\\"]},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,\\"fooLibFn\\",(function(){return fooLibFn}));__webpack_require__.d(__webpack_exports__,\\"ext\\",(function(){return ext}));__webpack_require__.d(__webpack_exports__,\\"getFoo\\",(function(){return getFoo}));function fooLibFn(){return\\"foo\\"}const ext=\\"TRUE\\";function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg);var value=info.value}catch(error){reject(error);return}if(info.done){resolve(value)}else{Promise.resolve(value).then(_next,_throw)}}function _asyncToGenerator(fn){return function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,\\"next\\",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,\\"throw\\",err)}_next(undefined)}))}}function getFoo(){return _getFoo.apply(this,arguments)}function _getFoo(){_getFoo=_asyncToGenerator(regeneratorRuntime.mark((function _callee(){return regeneratorRuntime.wrap((function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:_context.next=2;return __webpack_require__.e(1).then(__webpack_require__.bind(null,3));case 2:return _context.abrupt(\\"return\\",_context.sent);case 3:case\\"end\\":return _context.stop()}}),_callee)})));return _getFoo.apply(this,arguments)}}]);"`;
+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<chunkIds.length;i++){chunkId=chunkIds[i];if(Object.prototype.hasOwnProperty.call(installedChunks,chunkId)&&installedChunks[chunkId]){resolves.push(installedChunks[chunkId][0])}installedChunks[chunkId]=0}for(moduleId in moreModules){if(Object.prototype.hasOwnProperty.call(moreModules,moduleId)){modules[moduleId]=moreModules[moduleId]}}if(parentJsonpFunction)parentJsonpFunction(data);while(resolves.length){resolves.shift()()}}var installedModules={};var installedChunks={0:0};function jsonpScriptSrc(chunkId){return __webpack_require__.p+\\"foo.chunk.\\"+chunkId+\\".js\\"}function __webpack_require__(moduleId){if(installedModules[moduleId]){return installedModules[moduleId].exports}var module=installedModules[moduleId]={i:moduleId,l:false,exports:{}};modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);module.l=true;return module.exports}__webpack_require__.e=function requireEnsure(chunkId){var promises=[];var installedChunkData=installedChunks[chunkId];if(installedChunkData!==0){if(installedChunkData){promises.push(installedChunkData[2])}else{var promise=new Promise((function(resolve,reject){installedChunkData=installedChunks[chunkId]=[resolve,reject]}));promises.push(installedChunkData[2]=promise);var script=document.createElement(\\"script\\");var onScriptComplete;script.charset=\\"utf-8\\";script.timeout=120;if(__webpack_require__.nc){script.setAttribute(\\"nonce\\",__webpack_require__.nc)}script.src=jsonpScriptSrc(chunkId);var error=new Error;onScriptComplete=function(event){script.onerror=script.onload=null;clearTimeout(timeout);var chunk=installedChunks[chunkId];if(chunk!==0){if(chunk){var errorType=event&&(event.type===\\"load\\"?\\"missing\\":event.type);var realSrc=event&&event.target&&event.target.src;error.message=\\"Loading chunk \\"+chunkId+\\" failed.\\\\n(\\"+errorType+\\": \\"+realSrc+\\")\\";error.name=\\"ChunkLoadError\\";error.type=errorType;error.request=realSrc;chunk[1](error)}installedChunks[chunkId]=undefined}};var timeout=setTimeout((function(){onScriptComplete({type:\\"timeout\\",target:script})}),12e4);script.onerror=script.onload=onScriptComplete;document.head.appendChild(script)}}return Promise.all(promises)};__webpack_require__.m=modules;__webpack_require__.c=installedModules;__webpack_require__.d=function(exports,name,getter){if(!__webpack_require__.o(exports,name)){Object.defineProperty(exports,name,{enumerable:true,get:getter})}};__webpack_require__.r=function(exports){if(typeof Symbol!==\\"undefined\\"&&Symbol.toStringTag){Object.defineProperty(exports,Symbol.toStringTag,{value:\\"Module\\"})}Object.defineProperty(exports,\\"__esModule\\",{value:true})};__webpack_require__.t=function(value,mode){if(mode&1)value=__webpack_require__(value);if(mode&8)return value;if(mode&4&&typeof value===\\"object\\"&&value&&value.__esModule)return value;var ns=Object.create(null);__webpack_require__.r(ns);Object.defineProperty(ns,\\"default\\",{enumerable:true,value:value});if(mode&2&&typeof value!=\\"string\\")for(var key in value)__webpack_require__.d(ns,key,function(key){return value[key]}.bind(null,key));return ns};__webpack_require__.n=function(module){var getter=module&&module.__esModule?function getDefault(){return module[\\"default\\"]}:function getModuleExports(){return module};__webpack_require__.d(getter,\\"a\\",getter);return getter};__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)};__webpack_require__.p=\\"\\";__webpack_require__.oe=function(err){console.error(err);throw err};var jsonpArray=window[\\"foo_bundle_jsonpfunction\\"]=window[\\"foo_bundle_jsonpfunction\\"]||[];var oldJsonpFunction=jsonpArray.push.bind(jsonpArray);jsonpArray.push=webpackJsonpCallback;jsonpArray=jsonpArray.slice();for(var i=0;i<jsonpArray.length;i++)webpackJsonpCallback(jsonpArray[i]);var parentJsonpFunction=oldJsonpFunction;return __webpack_require__(__webpack_require__.s=0)})([function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_val_loader_dist_cjs_js_key_foo_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(1);var _node_modules_val_loader_dist_cjs_js_key_foo_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_val_loader_dist_cjs_js_key_foo_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0__);__kbnBundles__.define(\\"plugin/foo/public\\",__webpack_require__,2)},function(module,exports,__webpack_require__){__webpack_require__.p=window.__kbnPublicPath__[\\"foo\\"]},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,\\"fooLibFn\\",(function(){return fooLibFn}));__webpack_require__.d(__webpack_exports__,\\"ext\\",(function(){return ext}));__webpack_require__.d(__webpack_exports__,\\"getFoo\\",(function(){return getFoo}));function fooLibFn(){return\\"foo\\"}const ext=\\"TRUE\\";async function getFoo(){return await __webpack_require__.e(1).then(__webpack_require__.bind(null,3))}}]);"`;
diff --git a/yarn.lock b/yarn.lock
index 208231bfa6ed7..7625510d3b915 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9100,20 +9100,10 @@ camelize@^1.0.0:
   resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b"
   integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=
 
-caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001043, caniuse-lite@^1.0.30001097:
-  version "1.0.30001150"
-  resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001150.tgz"
-  integrity sha512-kiNKvihW0m36UhAFnl7bOAv0i1K1f6wpfVtTF5O5O82XzgtBnb05V0XeV3oZ968vfg2sRNChsHw8ASH2hDfoYQ==
-
-caniuse-lite@^1.0.30001109:
-  version "1.0.30001164"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001164.tgz#5bbfd64ca605d43132f13cc7fdabb17c3036bfdc"
-  integrity sha512-G+A/tkf4bu0dSp9+duNiXc7bGds35DioCyC6vgK2m/rjA4Krpy5WeZgZyfH2f0wj2kI6yAWWucyap6oOwmY1mg==
-
-caniuse-lite@^1.0.30001135:
-  version "1.0.30001144"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001144.tgz#bca0fffde12f97e1127a351fec3bfc1971aa3b3d"
-  integrity sha512-4GQTEWNMnVZVOFG3BK0xvGeaDAtiPAbG2N8yuMXuXzx/c2Vd4XoMPO8+E918zeXn5IF0FRVtGShBfkfQea2wHQ==
+caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001043, caniuse-lite@^1.0.30001097, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001135:
+  version "1.0.30001179"
+  resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001179.tgz"
+  integrity sha512-blMmO0QQujuUWZKyVrD1msR4WNDAqb/UPO1Sw2WWsQ7deoM5bJiicKnWJ1Y0NS/aGINSnKPIWBMw5luX+NDUCA==
 
 capture-exit@^2.0.0:
   version "2.0.0"