diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggfunctionsmapping.aggsinglepercentile.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggfunctionsmapping.aggsinglepercentile.md
new file mode 100644
index 0000000000000..4e432b8d365a3
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggfunctionsmapping.aggsinglepercentile.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggFunctionsMapping](./kibana-plugin-plugins-data-public.aggfunctionsmapping.md) > [aggSinglePercentile](./kibana-plugin-plugins-data-public.aggfunctionsmapping.aggsinglepercentile.md)
+
+## AggFunctionsMapping.aggSinglePercentile property
+
+Signature:
+
+```typescript
+aggSinglePercentile: ReturnType;
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggfunctionsmapping.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggfunctionsmapping.md
index 05388e2b86d7b..852c6d5f1c00b 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggfunctionsmapping.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggfunctionsmapping.md
@@ -45,6 +45,7 @@ export interface AggFunctionsMapping
| [aggRange](./kibana-plugin-plugins-data-public.aggfunctionsmapping.aggrange.md) | ReturnType<typeof aggRange>
| |
| [aggSerialDiff](./kibana-plugin-plugins-data-public.aggfunctionsmapping.aggserialdiff.md) | ReturnType<typeof aggSerialDiff>
| |
| [aggSignificantTerms](./kibana-plugin-plugins-data-public.aggfunctionsmapping.aggsignificantterms.md) | ReturnType<typeof aggSignificantTerms>
| |
+| [aggSinglePercentile](./kibana-plugin-plugins-data-public.aggfunctionsmapping.aggsinglepercentile.md) | ReturnType<typeof aggSinglePercentile>
| |
| [aggStdDeviation](./kibana-plugin-plugins-data-public.aggfunctionsmapping.aggstddeviation.md) | ReturnType<typeof aggStdDeviation>
| |
| [aggSum](./kibana-plugin-plugins-data-public.aggfunctionsmapping.aggsum.md) | ReturnType<typeof aggSum>
| |
| [aggTerms](./kibana-plugin-plugins-data-public.aggfunctionsmapping.aggterms.md) | ReturnType<typeof aggTerms>
| |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.metric_types.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.metric_types.md
index 3b5cecf1a0b82..bdae3ec738ac3 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.metric_types.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.metric_types.md
@@ -32,6 +32,7 @@ export declare enum METRIC_TYPES
| PERCENTILE\_RANKS | "percentile_ranks"
| |
| PERCENTILES | "percentiles"
| |
| SERIAL\_DIFF | "serial_diff"
| |
+| SINGLE\_PERCENTILE | "single_percentile"
| |
| STD\_DEV | "std_dev"
| |
| SUM | "sum"
| |
| SUM\_BUCKET | "sum_bucket"
| |
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggfunctionsmapping.aggsinglepercentile.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggfunctionsmapping.aggsinglepercentile.md
new file mode 100644
index 0000000000000..d1418d7245d73
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggfunctionsmapping.aggsinglepercentile.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [AggFunctionsMapping](./kibana-plugin-plugins-data-server.aggfunctionsmapping.md) > [aggSinglePercentile](./kibana-plugin-plugins-data-server.aggfunctionsmapping.aggsinglepercentile.md)
+
+## AggFunctionsMapping.aggSinglePercentile property
+
+Signature:
+
+```typescript
+aggSinglePercentile: ReturnType;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggfunctionsmapping.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggfunctionsmapping.md
index 86bf797572b09..6b5f854c155f3 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggfunctionsmapping.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggfunctionsmapping.md
@@ -45,6 +45,7 @@ export interface AggFunctionsMapping
| [aggRange](./kibana-plugin-plugins-data-server.aggfunctionsmapping.aggrange.md) | ReturnType<typeof aggRange>
| |
| [aggSerialDiff](./kibana-plugin-plugins-data-server.aggfunctionsmapping.aggserialdiff.md) | ReturnType<typeof aggSerialDiff>
| |
| [aggSignificantTerms](./kibana-plugin-plugins-data-server.aggfunctionsmapping.aggsignificantterms.md) | ReturnType<typeof aggSignificantTerms>
| |
+| [aggSinglePercentile](./kibana-plugin-plugins-data-server.aggfunctionsmapping.aggsinglepercentile.md) | ReturnType<typeof aggSinglePercentile>
| |
| [aggStdDeviation](./kibana-plugin-plugins-data-server.aggfunctionsmapping.aggstddeviation.md) | ReturnType<typeof aggStdDeviation>
| |
| [aggSum](./kibana-plugin-plugins-data-server.aggfunctionsmapping.aggsum.md) | ReturnType<typeof aggSum>
| |
| [aggTerms](./kibana-plugin-plugins-data-server.aggfunctionsmapping.aggterms.md) | ReturnType<typeof aggTerms>
| |
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.metric_types.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.metric_types.md
index 250173d11a056..37f53af8971b3 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.metric_types.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.metric_types.md
@@ -32,6 +32,7 @@ export declare enum METRIC_TYPES
| PERCENTILE\_RANKS | "percentile_ranks"
| |
| PERCENTILES | "percentiles"
| |
| SERIAL\_DIFF | "serial_diff"
| |
+| SINGLE\_PERCENTILE | "single_percentile"
| |
| STD\_DEV | "std_dev"
| |
| SUM | "sum"
| |
| SUM\_BUCKET | "sum_bucket"
| |
diff --git a/package.json b/package.json
index e379123269847..05a8e450793d6 100644
--- a/package.json
+++ b/package.json
@@ -97,7 +97,7 @@
"dependencies": {
"@elastic/apm-rum": "^5.6.1",
"@elastic/apm-rum-react": "^1.2.5",
- "@elastic/charts": "26.0.0",
+ "@elastic/charts": "26.1.0",
"@elastic/datemath": "link:packages/elastic-datemath",
"@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.4",
"@elastic/ems-client": "7.12.0",
diff --git a/src/plugins/dashboard/public/application/actions/add_to_library_action.test.tsx b/src/plugins/dashboard/public/application/actions/add_to_library_action.test.tsx
index 1156bf8c6e0d1..07c38fd406eea 100644
--- a/src/plugins/dashboard/public/application/actions/add_to_library_action.test.tsx
+++ b/src/plugins/dashboard/public/application/actions/add_to_library_action.test.tsx
@@ -41,15 +41,8 @@ const start = doStart();
let container: DashboardContainer;
let embeddable: ContactCardEmbeddable & ReferenceOrValueEmbeddable;
let coreStart: CoreStart;
-let capabilities: CoreStart['application']['capabilities'];
-
beforeEach(async () => {
coreStart = coreMock.createStart();
- capabilities = {
- ...coreStart.application.capabilities,
- visualize: { save: true },
- maps: { save: true },
- };
const containerOptions = {
ExitFullScreenButton: () => null,
@@ -90,10 +83,7 @@ beforeEach(async () => {
});
test('Add to library is incompatible with Error Embeddables', async () => {
- const action = new AddToLibraryAction({
- toasts: coreStart.notifications.toasts,
- capabilities,
- });
+ const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts });
const errorEmbeddable = new ErrorEmbeddable(
'Wow what an awful error',
{ id: ' 404' },
@@ -102,37 +92,20 @@ test('Add to library is incompatible with Error Embeddables', async () => {
expect(await action.isCompatible({ embeddable: errorEmbeddable })).toBe(false);
});
-test('Add to library is incompatible on visualize embeddable without visualize save permissions', async () => {
- const action = new AddToLibraryAction({
- toasts: coreStart.notifications.toasts,
- capabilities: { ...capabilities, visualize: { save: false } },
- });
- expect(await action.isCompatible({ embeddable })).toBe(false);
-});
-
test('Add to library is compatible when embeddable on dashboard has value type input', async () => {
- const action = new AddToLibraryAction({
- toasts: coreStart.notifications.toasts,
- capabilities,
- });
+ const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts });
embeddable.updateInput(await embeddable.getInputAsValueType());
expect(await action.isCompatible({ embeddable })).toBe(true);
});
test('Add to library is not compatible when embeddable input is by reference', async () => {
- const action = new AddToLibraryAction({
- toasts: coreStart.notifications.toasts,
- capabilities,
- });
+ const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts });
embeddable.updateInput(await embeddable.getInputAsRefType());
expect(await action.isCompatible({ embeddable })).toBe(false);
});
test('Add to library is not compatible when view mode is set to view', async () => {
- const action = new AddToLibraryAction({
- toasts: coreStart.notifications.toasts,
- capabilities,
- });
+ const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts });
embeddable.updateInput(await embeddable.getInputAsRefType());
embeddable.updateInput({ viewMode: ViewMode.VIEW });
expect(await action.isCompatible({ embeddable })).toBe(false);
@@ -153,10 +126,7 @@ test('Add to library is not compatible when embeddable is not in a dashboard con
mockedByReferenceInput: { savedObjectId: 'test', id: orphanContactCard.id },
mockedByValueInput: { firstName: 'Kibanana', id: orphanContactCard.id },
});
- const action = new AddToLibraryAction({
- toasts: coreStart.notifications.toasts,
- capabilities,
- });
+ const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts });
expect(await action.isCompatible({ embeddable: orphanContactCard })).toBe(false);
});
@@ -165,10 +135,7 @@ test('Add to library replaces embeddableId and retains panel count', async () =>
const originalPanelCount = Object.keys(dashboard.getInput().panels).length;
const originalPanelKeySet = new Set(Object.keys(dashboard.getInput().panels));
- const action = new AddToLibraryAction({
- toasts: coreStart.notifications.toasts,
- capabilities,
- });
+ const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts });
await action.execute({ embeddable });
expect(Object.keys(container.getInput().panels).length).toEqual(originalPanelCount);
@@ -194,10 +161,7 @@ test('Add to library returns reference type input', async () => {
});
const dashboard = embeddable.getRoot() as IContainer;
const originalPanelKeySet = new Set(Object.keys(dashboard.getInput().panels));
- const action = new AddToLibraryAction({
- toasts: coreStart.notifications.toasts,
- capabilities,
- });
+ const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts });
await action.execute({ embeddable });
const newPanelId = Object.keys(container.getInput().panels).find(
(key) => !originalPanelKeySet.has(key)
diff --git a/src/plugins/dashboard/public/application/actions/add_to_library_action.tsx b/src/plugins/dashboard/public/application/actions/add_to_library_action.tsx
index fa102a9415b3f..ef730e16bc5cf 100644
--- a/src/plugins/dashboard/public/application/actions/add_to_library_action.tsx
+++ b/src/plugins/dashboard/public/application/actions/add_to_library_action.tsx
@@ -18,7 +18,7 @@ import {
isReferenceOrValueEmbeddable,
isErrorEmbeddable,
} from '../../services/embeddable';
-import { ApplicationStart, NotificationsStart } from '../../services/core';
+import { NotificationsStart } from '../../services/core';
import { dashboardAddToLibraryAction } from '../../dashboard_strings';
import { DashboardPanelState, DASHBOARD_CONTAINER_TYPE, DashboardContainer } from '..';
@@ -33,12 +33,7 @@ export class AddToLibraryAction implements Action {
public readonly id = ACTION_ADD_TO_LIBRARY;
public order = 15;
- constructor(
- private deps: {
- toasts: NotificationsStart['toasts'];
- capabilities: ApplicationStart['capabilities'];
- }
- ) {}
+ constructor(private deps: { toasts: NotificationsStart['toasts'] }) {}
public getDisplayName({ embeddable }: AddToLibraryActionContext) {
if (!embeddable.getRoot() || !embeddable.getRoot().isContainer) {
@@ -55,15 +50,8 @@ export class AddToLibraryAction implements Action {
}
public async isCompatible({ embeddable }: AddToLibraryActionContext) {
- // TODO: Fix this, potentially by adding a 'canSave' function to embeddable interface
- const canSave =
- embeddable.type === 'map'
- ? this.deps.capabilities.maps?.save
- : this.deps.capabilities.visualize.save;
-
return Boolean(
- canSave &&
- !isErrorEmbeddable(embeddable) &&
+ !isErrorEmbeddable(embeddable) &&
embeddable.getInput()?.viewMode !== ViewMode.VIEW &&
embeddable.getRoot() &&
embeddable.getRoot().isContainer &&
diff --git a/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx b/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx
index 829344504b16b..c82f17f2b29c4 100644
--- a/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx
+++ b/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx
@@ -61,8 +61,7 @@ export class ClonePanelAction implements Action {
embeddable.getInput()?.viewMode !== ViewMode.VIEW &&
embeddable.getRoot() &&
embeddable.getRoot().isContainer &&
- embeddable.getRoot().type === DASHBOARD_CONTAINER_TYPE &&
- embeddable.getOutput().editable
+ embeddable.getRoot().type === DASHBOARD_CONTAINER_TYPE
);
}
diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx
index 5bf730996ab4f..ae2d2b5f237c9 100644
--- a/src/plugins/dashboard/public/plugin.tsx
+++ b/src/plugins/dashboard/public/plugin.tsx
@@ -342,7 +342,7 @@ export class DashboardPlugin
}
public start(core: CoreStart, plugins: DashboardStartDependencies): DashboardStart {
- const { notifications, overlays, application } = core;
+ const { notifications, overlays } = core;
const { uiActions, data, share, presentationUtil, embeddable } = plugins;
const SavedObjectFinder = getSavedObjectFinder(core.savedObjects, core.uiSettings);
@@ -370,10 +370,7 @@ export class DashboardPlugin
}
if (this.dashboardFeatureFlagConfig?.allowByValueEmbeddables) {
- const addToLibraryAction = new AddToLibraryAction({
- toasts: notifications.toasts,
- capabilities: application.capabilities,
- });
+ const addToLibraryAction = new AddToLibraryAction({ toasts: notifications.toasts });
uiActions.registerAction(addToLibraryAction);
uiActions.attachAction(CONTEXT_MENU_TRIGGER, addToLibraryAction.id);
@@ -389,8 +386,8 @@ export class DashboardPlugin
overlays,
embeddable.getStateTransfer(),
{
- canCreateNew: Boolean(application.capabilities.dashboard.createNew),
- canEditExisting: !Boolean(application.capabilities.dashboard.hideWriteControls),
+ canCreateNew: Boolean(core.application.capabilities.dashboard.createNew),
+ canEditExisting: !Boolean(core.application.capabilities.dashboard.hideWriteControls),
},
presentationUtil.ContextProvider
);
diff --git a/src/plugins/dashboard/public/services/core.ts b/src/plugins/dashboard/public/services/core.ts
index 75461729841e9..7c19b2d75a967 100644
--- a/src/plugins/dashboard/public/services/core.ts
+++ b/src/plugins/dashboard/public/services/core.ts
@@ -12,5 +12,4 @@ export {
PluginInitializerContext,
ScopedHistory,
NotificationsStart,
- ApplicationStart,
} from '../../../../core/public';
diff --git a/src/plugins/data/common/search/aggs/agg_types.ts b/src/plugins/data/common/search/aggs/agg_types.ts
index d02f8e1fc5af4..1db60db507f0f 100644
--- a/src/plugins/data/common/search/aggs/agg_types.ts
+++ b/src/plugins/data/common/search/aggs/agg_types.ts
@@ -29,6 +29,7 @@ export const getAggTypes = () => ({
{ name: METRIC_TYPES.AVG, fn: metrics.getAvgMetricAgg },
{ name: METRIC_TYPES.SUM, fn: metrics.getSumMetricAgg },
{ name: METRIC_TYPES.MEDIAN, fn: metrics.getMedianMetricAgg },
+ { name: METRIC_TYPES.SINGLE_PERCENTILE, fn: metrics.getSinglePercentileMetricAgg },
{ name: METRIC_TYPES.MIN, fn: metrics.getMinMetricAgg },
{ name: METRIC_TYPES.MAX, fn: metrics.getMaxMetricAgg },
{ name: METRIC_TYPES.STD_DEV, fn: metrics.getStdDeviationMetricAgg },
@@ -90,6 +91,7 @@ export const getAggTypesFunctions = () => [
metrics.aggGeoCentroid,
metrics.aggMax,
metrics.aggMedian,
+ metrics.aggSinglePercentile,
metrics.aggMin,
metrics.aggMovingAvg,
metrics.aggPercentileRanks,
diff --git a/src/plugins/data/common/search/aggs/aggs_service.test.ts b/src/plugins/data/common/search/aggs/aggs_service.test.ts
index e14c908023ac6..3f434b0cc1c15 100644
--- a/src/plugins/data/common/search/aggs/aggs_service.test.ts
+++ b/src/plugins/data/common/search/aggs/aggs_service.test.ts
@@ -82,6 +82,7 @@ describe('Aggs service', () => {
"avg",
"sum",
"median",
+ "single_percentile",
"min",
"max",
"std_dev",
@@ -128,6 +129,7 @@ describe('Aggs service', () => {
"avg",
"sum",
"median",
+ "single_percentile",
"min",
"max",
"std_dev",
diff --git a/src/plugins/data/common/search/aggs/metrics/__snapshots__/single_percentile.test.ts.snap b/src/plugins/data/common/search/aggs/metrics/__snapshots__/single_percentile.test.ts.snap
new file mode 100644
index 0000000000000..a8d546973b185
--- /dev/null
+++ b/src/plugins/data/common/search/aggs/metrics/__snapshots__/single_percentile.test.ts.snap
@@ -0,0 +1,17 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`AggTypeMetricSinglePercentileProvider class supports scripted fields 1`] = `
+Object {
+ "single_percentile": Object {
+ "percentiles": Object {
+ "percents": Array [
+ 95,
+ ],
+ "script": Object {
+ "lang": undefined,
+ "source": "return 456",
+ },
+ },
+ },
+}
+`;
diff --git a/src/plugins/data/common/search/aggs/metrics/index.ts b/src/plugins/data/common/search/aggs/metrics/index.ts
index 7038673d5d7c4..d37b74a1a28ae 100644
--- a/src/plugins/data/common/search/aggs/metrics/index.ts
+++ b/src/plugins/data/common/search/aggs/metrics/index.ts
@@ -36,6 +36,8 @@ export * from './max_fn';
export * from './max';
export * from './median_fn';
export * from './median';
+export * from './single_percentile_fn';
+export * from './single_percentile';
export * from './metric_agg_type';
export * from './metric_agg_types';
export * from './min_fn';
diff --git a/src/plugins/data/common/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts b/src/plugins/data/common/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts
index d51038d8a15e8..ac2beaf574256 100644
--- a/src/plugins/data/common/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts
+++ b/src/plugins/data/common/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts
@@ -22,6 +22,7 @@ const metricAggFilter = [
'!geo_bounds',
'!geo_centroid',
'!filtered_metric',
+ '!single_percentile',
];
export const parentPipelineType = i18n.translate(
diff --git a/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts b/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts
index c0d1be4f47f9b..2564fcb7a002b 100644
--- a/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts
+++ b/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts
@@ -28,6 +28,7 @@ const metricAggFilter: string[] = [
'!geo_bounds',
'!geo_centroid',
'!filtered_metric',
+ '!single_percentile',
];
const bucketAggFilter: string[] = [];
diff --git a/src/plugins/data/common/search/aggs/metrics/metric_agg_types.ts b/src/plugins/data/common/search/aggs/metrics/metric_agg_types.ts
index 3b6c9d8a0d55d..a308153b3816b 100644
--- a/src/plugins/data/common/search/aggs/metrics/metric_agg_types.ts
+++ b/src/plugins/data/common/search/aggs/metrics/metric_agg_types.ts
@@ -20,6 +20,7 @@ export enum METRIC_TYPES {
GEO_BOUNDS = 'geo_bounds',
GEO_CENTROID = 'geo_centroid',
MEDIAN = 'median',
+ SINGLE_PERCENTILE = 'single_percentile',
MIN = 'min',
MAX = 'max',
MOVING_FN = 'moving_avg',
diff --git a/src/plugins/data/common/search/aggs/metrics/single_percentile.test.ts b/src/plugins/data/common/search/aggs/metrics/single_percentile.test.ts
new file mode 100644
index 0000000000000..c2ba6ee1a403a
--- /dev/null
+++ b/src/plugins/data/common/search/aggs/metrics/single_percentile.test.ts
@@ -0,0 +1,145 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { AggConfigs, IAggConfigs } from '../agg_configs';
+import { mockAggTypesRegistry } from '../test_helpers';
+import { METRIC_TYPES } from './metric_agg_types';
+
+describe('AggTypeMetricSinglePercentileProvider class', () => {
+ let aggConfigs: IAggConfigs;
+
+ beforeEach(() => {
+ const typesRegistry = mockAggTypesRegistry();
+ const field = {
+ name: 'bytes',
+ };
+ const indexPattern = {
+ id: '1234',
+ title: 'logstash-*',
+ fields: {
+ getByName: () => field,
+ filter: () => [field],
+ },
+ } as any;
+
+ aggConfigs = new AggConfigs(
+ indexPattern,
+ [
+ {
+ id: METRIC_TYPES.SINGLE_PERCENTILE,
+ type: METRIC_TYPES.SINGLE_PERCENTILE,
+ schema: 'metric',
+ params: {
+ field: 'bytes',
+ percentile: 95,
+ },
+ },
+ ],
+ {
+ typesRegistry,
+ }
+ );
+ });
+
+ it('requests the percentiles aggregation in the Elasticsearch query DSL', () => {
+ const dsl: Record = aggConfigs.toDsl();
+
+ expect(dsl.single_percentile.percentiles.field).toEqual('bytes');
+ expect(dsl.single_percentile.percentiles.percents).toEqual([95]);
+ });
+
+ it('points to right value within multi metric for value bucket path', () => {
+ expect(aggConfigs.byId(METRIC_TYPES.SINGLE_PERCENTILE)!.getValueBucketPath()).toEqual(
+ `${METRIC_TYPES.SINGLE_PERCENTILE}.95`
+ );
+ });
+
+ it('converts the response', () => {
+ const agg = aggConfigs.getResponseAggs()[0];
+
+ expect(
+ agg.getValue({
+ [agg.id]: {
+ values: {
+ '95.0': 123,
+ },
+ },
+ })
+ ).toEqual(123);
+ });
+
+ it('produces the expected expression ast', () => {
+ const agg = aggConfigs.getResponseAggs()[0];
+ expect(agg.toExpressionAst()).toMatchInlineSnapshot(`
+ Object {
+ "chain": Array [
+ Object {
+ "arguments": Object {
+ "enabled": Array [
+ true,
+ ],
+ "field": Array [
+ "bytes",
+ ],
+ "id": Array [
+ "single_percentile",
+ ],
+ "percentile": Array [
+ 95,
+ ],
+ "schema": Array [
+ "metric",
+ ],
+ },
+ "function": "aggSinglePercentile",
+ "type": "function",
+ },
+ ],
+ "type": "expression",
+ }
+ `);
+ });
+
+ it('supports scripted fields', () => {
+ const typesRegistry = mockAggTypesRegistry();
+ const field = {
+ name: 'bytes',
+ scripted: true,
+ language: 'painless',
+ script: 'return 456',
+ };
+ const indexPattern = {
+ id: '1234',
+ title: 'logstash-*',
+ fields: {
+ getByName: () => field,
+ filter: () => [field],
+ },
+ } as any;
+
+ aggConfigs = new AggConfigs(
+ indexPattern,
+ [
+ {
+ id: METRIC_TYPES.SINGLE_PERCENTILE,
+ type: METRIC_TYPES.SINGLE_PERCENTILE,
+ schema: 'metric',
+ params: {
+ field: 'bytes',
+ percentile: 95,
+ },
+ },
+ ],
+ {
+ typesRegistry,
+ }
+ );
+
+ expect(aggConfigs.toDsl()).toMatchSnapshot();
+ });
+});
diff --git a/src/plugins/data/common/search/aggs/metrics/single_percentile.ts b/src/plugins/data/common/search/aggs/metrics/single_percentile.ts
new file mode 100644
index 0000000000000..4bdafcae327cd
--- /dev/null
+++ b/src/plugins/data/common/search/aggs/metrics/single_percentile.ts
@@ -0,0 +1,63 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { i18n } from '@kbn/i18n';
+import { aggSinglePercentileFnName } from './single_percentile_fn';
+import { MetricAggType } from './metric_agg_type';
+import { METRIC_TYPES } from './metric_agg_types';
+import { KBN_FIELD_TYPES } from '../../../../common';
+import { BaseAggParams } from '../types';
+
+const singlePercentileTitle = i18n.translate('data.search.aggs.metrics.singlePercentileTitle', {
+ defaultMessage: 'Percentile',
+});
+
+export interface AggParamsSinglePercentile extends BaseAggParams {
+ field: string;
+ percentile: number;
+}
+
+export const getSinglePercentileMetricAgg = () => {
+ return new MetricAggType({
+ name: METRIC_TYPES.SINGLE_PERCENTILE,
+ expressionName: aggSinglePercentileFnName,
+ dslName: 'percentiles',
+ title: singlePercentileTitle,
+ valueType: 'number',
+ makeLabel(aggConfig) {
+ return i18n.translate('data.search.aggs.metrics.singlePercentileLabel', {
+ defaultMessage: 'Percentile {field}',
+ values: { field: aggConfig.getFieldDisplayName() },
+ });
+ },
+ getValueBucketPath(aggConfig) {
+ return `${aggConfig.id}.${aggConfig.params.percentile}`;
+ },
+ params: [
+ {
+ name: 'field',
+ type: 'field',
+ filterFieldTypes: [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.DATE, KBN_FIELD_TYPES.HISTOGRAM],
+ },
+ {
+ name: 'percentile',
+ default: 95,
+ write: (agg, output) => {
+ output.params.percents = [agg.params.percentile];
+ },
+ },
+ ],
+ getValue(agg, bucket) {
+ let valueKey = String(agg.params.percentile);
+ if (Number.isInteger(agg.params.percentile)) {
+ valueKey += '.0';
+ }
+ return bucket[agg.id].values[valueKey];
+ },
+ });
+};
diff --git a/src/plugins/data/common/search/aggs/metrics/single_percentile_fn.ts b/src/plugins/data/common/search/aggs/metrics/single_percentile_fn.ts
new file mode 100644
index 0000000000000..e7ef22c6faeee
--- /dev/null
+++ b/src/plugins/data/common/search/aggs/metrics/single_percentile_fn.ts
@@ -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
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { i18n } from '@kbn/i18n';
+import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common';
+import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../';
+
+export const aggSinglePercentileFnName = 'aggSinglePercentile';
+
+type Input = any;
+type AggArgs = AggExpressionFunctionArgs;
+type Output = AggExpressionType;
+type FunctionDefinition = ExpressionFunctionDefinition<
+ typeof aggSinglePercentileFnName,
+ Input,
+ AggArgs,
+ Output
+>;
+
+export const aggSinglePercentile = (): FunctionDefinition => ({
+ name: aggSinglePercentileFnName,
+ help: i18n.translate('data.search.aggs.function.metrics.singlePercentile.help', {
+ defaultMessage: 'Generates a serialized agg config for a single percentile agg',
+ }),
+ type: 'agg_type',
+ args: {
+ id: {
+ types: ['string'],
+ help: i18n.translate('data.search.aggs.metrics.singlePercentile.id.help', {
+ defaultMessage: 'ID for this aggregation',
+ }),
+ },
+ enabled: {
+ types: ['boolean'],
+ default: true,
+ help: i18n.translate('data.search.aggs.metrics.singlePercentile.enabled.help', {
+ defaultMessage: 'Specifies whether this aggregation should be enabled',
+ }),
+ },
+ schema: {
+ types: ['string'],
+ help: i18n.translate('data.search.aggs.metrics.singlePercentile.schema.help', {
+ defaultMessage: 'Schema to use for this aggregation',
+ }),
+ },
+ field: {
+ types: ['string'],
+ required: true,
+ help: i18n.translate('data.search.aggs.metrics.singlePercentile.field.help', {
+ defaultMessage: 'Field to use for this aggregation',
+ }),
+ },
+ percentile: {
+ types: ['number'],
+ required: true,
+ help: i18n.translate('data.search.aggs.metrics.singlePercentile.percentile.help', {
+ defaultMessage: 'Percentile to fetch',
+ }),
+ },
+ json: {
+ types: ['string'],
+ help: i18n.translate('data.search.aggs.metrics.singlePercentile.json.help', {
+ defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch',
+ }),
+ },
+ customLabel: {
+ types: ['string'],
+ help: i18n.translate('data.search.aggs.metrics.singlePercentile.customLabel.help', {
+ defaultMessage: 'Represents a custom label for this aggregation',
+ }),
+ },
+ },
+ fn: (input, args) => {
+ const { id, enabled, schema, ...rest } = args;
+
+ return {
+ type: 'agg_type',
+ value: {
+ id,
+ enabled,
+ schema,
+ type: METRIC_TYPES.SINGLE_PERCENTILE,
+ params: {
+ ...rest,
+ },
+ },
+ };
+ },
+});
diff --git a/src/plugins/data/common/search/aggs/types.ts b/src/plugins/data/common/search/aggs/types.ts
index 2b5bb6596cef9..675be2323b93e 100644
--- a/src/plugins/data/common/search/aggs/types.ts
+++ b/src/plugins/data/common/search/aggs/types.ts
@@ -56,6 +56,7 @@ import {
AggParamsIpRange,
AggParamsMax,
AggParamsMedian,
+ AggParamsSinglePercentile,
AggParamsMin,
AggParamsMovingAvg,
AggParamsPercentileRanks,
@@ -85,6 +86,7 @@ import {
METRIC_TYPES,
AggConfig,
aggFilteredMetric,
+ aggSinglePercentile,
} from './';
export { IAggConfig, AggConfigSerialized } from './agg_config';
@@ -169,6 +171,7 @@ export interface AggParamsMapping {
[METRIC_TYPES.GEO_CENTROID]: AggParamsGeoCentroid;
[METRIC_TYPES.MAX]: AggParamsMax;
[METRIC_TYPES.MEDIAN]: AggParamsMedian;
+ [METRIC_TYPES.SINGLE_PERCENTILE]: AggParamsSinglePercentile;
[METRIC_TYPES.MIN]: AggParamsMin;
[METRIC_TYPES.STD_DEV]: AggParamsStdDeviation;
[METRIC_TYPES.SUM]: AggParamsSum;
@@ -215,6 +218,7 @@ export interface AggFunctionsMapping {
aggGeoCentroid: ReturnType;
aggMax: ReturnType;
aggMedian: ReturnType;
+ aggSinglePercentile: ReturnType;
aggMin: ReturnType;
aggMovingAvg: ReturnType;
aggPercentileRanks: ReturnType;
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index be57435aa29d4..7f243cefd08b6 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -399,6 +399,10 @@ export interface AggFunctionsMapping {
//
// (undocumented)
aggSignificantTerms: ReturnType;
+ // Warning: (ae-forgotten-export) The symbol "aggSinglePercentile" needs to be exported by the entry point index.d.ts
+ //
+ // (undocumented)
+ aggSinglePercentile: ReturnType;
// Warning: (ae-forgotten-export) The symbol "aggStdDeviation" needs to be exported by the entry point index.d.ts
//
// (undocumented)
@@ -717,7 +721,7 @@ export const ES_SEARCH_STRATEGY = "es";
// Warning: (ae-missing-release-tag) "EsaggsExpressionFunctionDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export type EsaggsExpressionFunctionDefinition = ExpressionFunctionDefinition<'esaggs', Input_35, Arguments_21, Output_35>;
+export type EsaggsExpressionFunctionDefinition = ExpressionFunctionDefinition<'esaggs', Input_36, Arguments_21, Output_36>;
// Warning: (ae-forgotten-export) The symbol "name" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "Input" needs to be exported by the entry point index.d.ts
@@ -726,7 +730,7 @@ export type EsaggsExpressionFunctionDefinition = ExpressionFunctionDefinition<'e
// Warning: (ae-missing-release-tag) "EsdslExpressionFunctionDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export type EsdslExpressionFunctionDefinition = ExpressionFunctionDefinition;
+export type EsdslExpressionFunctionDefinition = ExpressionFunctionDefinition;
// Warning: (ae-missing-release-tag) "esFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -1860,6 +1864,8 @@ export enum METRIC_TYPES {
// (undocumented)
SERIAL_DIFF = "serial_diff",
// (undocumented)
+ SINGLE_PERCENTILE = "single_percentile",
+ // (undocumented)
STD_DEV = "std_dev",
// (undocumented)
SUM = "sum",
@@ -2650,7 +2656,7 @@ export const UI_SETTINGS: {
// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:65:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts
// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:138:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts
// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:169:7 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts
-// src/plugins/data/common/search/aggs/types.ts:127:51 - (ae-forgotten-export) The symbol "AggTypesRegistryStart" needs to be exported by the entry point index.d.ts
+// src/plugins/data/common/search/aggs/types.ts:129:51 - (ae-forgotten-export) The symbol "AggTypesRegistryStart" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/field_formats/field_formats_service.ts:56:3 - (ae-forgotten-export) The symbol "FormatFactory" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:56:23 - (ae-forgotten-export) The symbol "FILTERS" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:56:23 - (ae-forgotten-export) The symbol "getDisplayValueFromFilter" needs to be exported by the entry point index.d.ts
diff --git a/src/plugins/data/public/search/aggs/aggs_service.test.ts b/src/plugins/data/public/search/aggs/aggs_service.test.ts
index 920a6a81cb5fd..cd2ee69d33996 100644
--- a/src/plugins/data/public/search/aggs/aggs_service.test.ts
+++ b/src/plugins/data/public/search/aggs/aggs_service.test.ts
@@ -54,7 +54,7 @@ describe('AggsService - public', () => {
service.setup(setupDeps);
const start = service.start(startDeps);
expect(start.types.getAll().buckets.length).toBe(11);
- expect(start.types.getAll().metrics.length).toBe(22);
+ expect(start.types.getAll().metrics.length).toBe(23);
});
test('registers custom agg types', () => {
@@ -71,7 +71,7 @@ describe('AggsService - public', () => {
const start = service.start(startDeps);
expect(start.types.getAll().buckets.length).toBe(12);
expect(start.types.getAll().buckets.some(({ name }) => name === 'foo')).toBe(true);
- expect(start.types.getAll().metrics.length).toBe(23);
+ expect(start.types.getAll().metrics.length).toBe(24);
expect(start.types.getAll().metrics.some(({ name }) => name === 'bar')).toBe(true);
});
});
diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md
index 6d3eac01dc4dc..1f043cc9aab4e 100644
--- a/src/plugins/data/server/server.api.md
+++ b/src/plugins/data/server/server.api.md
@@ -202,6 +202,10 @@ export interface AggFunctionsMapping {
//
// (undocumented)
aggSignificantTerms: ReturnType;
+ // Warning: (ae-forgotten-export) The symbol "aggSinglePercentile" needs to be exported by the entry point index.d.ts
+ //
+ // (undocumented)
+ aggSinglePercentile: ReturnType;
// Warning: (ae-forgotten-export) The symbol "aggStdDeviation" needs to be exported by the entry point index.d.ts
//
// (undocumented)
@@ -403,7 +407,7 @@ export const ES_SEARCH_STRATEGY = "es";
// Warning: (ae-missing-release-tag) "EsaggsExpressionFunctionDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export type EsaggsExpressionFunctionDefinition = ExpressionFunctionDefinition<'esaggs', Input_35, Arguments_21, Output_35>;
+export type EsaggsExpressionFunctionDefinition = ExpressionFunctionDefinition<'esaggs', Input_36, Arguments_21, Output_36>;
// Warning: (ae-missing-release-tag) "esFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -1163,6 +1167,8 @@ export enum METRIC_TYPES {
// (undocumented)
SERIAL_DIFF = "serial_diff",
// (undocumented)
+ SINGLE_PERCENTILE = "single_percentile",
+ // (undocumented)
STD_DEV = "std_dev",
// (undocumented)
SUM = "sum",
diff --git a/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx b/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx
index 4491be04b1a42..57d05262319f2 100644
--- a/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx
+++ b/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx
@@ -28,7 +28,6 @@ interface SaveModalDocumentInfo {
export interface SaveModalDashboardProps {
documentInfo: SaveModalDocumentInfo;
- canSaveByReference: boolean;
objectType: string;
onClose: () => void;
onSave: (props: OnSaveProps & { dashboardId: string | null; addToLibrary: boolean }) => void;
@@ -36,7 +35,7 @@ export interface SaveModalDashboardProps {
}
export function SavedObjectSaveModalDashboard(props: SaveModalDashboardProps) {
- const { documentInfo, tagOptions, objectType, onClose, canSaveByReference } = props;
+ const { documentInfo, tagOptions, objectType, onClose } = props;
const { id: documentId } = documentInfo;
const initialCopyOnSave = !Boolean(documentId);
@@ -50,7 +49,7 @@ export function SavedObjectSaveModalDashboard(props: SaveModalDashboardProps) {
documentId || disableDashboardOptions ? null : 'existing'
);
const [isAddToLibrarySelected, setAddToLibrary] = useState(
- canSaveByReference && (!initialCopyOnSave || disableDashboardOptions)
+ !initialCopyOnSave || disableDashboardOptions
);
const [selectedDashboard, setSelectedDashboard] = useState<{ id: string; name: string } | null>(
null
@@ -66,16 +65,13 @@ export function SavedObjectSaveModalDashboard(props: SaveModalDashboardProps) {
onChange={(option) => {
setDashboardOption(option);
}}
- canSaveByReference={canSaveByReference}
{...{ copyOnSave, documentId, dashboardOption, setAddToLibrary, isAddToLibrarySelected }}
/>
)
: null;
const onCopyOnSaveChange = (newCopyOnSave: boolean) => {
- if (canSaveByReference) {
- setAddToLibrary(true);
- }
+ setAddToLibrary(true);
setDashboardOption(null);
setCopyOnSave(newCopyOnSave);
};
diff --git a/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard_selector.stories.tsx b/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard_selector.stories.tsx
index 341f194b71ba4..dd6fd975f8e07 100644
--- a/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard_selector.stories.tsx
+++ b/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard_selector.stories.tsx
@@ -33,21 +33,15 @@ export default {
control: 'boolean',
defaultValue: true,
},
- canSaveVisualizations: {
- control: 'boolean',
- defaultValue: true,
- },
},
};
export function Example({
copyOnSave,
hasDocumentId,
- canSaveVisualizations,
}: {
copyOnSave: boolean;
hasDocumentId: boolean;
- canSaveVisualizations: boolean;
} & StorybookParams) {
const [dashboardOption, setDashboardOption] = useState<'new' | 'existing' | null>('existing');
const [isAddToLibrarySelected, setAddToLibrary] = useState(false);
@@ -58,7 +52,6 @@ export function Example({
onChange={setDashboardOption}
dashboardOption={dashboardOption}
copyOnSave={copyOnSave}
- canSaveByReference={canSaveVisualizations}
documentId={hasDocumentId ? 'abc' : undefined}
isAddToLibrarySelected={isAddToLibrarySelected}
setAddToLibrary={setAddToLibrary}
diff --git a/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard_selector.tsx b/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard_selector.tsx
index 78a1569c02ead..1ae54040571a2 100644
--- a/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard_selector.tsx
+++ b/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard_selector.tsx
@@ -30,7 +30,6 @@ export interface SaveModalDashboardSelectorProps {
copyOnSave: boolean;
documentId?: string;
onSelectDashboard: DashboardPickerProps['onChange'];
- canSaveByReference: boolean;
setAddToLibrary: (selected: boolean) => void;
isAddToLibrarySelected: boolean;
dashboardOption: 'new' | 'existing' | null;
@@ -41,7 +40,6 @@ export function SaveModalDashboardSelector(props: SaveModalDashboardSelectorProp
const {
documentId,
onSelectDashboard,
- canSaveByReference,
setAddToLibrary,
isAddToLibrarySelected,
dashboardOption,
@@ -116,7 +114,7 @@ export function SaveModalDashboardSelector(props: SaveModalDashboardSelectorProp
setAddToLibrary(true);
onChange(null);
}}
- disabled={isDisabled || !canSaveByReference}
+ disabled={isDisabled}
/>
@@ -129,7 +127,7 @@ export function SaveModalDashboardSelector(props: SaveModalDashboardSelectorProp
defaultMessage: 'Add to library',
})}
checked={isAddToLibrarySelected}
- disabled={dashboardOption === null || isDisabled || !canSaveByReference}
+ disabled={dashboardOption === null || isDisabled}
onChange={(event) => setAddToLibrary(event.target.checked)}
/>
diff --git a/src/plugins/presentation_util/public/services/index.ts b/src/plugins/presentation_util/public/services/index.ts
index 39dae92aa2ba9..74607b9e35e47 100644
--- a/src/plugins/presentation_util/public/services/index.ts
+++ b/src/plugins/presentation_util/public/services/index.ts
@@ -23,7 +23,6 @@ export interface PresentationDashboardsService {
export interface PresentationCapabilitiesService {
canAccessDashboards: () => boolean;
canCreateNewDashboards: () => boolean;
- canSaveVisualizations: () => boolean;
}
export interface PresentationUtilServices {
diff --git a/src/plugins/presentation_util/public/services/kibana/capabilities.ts b/src/plugins/presentation_util/public/services/kibana/capabilities.ts
index 6949fba00c65a..546281d083f2f 100644
--- a/src/plugins/presentation_util/public/services/kibana/capabilities.ts
+++ b/src/plugins/presentation_util/public/services/kibana/capabilities.ts
@@ -16,11 +16,10 @@ export type CapabilitiesServiceFactory = KibanaPluginServiceFactory<
>;
export const capabilitiesServiceFactory: CapabilitiesServiceFactory = ({ coreStart }) => {
- const { dashboard, visualize } = coreStart.application.capabilities;
+ const { dashboard } = coreStart.application.capabilities;
return {
canAccessDashboards: () => Boolean(dashboard.show),
canCreateNewDashboards: () => Boolean(dashboard.createNew),
- canSaveVisualizations: () => Boolean(visualize.save),
};
};
diff --git a/src/plugins/presentation_util/public/services/storybook/capabilities.ts b/src/plugins/presentation_util/public/services/storybook/capabilities.ts
index 16fbe3baf488f..fcd38b29f154c 100644
--- a/src/plugins/presentation_util/public/services/storybook/capabilities.ts
+++ b/src/plugins/presentation_util/public/services/storybook/capabilities.ts
@@ -19,13 +19,11 @@ export const capabilitiesServiceFactory: CapabilitiesServiceFactory = ({
canAccessDashboards,
canCreateNewDashboards,
canEditDashboards,
- canSaveVisualizations,
}) => {
const check = (value: boolean = true) => value;
return {
canAccessDashboards: () => check(canAccessDashboards),
canCreateNewDashboards: () => check(canCreateNewDashboards),
canEditDashboards: () => check(canEditDashboards),
- canSaveVisualizations: () => check(canSaveVisualizations),
};
};
diff --git a/src/plugins/presentation_util/public/services/storybook/index.ts b/src/plugins/presentation_util/public/services/storybook/index.ts
index dd7de54264062..37b2171635e96 100644
--- a/src/plugins/presentation_util/public/services/storybook/index.ts
+++ b/src/plugins/presentation_util/public/services/storybook/index.ts
@@ -18,7 +18,6 @@ export interface StorybookParams {
canAccessDashboards?: boolean;
canCreateNewDashboards?: boolean;
canEditDashboards?: boolean;
- canSaveVisualizations?: boolean;
}
export const providers: PluginServiceProviders = {
diff --git a/src/plugins/presentation_util/public/services/stub/capabilities.ts b/src/plugins/presentation_util/public/services/stub/capabilities.ts
index 4154fa65a0cd7..979ccc8faadd5 100644
--- a/src/plugins/presentation_util/public/services/stub/capabilities.ts
+++ b/src/plugins/presentation_util/public/services/stub/capabilities.ts
@@ -15,5 +15,4 @@ export const capabilitiesServiceFactory: CapabilitiesServiceFactory = () => ({
canAccessDashboards: () => true,
canCreateNewDashboards: () => true,
canEditDashboards: () => true,
- canSaveVisualizations: () => true,
});
diff --git a/src/plugins/vis_type_metric/public/metric_vis_type.ts b/src/plugins/vis_type_metric/public/metric_vis_type.ts
index 9e2e248c6ccd5..382ef925c5282 100644
--- a/src/plugins/vis_type_metric/public/metric_vis_type.ts
+++ b/src/plugins/vis_type_metric/public/metric_vis_type.ts
@@ -64,6 +64,7 @@ export const createMetricVisTypeDefinition = (): VisTypeDefinition =>
'!cumulative_sum',
'!geo_bounds',
'!filtered_metric',
+ '!single_percentile',
],
aggSettings: {
top_hits: {
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 2f30faa8e9a89..e582f098a5fd5 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
@@ -50,7 +50,7 @@ export const tableVisLegacyTypeDefinition: VisTypeDefinition = {
title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.metricTitle', {
defaultMessage: 'Metric',
}),
- aggFilter: ['!geo_centroid', '!geo_bounds', '!filtered_metric'],
+ aggFilter: ['!geo_centroid', '!geo_bounds', '!filtered_metric', '!single_percentile'],
aggSettings: {
top_hits: {
allowStrings: true,
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 d645af3180b08..a49748fe86c96 100644
--- a/src/plugins/vis_type_table/public/table_vis_type.ts
+++ b/src/plugins/vis_type_table/public/table_vis_type.ts
@@ -46,7 +46,7 @@ export const tableVisTypeDefinition: VisTypeDefinition = {
title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.metricTitle', {
defaultMessage: 'Metric',
}),
- aggFilter: ['!geo_centroid', '!geo_bounds', '!filtered_metric'],
+ aggFilter: ['!geo_centroid', '!geo_bounds', '!filtered_metric', '!single_percentile'],
aggSettings: {
top_hits: {
allowStrings: true,
diff --git a/src/plugins/vis_type_tagcloud/public/tag_cloud_type.ts b/src/plugins/vis_type_tagcloud/public/tag_cloud_type.ts
index 4052ecbe21997..960122c178caa 100644
--- a/src/plugins/vis_type_tagcloud/public/tag_cloud_type.ts
+++ b/src/plugins/vis_type_tagcloud/public/tag_cloud_type.ts
@@ -52,6 +52,7 @@ export const tagCloudVisTypeDefinition = {
'!geo_bounds',
'!geo_centroid',
'!filtered_metric',
+ '!single_percentile',
],
defaults: [{ schema: 'metric', type: 'count' }],
},
diff --git a/src/plugins/vis_type_vislib/public/gauge.ts b/src/plugins/vis_type_vislib/public/gauge.ts
index 7e3ff8226fbb6..fa463bea6f27f 100644
--- a/src/plugins/vis_type_vislib/public/gauge.ts
+++ b/src/plugins/vis_type_vislib/public/gauge.ts
@@ -120,6 +120,7 @@ export const gaugeVisTypeDefinition: VisTypeDefinition = {
'!cumulative_sum',
'!geo_bounds',
'!filtered_metric',
+ '!single_percentile',
],
defaults: [{ schema: 'metric', type: 'count' }],
},
diff --git a/src/plugins/vis_type_vislib/public/goal.ts b/src/plugins/vis_type_vislib/public/goal.ts
index 468651bb4cf4c..e594122871fe7 100644
--- a/src/plugins/vis_type_vislib/public/goal.ts
+++ b/src/plugins/vis_type_vislib/public/goal.ts
@@ -84,6 +84,7 @@ export const goalVisTypeDefinition: VisTypeDefinition = {
'!cumulative_sum',
'!geo_bounds',
'!filtered_metric',
+ '!single_percentile',
],
defaults: [{ schema: 'metric', type: 'count' }],
},
diff --git a/src/plugins/vis_type_vislib/public/heatmap.ts b/src/plugins/vis_type_vislib/public/heatmap.ts
index 8d538399f68b2..f3f320b3658a0 100644
--- a/src/plugins/vis_type_vislib/public/heatmap.ts
+++ b/src/plugins/vis_type_vislib/public/heatmap.ts
@@ -95,6 +95,7 @@ export const heatmapVisTypeDefinition: VisTypeDefinition = {
'std_dev',
'top_hits',
'!filtered_metric',
+ '!single_percentile',
],
defaults: [{ schema: 'metric', type: 'count' }],
},
diff --git a/src/plugins/vis_type_xy/public/vis_types/area.ts b/src/plugins/vis_type_xy/public/vis_types/area.ts
index dfe9bc2f42b84..f22f8df1752d6 100644
--- a/src/plugins/vis_type_xy/public/vis_types/area.ts
+++ b/src/plugins/vis_type_xy/public/vis_types/area.ts
@@ -133,7 +133,7 @@ export const getAreaVisTypeDefinition = (
title: i18n.translate('visTypeXy.area.metricsTitle', {
defaultMessage: 'Y-axis',
}),
- aggFilter: ['!geo_centroid', '!geo_bounds', '!filtered_metric'],
+ aggFilter: ['!geo_centroid', '!geo_bounds', '!filtered_metric', '!single_percentile'],
min: 1,
defaults: [{ schema: 'metric', type: 'count' }],
},
diff --git a/src/plugins/vis_type_xy/public/vis_types/histogram.ts b/src/plugins/vis_type_xy/public/vis_types/histogram.ts
index ba20502a3b9af..732833ffecc80 100644
--- a/src/plugins/vis_type_xy/public/vis_types/histogram.ts
+++ b/src/plugins/vis_type_xy/public/vis_types/histogram.ts
@@ -137,7 +137,7 @@ export const getHistogramVisTypeDefinition = (
defaultMessage: 'Y-axis',
}),
min: 1,
- aggFilter: ['!geo_centroid', '!geo_bounds', '!filtered_metric'],
+ aggFilter: ['!geo_centroid', '!geo_bounds', '!filtered_metric', '!single_percentile'],
defaults: [{ schema: 'metric', type: 'count' }],
},
{
diff --git a/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts b/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts
index 62da0448e56bd..791d93bb646b2 100644
--- a/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts
+++ b/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts
@@ -136,7 +136,7 @@ export const getHorizontalBarVisTypeDefinition = (
defaultMessage: 'Y-axis',
}),
min: 1,
- aggFilter: ['!geo_centroid', '!geo_bounds', '!filtered_metric'],
+ aggFilter: ['!geo_centroid', '!geo_bounds', '!filtered_metric', '!single_percentile'],
defaults: [{ schema: 'metric', type: 'count' }],
},
{
diff --git a/src/plugins/vis_type_xy/public/vis_types/line.ts b/src/plugins/vis_type_xy/public/vis_types/line.ts
index 5a9eb5198df35..6316fe4458229 100644
--- a/src/plugins/vis_type_xy/public/vis_types/line.ts
+++ b/src/plugins/vis_type_xy/public/vis_types/line.ts
@@ -132,7 +132,7 @@ export const getLineVisTypeDefinition = (
name: 'metric',
title: i18n.translate('visTypeXy.line.metricTitle', { defaultMessage: 'Y-axis' }),
min: 1,
- aggFilter: ['!geo_centroid', '!geo_bounds', '!filtered_metric'],
+ aggFilter: ['!geo_centroid', '!geo_bounds', '!filtered_metric', '!single_percentile'],
defaults: [{ schema: 'metric', type: 'count' }],
},
{
diff --git a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts
index e2e2a4c089270..989a9bf5d2cb7 100644
--- a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts
+++ b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts
@@ -67,10 +67,7 @@ export const createVisEmbeddableFromObject = (deps: VisualizeEmbeddableFactoryDe
indexPatterns = [vis.data.indexPattern];
}
- const capabilities = {
- visualizeSave: Boolean(getCapabilities().visualize.save),
- dashboardSave: Boolean(getCapabilities().dashboard?.showWriteControls),
- };
+ const editable = getCapabilities().visualize.save as boolean;
return new VisualizeEmbeddable(
getTimeFilter(),
@@ -79,8 +76,8 @@ export const createVisEmbeddableFromObject = (deps: VisualizeEmbeddableFactoryDe
indexPatterns,
editPath,
editUrl,
+ editable,
deps,
- capabilities,
},
input,
attributeService,
diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
index 429dabeeef042..580ffef548fe1 100644
--- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
+++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
@@ -50,7 +50,7 @@ export interface VisualizeEmbeddableConfiguration {
indexPatterns?: IIndexPattern[];
editPath: string;
editUrl: string;
- capabilities: { visualizeSave: boolean; dashboardSave: boolean };
+ editable: boolean;
deps: VisualizeEmbeddableFactoryDeps;
}
@@ -111,7 +111,7 @@ export class VisualizeEmbeddable
constructor(
timefilter: TimefilterContract,
- { vis, editPath, editUrl, indexPatterns, deps, capabilities }: VisualizeEmbeddableConfiguration,
+ { vis, editPath, editUrl, indexPatterns, editable, deps }: VisualizeEmbeddableConfiguration,
initialInput: VisualizeInput,
attributeService?: AttributeService<
VisualizeSavedObjectAttributes,
@@ -129,6 +129,7 @@ export class VisualizeEmbeddable
editApp: 'visualize',
editUrl,
indexPatterns,
+ editable,
visTypeName: vis.type.name,
},
parent
@@ -142,12 +143,6 @@ export class VisualizeEmbeddable
this.attributeService = attributeService;
this.savedVisualizationsLoader = savedVisualizationsLoader;
- if (this.attributeService) {
- const isByValue = !this.inputIsRefType(initialInput);
- const editable = capabilities.visualizeSave || (isByValue && capabilities.dashboardSave);
- this.updateOutput({ ...this.getOutput(), editable });
- }
-
this.subscriptions.push(
this.getUpdated$().subscribe(() => {
const isDirty = this.handleChanges();
diff --git a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx
index e696bcb5dbe4d..4f5679a14b0b7 100644
--- a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx
+++ b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx
@@ -82,7 +82,6 @@ export const getTopNavConfig = (
setActiveUrl,
toastNotifications,
visualizeCapabilities,
- dashboardCapabilities,
i18n: { Context: I18nContext },
dashboard,
savedObjectsTagging,
@@ -206,9 +205,9 @@ export const getTopNavConfig = (
}
};
- const allowByValue = dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables;
const saveButtonLabel =
- embeddableId || (!savedVis.id && allowByValue && originatingApp)
+ embeddableId ||
+ (!savedVis.id && dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables && originatingApp)
? i18n.translate('visualize.topNavMenu.saveVisualizationToLibraryButtonLabel', {
defaultMessage: 'Save to library',
})
@@ -220,11 +219,9 @@ export const getTopNavConfig = (
defaultMessage: 'Save',
});
- const showSaveAndReturn = originatingApp && (savedVis?.id || allowByValue);
-
- const showSaveButton =
- visualizeCapabilities.save ||
- (allowByValue && !showSaveAndReturn && dashboardCapabilities.showWriteControls);
+ const showSaveAndReturn =
+ originatingApp &&
+ (savedVis?.id || dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables);
const topNavMenu: TopNavMenuData[] = [
{
@@ -303,7 +300,7 @@ export const getTopNavConfig = (
},
]
: []),
- ...(showSaveButton
+ ...(visualizeCapabilities.save
? [
{
id: 'save',
@@ -442,12 +439,7 @@ export const getTopNavConfig = (
/>
) : (
{
defaultMessage: 'Read only',
}),
tooltip: i18n.translate('visualize.badge.readOnly.tooltip', {
- defaultMessage: 'Unable to save visualizations to the library',
+ defaultMessage: 'Unable to save visualizations',
}),
iconType: 'glasses',
});
diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx
index dbc10c751a649..9d5feec9f21e6 100644
--- a/x-pack/plugins/lens/public/app_plugin/app.tsx
+++ b/x-pack/plugins/lens/public/app_plugin/app.tsx
@@ -531,13 +531,7 @@ export function App({
const { TopNavMenu } = navigation.ui;
- const savingToLibraryPermitted = Boolean(
- state.isSaveable && application.capabilities.visualize.save
- );
- const savingToDashboardPermitted = Boolean(
- state.isSaveable && application.capabilities.dashboard?.showWriteControls
- );
-
+ const savingPermitted = Boolean(state.isSaveable && application.capabilities.visualize.save);
const unsavedTitle = i18n.translate('xpack.lens.app.unsavedFilename', {
defaultMessage: 'unsaved',
});
@@ -551,10 +545,8 @@ export function App({
state.isSaveable && state.activeData && Object.keys(state.activeData).length
),
isByValueMode: getIsByValueMode(),
- allowByValue: dashboardFeatureFlag.allowByValueEmbeddables,
showCancel: Boolean(state.isLinkedToOriginatingApp),
- savingToLibraryPermitted,
- savingToDashboardPermitted,
+ savingPermitted,
actions: {
exportToCSV: () => {
if (!state.activeData) {
@@ -585,7 +577,7 @@ export function App({
}
},
saveAndReturn: () => {
- if (savingToDashboardPermitted && lastKnownDoc) {
+ if (savingPermitted && lastKnownDoc) {
// disabling the validation on app leave because the document has been saved.
onAppLeave((actions) => {
return actions.default();
@@ -605,7 +597,7 @@ export function App({
}
},
showSaveModal: () => {
- if (savingToDashboardPermitted || savingToLibraryPermitted) {
+ if (savingPermitted) {
setState((s) => ({ ...s, isSaveModalVisible: true }));
}
},
@@ -705,7 +697,6 @@ export function App({
{
const {
originatingApp,
- savingToLibraryPermitted,
savedObjectsTagging,
tagsIds,
lastKnownDoc,
@@ -87,15 +85,13 @@ export const SaveModal = (props: Props) => {
{
const saveToLibrary = Boolean(saveProps.addToLibrary);
onSave(saveProps, { saveToLibrary });
}}
onClose={onClose}
documentInfo={{
- // if the user cannot save to the library - treat this as a new document.
- id: savingToLibraryPermitted ? lastKnownDoc.savedObjectId : undefined,
+ id: lastKnownDoc.savedObjectId,
title: lastKnownDoc.title || '',
description: lastKnownDoc.description || '',
}}
diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx
index a44870b427ab1..157975b630e1e 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx
@@ -112,10 +112,7 @@ describe('embeddable', () => {
expressionRenderer,
basePath,
indexPatternService: {} as IndexPatternsContract,
- capabilities: {
- canSaveDashboards: true,
- canSaveVisualizations: true,
- },
+ editable: true,
getTrigger,
documentToExpression: () =>
Promise.resolve({
@@ -154,7 +151,7 @@ describe('embeddable', () => {
expressionRenderer,
basePath,
indexPatternService: {} as IndexPatternsContract,
- capabilities: { canSaveDashboards: true, canSaveVisualizations: true },
+ editable: true,
getTrigger,
documentToExpression: () =>
Promise.resolve({
@@ -194,10 +191,7 @@ describe('embeddable', () => {
expressionRenderer,
basePath,
indexPatternService: {} as IndexPatternsContract,
- capabilities: {
- canSaveDashboards: true,
- canSaveVisualizations: true,
- },
+ editable: true,
getTrigger,
documentToExpression: () =>
Promise.resolve({
@@ -237,10 +231,7 @@ describe('embeddable', () => {
indexPatternService: ({
get: (id: string) => Promise.resolve({ id }),
} as unknown) as IndexPatternsContract,
- capabilities: {
- canSaveDashboards: true,
- canSaveVisualizations: true,
- },
+ editable: true,
getTrigger,
documentToExpression: () =>
Promise.resolve({
@@ -275,10 +266,7 @@ describe('embeddable', () => {
expressionRenderer,
basePath,
indexPatternService: {} as IndexPatternsContract,
- capabilities: {
- canSaveDashboards: true,
- canSaveVisualizations: true,
- },
+ editable: true,
getTrigger,
documentToExpression: () =>
Promise.resolve({
@@ -319,7 +307,7 @@ describe('embeddable', () => {
expressionRenderer,
basePath,
indexPatternService: {} as IndexPatternsContract,
- capabilities: { canSaveDashboards: true, canSaveVisualizations: true },
+ editable: true,
getTrigger,
documentToExpression: () =>
Promise.resolve({
@@ -364,10 +352,7 @@ describe('embeddable', () => {
expressionRenderer,
basePath,
indexPatternService: {} as IndexPatternsContract,
- capabilities: {
- canSaveDashboards: true,
- canSaveVisualizations: true,
- },
+ editable: true,
getTrigger,
documentToExpression: () =>
Promise.resolve({
@@ -410,10 +395,7 @@ describe('embeddable', () => {
expressionRenderer,
basePath,
indexPatternService: {} as IndexPatternsContract,
- capabilities: {
- canSaveDashboards: true,
- canSaveVisualizations: true,
- },
+ editable: true,
getTrigger,
documentToExpression: () =>
Promise.resolve({
@@ -463,10 +445,7 @@ describe('embeddable', () => {
expressionRenderer,
basePath,
indexPatternService: {} as IndexPatternsContract,
- capabilities: {
- canSaveDashboards: true,
- canSaveVisualizations: true,
- },
+ editable: true,
getTrigger,
documentToExpression: () =>
Promise.resolve({
@@ -516,10 +495,7 @@ describe('embeddable', () => {
expressionRenderer,
basePath,
indexPatternService: {} as IndexPatternsContract,
- capabilities: {
- canSaveDashboards: true,
- canSaveVisualizations: true,
- },
+ editable: true,
getTrigger,
documentToExpression: () =>
Promise.resolve({
@@ -568,10 +544,7 @@ describe('embeddable', () => {
expressionRenderer,
basePath,
indexPatternService: ({ get: jest.fn() } as unknown) as IndexPatternsContract,
- capabilities: {
- canSaveDashboards: true,
- canSaveVisualizations: true,
- },
+ editable: true,
getTrigger,
documentToExpression: () =>
Promise.resolve({
@@ -609,10 +582,7 @@ describe('embeddable', () => {
expressionRenderer,
basePath,
indexPatternService: {} as IndexPatternsContract,
- capabilities: {
- canSaveDashboards: true,
- canSaveVisualizations: true,
- },
+ editable: true,
getTrigger,
documentToExpression: () =>
Promise.resolve({
@@ -650,10 +620,7 @@ describe('embeddable', () => {
expressionRenderer,
basePath,
indexPatternService: {} as IndexPatternsContract,
- capabilities: {
- canSaveDashboards: true,
- canSaveVisualizations: true,
- },
+ editable: true,
getTrigger,
documentToExpression: () =>
Promise.resolve({
@@ -691,10 +658,7 @@ describe('embeddable', () => {
expressionRenderer,
basePath,
indexPatternService: {} as IndexPatternsContract,
- capabilities: {
- canSaveDashboards: true,
- canSaveVisualizations: true,
- },
+ editable: true,
getTrigger,
documentToExpression: () =>
Promise.resolve({
diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx
index a3316e0083d35..1db067606dc82 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx
@@ -88,13 +88,13 @@ export interface LensEmbeddableDeps {
documentToExpression: (
doc: Document
) => Promise<{ ast: Ast | null; errors: ErrorMessage[] | undefined }>;
+ editable: boolean;
indexPatternService: IndexPatternsContract;
expressionRenderer: ReactExpressionRendererType;
timefilter: TimefilterContract;
basePath: IBasePath;
getTrigger?: UiActionsStart['getTrigger'] | undefined;
getTriggerCompatibleActions?: UiActionsStart['getTriggerCompatibleActions'];
- capabilities: { canSaveVisualizations: boolean; canSaveDashboards: boolean };
}
export class Embeddable
@@ -129,6 +129,7 @@ export class Embeddable
initialInput,
{
editApp: 'lens',
+ editable: deps.editable,
},
parent
);
@@ -325,7 +326,7 @@ export class Embeddable
hasCompatibleActions={this.hasCompatibleActions}
className={input.className}
style={input.style}
- canEdit={this.getIsEditable() && input.viewMode === 'edit'}
+ canEdit={this.deps.editable && input.viewMode === 'edit'}
/>,
domNode
);
@@ -450,7 +451,6 @@ export class Embeddable
this.updateOutput({
...this.getOutput(),
defaultTitle: this.savedVis.title,
- editable: this.getIsEditable(),
title,
editPath: getEditPath(savedObjectId),
editUrl: this.deps.basePath.prepend(`/app/lens${getEditPath(savedObjectId)}`),
@@ -458,13 +458,6 @@ export class Embeddable
});
}
- private getIsEditable() {
- return (
- this.deps.capabilities.canSaveVisualizations ||
- (!this.inputIsRefType(this.getInput()) && this.deps.capabilities.canSaveDashboards)
- );
- }
-
public inputIsRefType = (
input: LensByValueInput | LensByReferenceInput
): input is LensByReferenceInput => {
diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts
index 1a4962bd1fe8e..a676b7283671c 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts
+++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts
@@ -53,7 +53,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition {
public isEditable = async () => {
const { capabilities } = await this.getStartServices();
- return Boolean(capabilities.visualize.save || capabilities.dashboard?.showWriteControls);
+ return capabilities.visualize.save as boolean;
};
canCreateNew() {
@@ -86,7 +86,6 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition {
coreHttp,
attributeService,
indexPatternService,
- capabilities,
} = await this.getStartServices();
const { Embeddable } = await import('../../async_services');
@@ -97,14 +96,11 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition {
indexPatternService,
timefilter,
expressionRenderer,
+ editable: await this.isEditable(),
basePath: coreHttp.basePath,
getTrigger: uiActions?.getTrigger,
getTriggerCompatibleActions: uiActions?.getTriggerCompatibleActions,
documentToExpression,
- capabilities: {
- canSaveDashboards: Boolean(capabilities.dashboard?.showWriteControls),
- canSaveVisualizations: Boolean(capabilities.visualize.save),
- },
},
input,
parent
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts
index 14834adfc33cc..0ea533e22e4d9 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts
@@ -474,53 +474,6 @@ describe('IndexPattern Data Source', () => {
expect(ast.chain[0].arguments.timeFields).toEqual(['timestamp', 'another_datefield']);
});
- it('should add the suffix to the remap column id if provided by the operation', async () => {
- const queryBaseState: IndexPatternBaseState = {
- currentIndexPatternId: '1',
- layers: {
- first: {
- indexPatternId: '1',
- columnOrder: ['def', 'abc'],
- columns: {
- abc: {
- label: '23rd percentile',
- dataType: 'number',
- isBucketed: false,
- sourceField: 'bytes',
- operationType: 'percentile',
- params: {
- percentile: 23,
- },
- },
- def: {
- label: 'Terms',
- dataType: 'string',
- isBucketed: true,
- operationType: 'terms',
- sourceField: 'source',
- params: {
- size: 5,
- orderBy: {
- type: 'alphabetical',
- },
- orderDirection: 'asc',
- },
- },
- },
- },
- },
- };
-
- const state = enrichBaseState(queryBaseState);
-
- const ast = indexPatternDatasource.toExpression(state, 'first') as Ast;
- expect(Object.keys(JSON.parse(ast.chain[1].arguments.idMap[0] as string))).toEqual([
- 'col-0-def',
- // col-1 is the auto naming of esasggs, abc is the specified column id, .23 is the generated suffix
- 'col-1-abc.23',
- ]);
- });
-
it('should wrap filtered metrics in filtered metric aggregation', async () => {
const queryBaseState: IndexPatternBaseState = {
currentIndexPatternId: '1',
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx
index 2d2227396afa6..b7e92a0b54952 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx
@@ -64,13 +64,6 @@ export function getInvalidFieldMessage(
: undefined;
}
-export function getEsAggsSuffix(column: IndexPatternColumn) {
- const operationDefinition = operationDefinitionMap[column.operationType];
- return operationDefinition.input === 'field' && operationDefinition.getEsAggsSuffix
- ? operationDefinition.getEsAggsSuffix(column)
- : '';
-}
-
export function getSafeName(name: string, indexPattern: IndexPattern): string {
const field = indexPattern.getFieldByName(name);
return field
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts
index b3aa93b062eb1..0b63dc6ece974 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts
@@ -311,13 +311,6 @@ interface FieldBasedOperationDefinition {
layer: IndexPatternLayer,
uiSettings: IUiSettingsClient
) => ExpressionAstFunction;
- /**
- * Optional function to return the suffix used for ES bucket paths and esaggs column id.
- * This is relevant for multi metrics to pick the right value.
- *
- * @param column The current column
- */
- getEsAggsSuffix?: (column: C) => string;
/**
* Validate that the operation has the right preconditions in the state. For example:
*
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.test.tsx
index 9ac91be5a17ec..c14ff9f86f602 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.test.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.test.tsx
@@ -127,7 +127,7 @@ describe('percentile', () => {
expect(esAggsFn).toEqual(
expect.objectContaining({
arguments: expect.objectContaining({
- percents: [23],
+ percentile: [23],
field: ['a'],
}),
})
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx
index 639b9e3a95c47..dd0f3b978da5f 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx
@@ -51,6 +51,7 @@ export const percentileOperation: OperationDefinition {
if (supportedFieldTypes.includes(fieldType) && aggregatable && !aggregationRestrictions) {
return {
@@ -86,6 +87,7 @@ export const percentileOperation: OperationDefinition {
- return buildExpressionFunction('aggPercentiles', {
- id: columnId,
- enabled: true,
- schema: 'metric',
- field: column.sourceField,
- percents: [column.params.percentile],
- }).toAst();
- },
- getEsAggsSuffix: (column) => {
- const value = column.params.percentile;
- return `.${value}`;
+ return buildExpressionFunction(
+ 'aggSinglePercentile',
+ {
+ id: columnId,
+ enabled: true,
+ schema: 'metric',
+ field: column.sourceField,
+ percentile: column.params.percentile,
+ }
+ ).toAst();
},
getErrorMessage: (layer, columnId, indexPattern) =>
getInvalidFieldMessage(layer.columns[columnId] as FieldBasedIndexPatternColumn, indexPattern),
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx
index a4a061db04797..857e8b3605cfc 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx
@@ -23,7 +23,7 @@ import { DataType } from '../../../../types';
import { OperationDefinition } from '../index';
import { FieldBasedIndexPatternColumn } from '../column_types';
import { ValuesInput } from './values_input';
-import { getEsAggsSuffix, getInvalidFieldMessage } from '../helpers';
+import { getInvalidFieldMessage } from '../helpers';
import type { IndexPatternLayer } from '../../../types';
function ofName(name?: string) {
@@ -137,11 +137,7 @@ export const termsOperation: OperationDefinition {
})
);
});
-
- it('should include esaggs suffix from other columns in orderby argument', () => {
- const termsColumn = layer.columns.col1 as TermsIndexPatternColumn;
- const esAggsFn = termsOperation.toEsAggsFn(
- {
- ...termsColumn,
- params: {
- ...termsColumn.params,
- otherBucket: true,
- orderBy: { type: 'column', columnId: 'abcde' },
- },
- },
- 'col1',
- {} as IndexPattern,
- {
- ...layer,
- columns: {
- ...layer.columns,
- abcde: {
- dataType: 'number',
- isBucketed: false,
- operationType: 'percentile',
- sourceField: 'abc',
- label: '',
- params: {
- percentile: 12,
- },
- },
- },
- },
- uiSettingsMock
- );
- expect(esAggsFn).toEqual(
- expect.objectContaining({
- arguments: expect.objectContaining({
- orderBy: ['abcde.12'],
- }),
- })
- );
- });
});
describe('onFieldChange', () => {
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts
index d786d781199b6..b272e5476aa63 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts
@@ -23,7 +23,6 @@ import { operationDefinitionMap } from './operations';
import { IndexPattern, IndexPatternPrivateState, IndexPatternLayer } from './types';
import { OriginalColumn } from './rename_columns';
import { dateHistogramOperation } from './operations/definitions';
-import { getEsAggsSuffix } from './operations/definitions/helpers';
function getExpressionForLayer(
layer: IndexPatternLayer,
@@ -104,10 +103,9 @@ function getExpressionForLayer(
const idMap = columnEntries.reduce((currentIdMap, [colId, column], index) => {
const esAggsId = `col-${columnEntries.length === 1 ? 0 : index}-${colId}`;
- const suffix = getEsAggsSuffix(column);
return {
...currentIdMap,
- [`${esAggsId}${suffix}`]: {
+ [esAggsId]: {
...column,
id: colId,
},
diff --git a/x-pack/plugins/maps/public/actions/ui_actions.ts b/x-pack/plugins/maps/public/actions/ui_actions.ts
index ef2c9ac807c1d..f9c0e324aa5d8 100644
--- a/x-pack/plugins/maps/public/actions/ui_actions.ts
+++ b/x-pack/plugins/maps/public/actions/ui_actions.ts
@@ -14,8 +14,6 @@ import { trackMapSettings } from './map_actions';
import { setSelectedLayer } from './layer_actions';
export const UPDATE_FLYOUT = 'UPDATE_FLYOUT';
-export const CLOSE_SET_VIEW = 'CLOSE_SET_VIEW';
-export const OPEN_SET_VIEW = 'OPEN_SET_VIEW';
export const SET_IS_LAYER_TOC_OPEN = 'SET_IS_LAYER_TOC_OPEN';
export const SET_FULL_SCREEN = 'SET_FULL_SCREEN';
export const SET_READ_ONLY = 'SET_READ_ONLY';
@@ -50,16 +48,6 @@ export function openMapSettings() {
dispatch(updateFlyout(FLYOUT_STATE.MAP_SETTINGS_PANEL));
};
}
-export function closeSetView() {
- return {
- type: CLOSE_SET_VIEW,
- };
-}
-export function openSetView() {
- return {
- type: OPEN_SET_VIEW,
- };
-}
export function setIsLayerTOCOpen(isLayerTOCOpen: boolean) {
return {
type: SET_IS_LAYER_TOC_OPEN,
diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/data_mapping/data_mapping_popover.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/data_mapping/data_mapping_popover.tsx
index 5c2e11813bb5f..47c2012d6ed8f 100644
--- a/x-pack/plugins/maps/public/classes/styles/vector/components/data_mapping/data_mapping_popover.tsx
+++ b/x-pack/plugins/maps/public/classes/styles/vector/components/data_mapping/data_mapping_popover.tsx
@@ -25,9 +25,9 @@ export class DataMappingPopover extends Component {
};
_togglePopover = () => {
- this.setState({
- isPopoverOpen: !this.state.isPopoverOpen,
- });
+ this.setState((prevState) => ({
+ isPopoverOpen: !prevState.isPopoverOpen,
+ }));
};
_closePopover = () => {
diff --git a/x-pack/plugins/maps/public/components/tooltip_selector/add_tooltip_field_popover.tsx b/x-pack/plugins/maps/public/components/tooltip_selector/add_tooltip_field_popover.tsx
index 78739731e14b6..04ae7af62fddc 100644
--- a/x-pack/plugins/maps/public/components/tooltip_selector/add_tooltip_field_popover.tsx
+++ b/x-pack/plugins/maps/public/components/tooltip_selector/add_tooltip_field_popover.tsx
@@ -98,9 +98,9 @@ export class AddTooltipFieldPopover extends Component {
}
_togglePopover = () => {
- this.setState({
- isPopoverOpen: !this.state.isPopoverOpen,
- });
+ this.setState((prevState) => ({
+ isPopoverOpen: !prevState.isPopoverOpen,
+ }));
};
_closePopover = () => {
diff --git a/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx b/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx
index 622aeae3cbb87..525ba394ed503 100644
--- a/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx
+++ b/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx
@@ -16,7 +16,6 @@ import { ActionExecutionContext, Action } from 'src/plugins/ui_actions/public';
import { MBMap } from '../mb_map';
// @ts-expect-error
import { WidgetOverlay } from '../widget_overlay';
-// @ts-expect-error
import { ToolbarOverlay } from '../toolbar_overlay';
// @ts-expect-error
import { LayerPanel } from '../layer_panel';
diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/__snapshots__/toolbar_overlay.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/__snapshots__/toolbar_overlay.test.tsx.snap
index 3407bcfd4f845..506767fcd4706 100644
--- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/__snapshots__/toolbar_overlay.test.tsx.snap
+++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/__snapshots__/toolbar_overlay.test.tsx.snap
@@ -35,7 +35,12 @@ exports[`Must zoom tools and draw filter tools 1`] = `
diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/index.js b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/index.js
deleted file mode 100644
index 6470718fc7e4a..0000000000000
--- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/index.js
+++ /dev/null
@@ -1,16 +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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { connect } from 'react-redux';
-import { ToolbarOverlay } from './toolbar_overlay';
-
-function mapStateToProps() {
- return {};
-}
-
-const connectedToolbarOverlay = connect(mapStateToProps, null)(ToolbarOverlay);
-export { connectedToolbarOverlay as ToolbarOverlay };
diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/index.ts b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/index.ts
new file mode 100644
index 0000000000000..d1008edfd572d
--- /dev/null
+++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/index.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+export { ToolbarOverlay } from './toolbar_overlay';
diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/index.js b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/index.ts
similarity index 62%
rename from x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/index.js
rename to x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/index.ts
index 3220f84967f16..8f7a3cf762a6b 100644
--- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/index.js
+++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/index.ts
@@ -5,33 +5,27 @@
* 2.0.
*/
+import { AnyAction } from 'redux';
+import { ThunkDispatch } from 'redux-thunk';
import { connect } from 'react-redux';
import { SetViewControl } from './set_view_control';
-import { setGotoWithCenter, closeSetView, openSetView } from '../../../actions';
+import { setGotoWithCenter } from '../../../actions';
import { getMapZoom, getMapCenter, getMapSettings } from '../../../selectors/map_selectors';
-import { getIsSetViewOpen } from '../../../selectors/ui_selectors';
+import { MapStoreState } from '../../../reducers/store';
-function mapStateToProps(state = {}) {
+function mapStateToProps(state: MapStoreState) {
return {
settings: getMapSettings(state),
- isSetViewOpen: getIsSetViewOpen(state),
zoom: getMapZoom(state),
center: getMapCenter(state),
};
}
-function mapDispatchToProps(dispatch) {
+function mapDispatchToProps(dispatch: ThunkDispatch) {
return {
- onSubmit: ({ lat, lon, zoom }) => {
- dispatch(closeSetView());
+ onSubmit: ({ lat, lon, zoom }: { lat: number; lon: number; zoom: number }) => {
dispatch(setGotoWithCenter({ lat, lon, zoom }));
},
- closeSetView: () => {
- dispatch(closeSetView());
- },
- openSetView: () => {
- dispatch(openSetView());
- },
};
}
diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_control.js b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_control.tsx
similarity index 69%
rename from x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_control.js
rename to x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_control.tsx
index 21818476d6965..b657d6369f8aa 100644
--- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_control.js
+++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_control.tsx
@@ -5,8 +5,7 @@
* 2.0.
*/
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
+import React, { ChangeEvent, Component } from 'react';
import {
EuiForm,
EuiFormRow,
@@ -19,57 +18,86 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
-
-function getViewString(lat, lon, zoom) {
- return `${lat},${lon},${zoom}`;
+import { MapCenter } from '../../../../common/descriptor_types';
+import { MapSettings } from '../../../reducers/map';
+
+export interface Props {
+ settings: MapSettings;
+ zoom: number;
+ center: MapCenter;
+ onSubmit: ({ lat, lon, zoom }: { lat: number; lon: number; zoom: number }) => void;
}
-export class SetViewControl extends Component {
- state = {};
-
- static getDerivedStateFromProps(nextProps, prevState) {
- const nextView = getViewString(nextProps.center.lat, nextProps.center.lon, nextProps.zoom);
- if (nextView !== prevState.prevView) {
- return {
- lat: nextProps.center.lat,
- lon: nextProps.center.lon,
- zoom: nextProps.zoom,
- prevView: nextView,
- };
- }
+interface State {
+ isPopoverOpen: boolean;
+ lat: number | string;
+ lon: number | string;
+ zoom: number | string;
+}
- return null;
- }
+export class SetViewControl extends Component {
+ state: State = {
+ isPopoverOpen: false,
+ lat: 0,
+ lon: 0,
+ zoom: 0,
+ };
_togglePopover = () => {
- if (this.props.isSetViewOpen) {
- this.props.closeSetView();
+ if (this.state.isPopoverOpen) {
+ this._closePopover();
return;
}
- this.props.openSetView();
+ this.setState({
+ lat: this.props.center.lat,
+ lon: this.props.center.lon,
+ zoom: this.props.zoom,
+ isPopoverOpen: true,
+ });
+ };
+
+ _closePopover = () => {
+ this.setState({
+ isPopoverOpen: false,
+ });
};
- _onLatChange = (evt) => {
+ _onLatChange = (evt: ChangeEvent) => {
this._onChange('lat', evt);
};
- _onLonChange = (evt) => {
+ _onLonChange = (evt: ChangeEvent) => {
this._onChange('lon', evt);
};
- _onZoomChange = (evt) => {
+ _onZoomChange = (evt: ChangeEvent) => {
this._onChange('zoom', evt);
};
- _onChange = (name, evt) => {
+ _onChange = (name: 'lat' | 'lon' | 'zoom', evt: ChangeEvent) => {
const sanitizedValue = parseFloat(evt.target.value);
+ // @ts-expect-error
this.setState({
[name]: isNaN(sanitizedValue) ? '' : sanitizedValue,
});
};
- _renderNumberFormRow = ({ value, min, max, onChange, label, dataTestSubj }) => {
+ _renderNumberFormRow = ({
+ value,
+ min,
+ max,
+ onChange,
+ label,
+ dataTestSubj,
+ }: {
+ value: string | number;
+ min: number;
+ max: number;
+ onChange: (evt: ChangeEvent) => void;
+ label: string;
+ dataTestSubj: string;
+ }) => {
const isInvalid = value === '' || value > max || value < min;
const error = isInvalid ? `Must be between ${min} and ${max}` : null;
return {
@@ -90,7 +118,8 @@ export class SetViewControl extends Component {
_onSubmit = () => {
const { lat, lon, zoom } = this.state;
- this.props.onSubmit({ lat, lon, zoom });
+ this._closePopover();
+ this.props.onSubmit({ lat: lat as number, lon: lon as number, zoom: zoom as number });
};
_renderSetViewForm() {
@@ -175,23 +204,11 @@ export class SetViewControl extends Component {
})}
/>
}
- isOpen={this.props.isSetViewOpen}
- closePopover={this.props.closeSetView}
+ isOpen={this.state.isPopoverOpen}
+ closePopover={this._closePopover}
>
{this._renderSetViewForm()}
);
}
}
-
-SetViewControl.propTypes = {
- isSetViewOpen: PropTypes.bool.isRequired,
- zoom: PropTypes.number.isRequired,
- center: PropTypes.shape({
- lat: PropTypes.number.isRequired,
- lon: PropTypes.number.isRequired,
- }),
- onSubmit: PropTypes.func.isRequired,
- closeSetView: PropTypes.func.isRequired,
- openSetView: PropTypes.func.isRequired,
-};
diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.js b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.js
deleted file mode 100644
index ceca3f5b7fdc1..0000000000000
--- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.js
+++ /dev/null
@@ -1,53 +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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import React from 'react';
-import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
-import { SetViewControl } from './set_view_control';
-import { ToolsControl } from './tools_control';
-import { FitToData } from './fit_to_data';
-
-export class ToolbarOverlay extends React.Component {
- _renderToolsControl() {
- const { addFilters, geoFields, getFilterActions, getActionContext } = this.props;
- if (!addFilters || !geoFields.length) {
- return null;
- }
-
- return (
-
-
-
- );
- }
-
- render() {
- return (
-
-
-
-
-
-
-
-
-
- {this._renderToolsControl()}
-
- );
- }
-}
diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.test.tsx b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.test.tsx
index a6d17819e2fea..d8ac971ae3983 100644
--- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.test.tsx
+++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.test.tsx
@@ -7,6 +7,7 @@
import React from 'react';
import { shallow } from 'enzyme';
+import { Filter } from 'src/plugins/data/public';
jest.mock('../../kibana_services', () => {
return {
@@ -16,15 +17,25 @@ jest.mock('../../kibana_services', () => {
};
});
-// @ts-ignore
import { ToolbarOverlay } from './toolbar_overlay';
test('Must render zoom tools', async () => {
- const component = shallow();
+ const component = shallow();
expect(component).toMatchSnapshot();
});
test('Must zoom tools and draw filter tools', async () => {
- const component = shallow( {}} geoFields={['coordinates']} />);
+ const geoFieldWithIndex = {
+ geoFieldName: 'myGeoFieldName',
+ geoFieldType: 'geo_point',
+ indexPatternTitle: 'myIndex',
+ indexPatternId: '1',
+ };
+ const component = shallow(
+ {}}
+ geoFields={[geoFieldWithIndex]}
+ />
+ );
expect(component).toMatchSnapshot();
});
diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.tsx b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.tsx
new file mode 100644
index 0000000000000..c5208bc254fc8
--- /dev/null
+++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.tsx
@@ -0,0 +1,61 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import { Filter } from 'src/plugins/data/public';
+import { ActionExecutionContext, Action } from 'src/plugins/ui_actions/public';
+import { SetViewControl } from './set_view_control';
+import { ToolsControl } from './tools_control';
+import { FitToData } from './fit_to_data';
+import { GeoFieldWithIndex } from '../../components/geo_field_with_index';
+
+export interface Props {
+ addFilters?: ((filters: Filter[], actionId: string) => Promise) | null;
+ geoFields: GeoFieldWithIndex[];
+ getFilterActions?: () => Promise;
+ getActionContext?: () => ActionExecutionContext;
+}
+
+export function ToolbarOverlay(props: Props) {
+ function renderToolsControl() {
+ const { addFilters, geoFields, getFilterActions, getActionContext } = props;
+ if (!addFilters || !geoFields.length) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+ {renderToolsControl()}
+
+ );
+}
diff --git a/x-pack/plugins/maps/public/reducers/ui.ts b/x-pack/plugins/maps/public/reducers/ui.ts
index 90dafa3afb67a..676ac6ce12efe 100644
--- a/x-pack/plugins/maps/public/reducers/ui.ts
+++ b/x-pack/plugins/maps/public/reducers/ui.ts
@@ -11,8 +11,6 @@ import { getMapsCapabilities } from '../kibana_services';
import {
UPDATE_FLYOUT,
- CLOSE_SET_VIEW,
- OPEN_SET_VIEW,
SET_IS_LAYER_TOC_OPEN,
SET_FULL_SCREEN,
SET_READ_ONLY,
@@ -33,7 +31,6 @@ export type MapUiState = {
isFullScreen: boolean;
isReadOnly: boolean;
isLayerTOCOpen: boolean;
- isSetViewOpen: boolean;
openTOCDetails: string[];
};
@@ -44,7 +41,6 @@ export const DEFAULT_MAP_UI_STATE = {
isFullScreen: false,
isReadOnly: !getMapsCapabilities().save,
isLayerTOCOpen: DEFAULT_IS_LAYER_TOC_OPEN,
- isSetViewOpen: false,
// storing TOC detail visibility outside of map.layerList because its UI state and not map rendering state.
// This also makes for easy read/write access for embeddables.
openTOCDetails: [],
@@ -55,10 +51,6 @@ export function ui(state: MapUiState = DEFAULT_MAP_UI_STATE, action: any) {
switch (action.type) {
case UPDATE_FLYOUT:
return { ...state, flyoutDisplay: action.display };
- case CLOSE_SET_VIEW:
- return { ...state, isSetViewOpen: false };
- case OPEN_SET_VIEW:
- return { ...state, isSetViewOpen: true };
case SET_IS_LAYER_TOC_OPEN:
return { ...state, isLayerTOCOpen: action.isLayerTOCOpen };
case SET_FULL_SCREEN:
diff --git a/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx b/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx
index 7e0aa59756876..597cd8e9c4287 100644
--- a/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx
+++ b/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx
@@ -201,11 +201,7 @@ export function getTopNavConfig({
options={tagSelector}
/>
) : (
-
+
);
showSaveModal(saveModal, getCoreI18n().Context, PresentationUtilContext);
diff --git a/x-pack/plugins/maps/public/selectors/ui_selectors.ts b/x-pack/plugins/maps/public/selectors/ui_selectors.ts
index dc34035c21b29..e5c83bd0f8f4a 100644
--- a/x-pack/plugins/maps/public/selectors/ui_selectors.ts
+++ b/x-pack/plugins/maps/public/selectors/ui_selectors.ts
@@ -10,7 +10,6 @@ import { MapStoreState } from '../reducers/store';
import { FLYOUT_STATE } from '../reducers/ui';
export const getFlyoutDisplay = ({ ui }: MapStoreState): FLYOUT_STATE => ui.flyoutDisplay;
-export const getIsSetViewOpen = ({ ui }: MapStoreState): boolean => ui.isSetViewOpen;
export const getIsLayerTOCOpen = ({ ui }: MapStoreState): boolean => ui.isLayerTOCOpen;
export const getOpenTOCDetails = ({ ui }: MapStoreState): string[] => ui.openTOCDetails;
export const getIsFullScreen = ({ ui }: MapStoreState): boolean => ui.isFullScreen;
diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/index.ts b/x-pack/test/functional/apps/dashboard/feature_controls/index.ts
index 3b32ea031f6e2..38d139c59430e 100644
--- a/x-pack/test/functional/apps/dashboard/feature_controls/index.ts
+++ b/x-pack/test/functional/apps/dashboard/feature_controls/index.ts
@@ -11,7 +11,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
describe('feature controls', function () {
this.tags(['skipFirefox']);
loadTestFile(require.resolve('./dashboard_security'));
- loadTestFile(require.resolve('./time_to_visualize_security'));
loadTestFile(require.resolve('./dashboard_spaces'));
});
}
diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts
deleted file mode 100644
index 3ebc53cc7cf27..0000000000000
--- a/x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts
+++ /dev/null
@@ -1,233 +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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import expect from '@kbn/expect';
-import { FtrProviderContext } from '../../../ftr_provider_context';
-
-export default function ({ getPageObjects, getService }: FtrProviderContext) {
- const PageObjects = getPageObjects([
- 'timeToVisualize',
- 'timePicker',
- 'dashboard',
- 'visEditor',
- 'visualize',
- 'security',
- 'common',
- 'header',
- 'lens',
- ]);
-
- const dashboardVisualizations = getService('dashboardVisualizations');
- const dashboardPanelActions = getService('dashboardPanelActions');
- const dashboardExpect = getService('dashboardExpect');
- const testSubjects = getService('testSubjects');
- const esArchiver = getService('esArchiver');
- const security = getService('security');
- const find = getService('find');
-
- describe('dashboard time to visualize security', () => {
- before(async () => {
- await esArchiver.load('dashboard/feature_controls/security');
- await esArchiver.loadIfNeeded('logstash_functional');
-
- // ensure we're logged out so we can login as the appropriate users
- await PageObjects.security.forceLogout();
-
- await security.role.create('dashboard_write_vis_read', {
- elasticsearch: {
- indices: [{ names: ['logstash-*'], privileges: ['read', 'view_index_metadata'] }],
- },
- kibana: [
- {
- feature: {
- dashboard: ['all'],
- visualize: ['read'],
- },
- spaces: ['*'],
- },
- ],
- });
-
- await security.user.create('dashboard_write_vis_read_user', {
- password: 'dashboard_write_vis_read_user-password',
- roles: ['dashboard_write_vis_read'],
- full_name: 'test user',
- });
-
- await PageObjects.security.login(
- 'dashboard_write_vis_read_user',
- 'dashboard_write_vis_read_user-password',
- {
- expectSpaceSelector: false,
- }
- );
- });
-
- after(async () => {
- await security.role.delete('dashboard_write_vis_read');
- await security.user.delete('dashboard_write_vis_read_user');
-
- await esArchiver.unload('dashboard/feature_controls/security');
-
- // logout, so the other tests don't accidentally run as the custom users we're testing below
- await PageObjects.security.forceLogout();
- });
-
- describe('lens by value works without library save permissions', () => {
- before(async () => {
- await PageObjects.common.navigateToApp('dashboard');
- await PageObjects.dashboard.preserveCrossAppState();
- await PageObjects.dashboard.clickNewDashboard();
- });
-
- it('can add a lens panel by value', async () => {
- await dashboardVisualizations.ensureNewVisualizationDialogIsShowing();
- await PageObjects.lens.createAndAddLensFromDashboard({});
- const newPanelCount = await PageObjects.dashboard.getPanelCount();
- expect(newPanelCount).to.eql(1);
- });
-
- it('edits to a by value lens panel are properly applied', async () => {
- await PageObjects.dashboard.waitForRenderComplete();
- await dashboardPanelActions.openContextMenu();
- await dashboardPanelActions.clickEdit();
- await PageObjects.lens.switchToVisualization('donut');
- await PageObjects.lens.saveAndReturn();
- await PageObjects.dashboard.waitForRenderComplete();
-
- const pieExists = await find.existsByCssSelector('.lnsPieExpression__container');
- expect(pieExists).to.be(true);
- });
-
- it('disables save to library button without visualize save permissions', async () => {
- await PageObjects.dashboard.waitForRenderComplete();
- await dashboardPanelActions.openContextMenu();
- await dashboardPanelActions.clickEdit();
- const saveButton = await testSubjects.find('lnsApp_saveButton');
- expect(await saveButton.getAttribute('disabled')).to.equal('true');
- await PageObjects.lens.saveAndReturn();
- await PageObjects.timeToVisualize.resetNewDashboard();
- });
-
- it('should allow new lens to be added by value, but not by reference', async () => {
- await PageObjects.visualize.navigateToNewVisualization();
- await PageObjects.visualize.clickVisType('lens');
- await PageObjects.lens.goToTimeRange();
-
- await PageObjects.lens.configureDimension({
- dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension',
- operation: 'average',
- field: 'bytes',
- });
-
- await PageObjects.lens.switchToVisualization('lnsMetric');
-
- await PageObjects.lens.waitForVisualization();
- await PageObjects.lens.assertMetric('Average of bytes', '5,727.322');
-
- await PageObjects.header.waitUntilLoadingHasFinished();
- await testSubjects.click('lnsApp_saveButton');
-
- const libraryCheckbox = await find.byCssSelector('#add-to-library-checkbox');
- expect(await libraryCheckbox.getAttribute('disabled')).to.equal('true');
-
- await PageObjects.timeToVisualize.saveFromModal('New Lens from Modal', {
- addToDashboard: 'new',
- saveAsNew: true,
- saveToLibrary: false,
- });
-
- await PageObjects.dashboard.waitForRenderComplete();
-
- await PageObjects.lens.assertMetric('Average of bytes', '5,727.322');
- const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists(
- 'New Lens from Modal'
- );
- expect(isLinked).to.be(false);
-
- const panelCount = await PageObjects.dashboard.getPanelCount();
- expect(panelCount).to.eql(1);
-
- await PageObjects.timeToVisualize.resetNewDashboard();
- });
- });
-
- describe('visualize by value works without library save permissions', () => {
- const originalMarkdownText = 'Original markdown text';
- const modifiedMarkdownText = 'Modified markdown text';
-
- before(async () => {
- await PageObjects.common.navigateToApp('dashboard');
- await PageObjects.dashboard.preserveCrossAppState();
- await PageObjects.dashboard.clickNewDashboard();
- });
-
- it('can add a markdown panel by value', async () => {
- await PageObjects.common.navigateToApp('dashboard');
- await PageObjects.dashboard.clickNewDashboard();
- await PageObjects.dashboard.waitForRenderComplete();
-
- await testSubjects.click('dashboardAddNewPanelButton');
- await dashboardVisualizations.ensureNewVisualizationDialogIsShowing();
- await PageObjects.visualize.clickMarkdownWidget();
- await PageObjects.visEditor.setMarkdownTxt(originalMarkdownText);
- await PageObjects.visEditor.clickGo();
-
- await PageObjects.visualize.saveVisualizationAndReturn();
- const newPanelCount = await PageObjects.dashboard.getPanelCount();
- expect(newPanelCount).to.eql(1);
- });
-
- it('edits to a by value visualize panel are properly applied', async () => {
- await dashboardPanelActions.openContextMenu();
- await dashboardPanelActions.clickEdit();
- await PageObjects.header.waitUntilLoadingHasFinished();
- await PageObjects.visEditor.setMarkdownTxt(modifiedMarkdownText);
- await PageObjects.visEditor.clickGo();
- await PageObjects.visualize.saveVisualizationAndReturn();
-
- await PageObjects.dashboard.waitForRenderComplete();
- const markdownText = await testSubjects.find('markdownBody');
- expect(await markdownText.getVisibleText()).to.eql(modifiedMarkdownText);
-
- const newPanelCount = PageObjects.dashboard.getPanelCount();
- expect(newPanelCount).to.eql(1);
- });
-
- it('disables save to library button without visualize save permissions', async () => {
- await dashboardPanelActions.openContextMenu();
- await dashboardPanelActions.clickEdit();
- await PageObjects.header.waitUntilLoadingHasFinished();
- await testSubjects.missingOrFail('visualizeSaveButton');
- await PageObjects.visualize.saveVisualizationAndReturn();
- await PageObjects.timeToVisualize.resetNewDashboard();
- });
-
- it('should allow new visualization to be added by value, but not by reference', async function () {
- await PageObjects.visualize.navigateToNewAggBasedVisualization();
- await PageObjects.visualize.clickMetric();
- await PageObjects.visualize.clickNewSearch();
- await PageObjects.timePicker.setDefaultAbsoluteRange();
-
- await testSubjects.click('visualizeSaveButton');
-
- await PageObjects.visualize.ensureSavePanelOpen();
- const libraryCheckbox = await find.byCssSelector('#add-to-library-checkbox');
- expect(await libraryCheckbox.getAttribute('disabled')).to.equal('true');
-
- await PageObjects.timeToVisualize.saveFromModal('My New Vis 1', {
- addToDashboard: 'new',
- });
-
- await PageObjects.dashboard.waitForRenderComplete();
- await dashboardExpect.metricValuesExist(['14,005']);
- const panelCount = await PageObjects.dashboard.getPanelCount();
- expect(panelCount).to.eql(1);
- });
- });
- });
-}
diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts
index 205a4391062a2..65020be390f9d 100644
--- a/x-pack/test/functional/page_objects/lens_page.ts
+++ b/x-pack/test/functional/page_objects/lens_page.ts
@@ -197,7 +197,10 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
},
async searchField(name: string) {
- await testSubjects.setValue('lnsIndexPatternFieldSearch', name);
+ await testSubjects.setValue('lnsIndexPatternFieldSearch', name, {
+ clearWithKeyboard: true,
+ typeCharByChar: true,
+ });
},
async waitForField(field: string) {
diff --git a/yarn.lock b/yarn.lock
index 486752dce5587..80ad1acf7fccd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1349,10 +1349,10 @@
dependencies:
object-hash "^1.3.0"
-"@elastic/charts@26.0.0":
- version "26.0.0"
- resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-26.0.0.tgz#42f06d3be0f40e0128e301b37bdfc509169c387b"
- integrity sha512-5eBPSjdBb+pVDCcQOYA0dFBiYonHcw7ewxOUxgR8qMmay0xHc7gGUXZiDfIkyUDpJA+a9DS9juNNqKn/M4UbiQ==
+"@elastic/charts@26.1.0":
+ version "26.1.0"
+ resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-26.1.0.tgz#3c8677d84e52ac7209aee19484fb2b7e2a22e5cc"
+ integrity sha512-RiidG+9QIn17o5AW8cntrznH+MaOO8gIAwrkJW1EMInntZgEA66WhVs4Kg2Negp6hsPMMeArQVWbDhXE9ST3qg==
dependencies:
"@popperjs/core" "^2.4.0"
chroma-js "^2.1.0"