mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
decouple url drilldown action from Embeddable framework (#175930)
Part of https://github.com/elastic/kibana/issues/175138 and prerequisite
for https://github.com/elastic/kibana/issues/174960
PR decouples Url drilldown action from Embeddable framework by migrating
to sets of composable interfaces.
### test instructions
1. Create panel and add "Url drilldown"
2. Verify `context` variables are populated with the same values when
they were grabbed from embeddable.input and embeddable.output
<img width="600" alt="Screenshot 2024-01-31 at 2 11 20 PM"
src="150f7a61
-911f-4fb6-bf85-5c0481865e4b">
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
d17fa4bf4e
commit
ca98633754
20 changed files with 229 additions and 385 deletions
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { EmbeddableApiContext } from '@kbn/presentation-publishing';
|
||||
import type { Datatable } from '@kbn/expressions-plugin/common';
|
||||
import { Trigger } from '.';
|
||||
|
||||
|
@ -22,10 +23,7 @@ export const rowClickTrigger: Trigger = {
|
|||
}),
|
||||
};
|
||||
|
||||
export interface RowClickContext {
|
||||
// Need to make this unknown to prevent circular dependencies.
|
||||
// Apps using this property will need to cast to `IEmbeddable`.
|
||||
embeddable?: unknown;
|
||||
export type RowClickContext = Partial<EmbeddableApiContext> & {
|
||||
data: {
|
||||
/**
|
||||
* Row index, starting from 0, where user clicked.
|
||||
|
@ -40,4 +38,4 @@ export interface RowClickContext {
|
|||
*/
|
||||
columns?: string[];
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -18,5 +18,6 @@
|
|||
"kbn_references": [
|
||||
"@kbn/i18n",
|
||||
"@kbn/expressions-plugin",
|
||||
"@kbn/presentation-publishing",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -686,4 +686,12 @@ export class DashboardContainer
|
|||
}
|
||||
if (resetChangedPanelCount) this.reactEmbeddableChildren.next(currentChildren);
|
||||
};
|
||||
|
||||
public getFilters() {
|
||||
return this.getInput().filters;
|
||||
}
|
||||
|
||||
public getQuery(): Query | undefined {
|
||||
return this.getInput().query;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,14 @@ import { i18n } from '@kbn/i18n';
|
|||
import { PhaseEvent, PhaseEventType } from '@kbn/presentation-publishing';
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
import { isNil } from 'lodash';
|
||||
import { BehaviorSubject, map, Subscription, distinct } from 'rxjs';
|
||||
import {
|
||||
BehaviorSubject,
|
||||
map,
|
||||
Subscription,
|
||||
distinct,
|
||||
combineLatest,
|
||||
distinctUntilChanged,
|
||||
} from 'rxjs';
|
||||
import { embeddableStart } from '../../../kibana_services';
|
||||
import { isFilterableEmbeddable } from '../../filterable_embeddable';
|
||||
import {
|
||||
|
@ -31,7 +38,7 @@ import {
|
|||
import { canLinkLegacyEmbeddable, linkLegacyEmbeddable } from './link_legacy_embeddable';
|
||||
import { canUnlinkLegacyEmbeddable, unlinkLegacyEmbeddable } from './unlink_legacy_embeddable';
|
||||
|
||||
export type CommonLegacyInput = EmbeddableInput & { timeRange: TimeRange };
|
||||
export type CommonLegacyInput = EmbeddableInput & { savedObjectId?: string; timeRange: TimeRange };
|
||||
export type CommonLegacyOutput = EmbeddableOutput & { indexPatterns: DataView[] };
|
||||
export type CommonLegacyEmbeddable = IEmbeddable<CommonLegacyInput, CommonLegacyOutput>;
|
||||
|
||||
|
@ -135,6 +142,25 @@ export const legacyEmbeddableToApi = (
|
|||
const defaultPanelTitle = outputKeyToSubject<string>('defaultTitle');
|
||||
const disabledActionIds = inputKeyToSubject<string[] | undefined>('disabledActions');
|
||||
|
||||
function getSavedObjectId(input: { savedObjectId?: string }, output: { savedObjectId?: string }) {
|
||||
return output.savedObjectId ?? input.savedObjectId;
|
||||
}
|
||||
const savedObjectId = new BehaviorSubject<string | undefined>(
|
||||
getSavedObjectId(embeddable.getInput(), embeddable.getOutput())
|
||||
);
|
||||
subscriptions.add(
|
||||
combineLatest([embeddable.getInput$(), embeddable.getOutput$()])
|
||||
.pipe(
|
||||
map(([input, output]) => {
|
||||
return getSavedObjectId(input, output);
|
||||
}),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
.subscribe((nextSavedObjectId) => {
|
||||
savedObjectId.next(nextSavedObjectId);
|
||||
})
|
||||
);
|
||||
|
||||
const blockingError = new BehaviorSubject<ErrorLike | undefined>(undefined);
|
||||
subscriptions.add(
|
||||
embeddable.getOutput$().subscribe({
|
||||
|
@ -238,6 +264,8 @@ export const legacyEmbeddableToApi = (
|
|||
|
||||
canUnlinkFromLibrary: () => canUnlinkLegacyEmbeddable(embeddable),
|
||||
unlinkFromLibrary: () => unlinkLegacyEmbeddable(embeddable),
|
||||
|
||||
savedObjectId,
|
||||
},
|
||||
destroyAPI: () => {
|
||||
subscriptions.unsubscribe();
|
||||
|
|
|
@ -145,6 +145,7 @@ export abstract class Embeddable<
|
|||
getFallbackTimeRange: this.getFallbackTimeRange,
|
||||
canUnlinkFromLibrary: this.canUnlinkFromLibrary,
|
||||
isCompatibleWithLocalUnifiedSearch: this.isCompatibleWithLocalUnifiedSearch,
|
||||
savedObjectId: this.savedObjectId,
|
||||
} = api);
|
||||
|
||||
setTimeout(() => {
|
||||
|
@ -186,6 +187,7 @@ export abstract class Embeddable<
|
|||
public canUnlinkFromLibrary: LegacyEmbeddableAPI['canUnlinkFromLibrary'];
|
||||
public getFallbackTimeRange: LegacyEmbeddableAPI['getFallbackTimeRange'];
|
||||
public isCompatibleWithLocalUnifiedSearch: LegacyEmbeddableAPI['isCompatibleWithLocalUnifiedSearch'];
|
||||
public savedObjectId: LegacyEmbeddableAPI['savedObjectId'];
|
||||
|
||||
public getEditHref(): string | undefined {
|
||||
return this.getOutput().editUrl ?? undefined;
|
||||
|
|
|
@ -24,6 +24,7 @@ import {
|
|||
PublishesWritablePanelDescription,
|
||||
PublishesWritablePanelTitle,
|
||||
PublishesPhaseEvents,
|
||||
PublishesSavedObjectId,
|
||||
} from '@kbn/presentation-publishing';
|
||||
import { Observable } from 'rxjs';
|
||||
import { EmbeddableInput } from '../../../common/types';
|
||||
|
@ -54,7 +55,8 @@ export type LegacyEmbeddableAPI = HasType &
|
|||
PublishesWritablePanelDescription &
|
||||
Partial<CanLinkToLibrary & CanUnlinkFromLibrary> &
|
||||
HasParentApi<DefaultPresentationPanelApi['parentApi']> &
|
||||
EmbeddableHasTimeRange;
|
||||
EmbeddableHasTimeRange &
|
||||
PublishesSavedObjectId;
|
||||
|
||||
export interface EmbeddableAppContext {
|
||||
/**
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { EmbeddableApiContext } from '@kbn/presentation-publishing';
|
||||
import { Datatable, DatatableColumnMeta } from '@kbn/expressions-plugin/common';
|
||||
import { Trigger, RowClickContext } from '@kbn/ui-actions-plugin/public';
|
||||
import { BooleanRelation } from '@kbn/es-query';
|
||||
|
@ -16,8 +17,7 @@ export interface EmbeddableContext<T extends IEmbeddable = IEmbeddable> {
|
|||
embeddable: T;
|
||||
}
|
||||
|
||||
export interface ValueClickContext<T extends IEmbeddable = IEmbeddable> {
|
||||
embeddable?: T;
|
||||
export type ValueClickContext = Partial<EmbeddableApiContext> & {
|
||||
data: {
|
||||
data: Array<{
|
||||
table: Pick<Datatable, 'rows' | 'columns'>;
|
||||
|
@ -28,10 +28,9 @@ export interface ValueClickContext<T extends IEmbeddable = IEmbeddable> {
|
|||
timeFieldName?: string;
|
||||
negate?: boolean;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export interface MultiValueClickContext<T extends IEmbeddable = IEmbeddable> {
|
||||
embeddable?: T;
|
||||
export type MultiValueClickContext = Partial<EmbeddableApiContext> & {
|
||||
data: {
|
||||
data: Array<{
|
||||
table: Pick<Datatable, 'rows' | 'columns'>;
|
||||
|
@ -44,31 +43,29 @@ export interface MultiValueClickContext<T extends IEmbeddable = IEmbeddable> {
|
|||
timeFieldName?: string;
|
||||
negate?: boolean;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export interface CellValueContext<T extends IEmbeddable = IEmbeddable> {
|
||||
embeddable: T;
|
||||
export type CellValueContext = Partial<EmbeddableApiContext> & {
|
||||
data: Array<{
|
||||
value?: any;
|
||||
eventId?: string;
|
||||
columnMeta?: DatatableColumnMeta;
|
||||
}>;
|
||||
}
|
||||
};
|
||||
|
||||
export interface RangeSelectContext<T extends IEmbeddable = IEmbeddable> {
|
||||
embeddable?: T;
|
||||
export type RangeSelectContext = Partial<EmbeddableApiContext> & {
|
||||
data: {
|
||||
table: Datatable;
|
||||
column: number;
|
||||
range: number[];
|
||||
timeFieldName?: string;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export type ChartActionContext<T extends IEmbeddable = IEmbeddable> =
|
||||
| ValueClickContext<T>
|
||||
| MultiValueClickContext<T>
|
||||
| RangeSelectContext<T>
|
||||
export type ChartActionContext =
|
||||
| ValueClickContext
|
||||
| MultiValueClickContext
|
||||
| RangeSelectContext
|
||||
| RowClickContext;
|
||||
|
||||
export const CONTEXT_MENU_TRIGGER = 'CONTEXT_MENU_TRIGGER';
|
||||
|
|
|
@ -11,18 +11,11 @@ import { UiActionsEnhancedDrilldownDefinition as Drilldown } from '@kbn/ui-actio
|
|||
import { APPLY_FILTER_TRIGGER } from '@kbn/data-plugin/public';
|
||||
import { ApplyGlobalFilterActionContext } from '@kbn/unified-search-plugin/public';
|
||||
import { StartDependencies as Start } from '../../plugin';
|
||||
import { ActionContext, Config, CollectConfigProps } from './types';
|
||||
import type { ActionApi, ActionContext, Config, CollectConfigProps } from './types';
|
||||
import { CollectConfigContainer } from './collect_config_container';
|
||||
import { SAMPLE_DASHBOARD_TO_DISCOVER_DRILLDOWN } from './constants';
|
||||
import { txtGoToDiscover } from './i18n';
|
||||
|
||||
const isOutputWithIndexPatterns = (
|
||||
output: unknown
|
||||
): output is { indexPatterns: Array<{ id: string }> } => {
|
||||
if (!output || typeof output !== 'object') return false;
|
||||
return Array.isArray((output as any).indexPatterns);
|
||||
};
|
||||
|
||||
export interface Params {
|
||||
start: StartServicesGetter<Pick<Start, 'data' | 'discover'>>;
|
||||
}
|
||||
|
@ -67,10 +60,10 @@ export class DashboardToDiscoverDrilldown
|
|||
let indexPatternId =
|
||||
!!config.customIndexPattern && !!config.indexPatternId ? config.indexPatternId : '';
|
||||
|
||||
if (!indexPatternId && !!context.embeddable) {
|
||||
const output = context.embeddable!.getOutput();
|
||||
if (isOutputWithIndexPatterns(output) && output.indexPatterns.length > 0) {
|
||||
indexPatternId = output.indexPatterns[0].id;
|
||||
if (!indexPatternId) {
|
||||
const dataViews = (context?.embeddable as ActionApi).dataViews?.value;
|
||||
if (dataViews?.[0].id) {
|
||||
indexPatternId = dataViews[0].id;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { PublishesDataViews } from '@kbn/presentation-publishing';
|
||||
import { CollectConfigProps as CollectConfigPropsBase } from '@kbn/kibana-utils-plugin/public';
|
||||
import { ApplyGlobalFilterActionContext } from '@kbn/unified-search-plugin/public';
|
||||
import { IEmbeddable } from '@kbn/embeddable-plugin/public';
|
||||
|
||||
export type ActionContext = ApplyGlobalFilterActionContext & { embeddable: IEmbeddable };
|
||||
export type ActionApi = Partial<PublishesDataViews>;
|
||||
|
||||
export type ActionContext = ApplyGlobalFilterActionContext & { embeddable: ActionApi };
|
||||
|
||||
export type Config = {
|
||||
/**
|
||||
|
|
|
@ -29,5 +29,6 @@
|
|||
"@kbn/i18n",
|
||||
"@kbn/unified-search-plugin",
|
||||
"@kbn/utility-types",
|
||||
"@kbn/presentation-publishing",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
*/
|
||||
|
||||
import { DatatableColumnType } from '@kbn/expressions-plugin/common';
|
||||
import type { Query, Filter, TimeRange } from '@kbn/es-query';
|
||||
import { Embeddable, EmbeddableInput, EmbeddableOutput } from '@kbn/embeddable-plugin/public';
|
||||
|
||||
export const createPoint = ({
|
||||
field,
|
||||
|
@ -154,21 +152,3 @@ export const rowClickData = {
|
|||
'e0719f1a-04fb-4036-a63c-c25deac3f011',
|
||||
],
|
||||
};
|
||||
|
||||
interface TestInput extends EmbeddableInput {
|
||||
savedObjectId?: string;
|
||||
query?: Query;
|
||||
filters?: Filter[];
|
||||
timeRange?: TimeRange;
|
||||
}
|
||||
|
||||
interface TestOutput extends EmbeddableOutput {
|
||||
indexPatterns?: Array<{ id: string }>;
|
||||
}
|
||||
|
||||
export class TestEmbeddable extends Embeddable<TestInput, TestOutput> {
|
||||
type = 'test';
|
||||
|
||||
destroy() {}
|
||||
reload() {}
|
||||
}
|
||||
|
|
|
@ -5,17 +5,18 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { IExternalUrl } from '@kbn/core/public';
|
||||
import { UrlDrilldown, ActionContext, Config } from './url_drilldown';
|
||||
import { UrlDrilldown, Config } from './url_drilldown';
|
||||
import {
|
||||
IEmbeddable,
|
||||
ValueClickContext,
|
||||
VALUE_CLICK_TRIGGER,
|
||||
SELECT_RANGE_TRIGGER,
|
||||
CONTEXT_MENU_TRIGGER,
|
||||
} from '@kbn/embeddable-plugin/public';
|
||||
import { DatatableColumnType } from '@kbn/expressions-plugin/common';
|
||||
import { of } from '@kbn/kibana-utils-plugin/common';
|
||||
import { createPoint, rowClickData, TestEmbeddable } from './test/data';
|
||||
import { createPoint, rowClickData } from './test/data';
|
||||
import { ROW_CLICK_TRIGGER } from '@kbn/ui-actions-plugin/public';
|
||||
import { settingsServiceMock } from '@kbn/core-ui-settings-browser-mocks';
|
||||
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
|
||||
|
@ -55,14 +56,13 @@ const mockDataPoints = [
|
|||
},
|
||||
];
|
||||
|
||||
const mockEmbeddable = {
|
||||
getInput: () => ({
|
||||
filters: [],
|
||||
timeRange: { from: 'now-15m', to: 'now' },
|
||||
query: { query: 'test', language: 'kuery' },
|
||||
}),
|
||||
getOutput: () => ({}),
|
||||
} as unknown as IEmbeddable;
|
||||
const mockEmbeddableApi = {
|
||||
parentApi: {
|
||||
localFilters: new BehaviorSubject([]),
|
||||
localQuery: new BehaviorSubject({ query: 'test', language: 'kuery' }),
|
||||
localTimeRange: new BehaviorSubject({ from: 'now-15m', to: 'now' }),
|
||||
},
|
||||
};
|
||||
|
||||
const mockNavigateToUrl = jest.fn(() => Promise.resolve());
|
||||
|
||||
|
@ -110,7 +110,7 @@ describe('UrlDrilldown', () => {
|
|||
encodeUrl: true,
|
||||
};
|
||||
|
||||
const context: ActionContext = {
|
||||
const context: ValueClickContext = {
|
||||
data: {
|
||||
data: mockDataPoints,
|
||||
},
|
||||
|
@ -128,11 +128,11 @@ describe('UrlDrilldown', () => {
|
|||
encodeUrl: true,
|
||||
};
|
||||
|
||||
const context: ActionContext = {
|
||||
const context: ValueClickContext = {
|
||||
data: {
|
||||
data: mockDataPoints,
|
||||
},
|
||||
embeddable: mockEmbeddable,
|
||||
embeddable: mockEmbeddableApi,
|
||||
};
|
||||
|
||||
const result = urlDrilldown.isCompatible(config, context);
|
||||
|
@ -148,11 +148,11 @@ describe('UrlDrilldown', () => {
|
|||
encodeUrl: true,
|
||||
};
|
||||
|
||||
const context: ActionContext = {
|
||||
const context: ValueClickContext = {
|
||||
data: {
|
||||
data: mockDataPoints,
|
||||
},
|
||||
embeddable: mockEmbeddable,
|
||||
embeddable: mockEmbeddableApi,
|
||||
};
|
||||
|
||||
await expect(urlDrilldown.isCompatible(config, context)).resolves.toBe(false);
|
||||
|
@ -169,11 +169,11 @@ describe('UrlDrilldown', () => {
|
|||
encodeUrl: true,
|
||||
};
|
||||
|
||||
const context: ActionContext = {
|
||||
const context: ValueClickContext = {
|
||||
data: {
|
||||
data: mockDataPoints,
|
||||
},
|
||||
embeddable: mockEmbeddable,
|
||||
embeddable: mockEmbeddableApi,
|
||||
};
|
||||
|
||||
const result1 = await drilldown1.isCompatible(config, context);
|
||||
|
@ -198,11 +198,11 @@ describe('UrlDrilldown', () => {
|
|||
encodeUrl: true,
|
||||
};
|
||||
|
||||
const context: ActionContext = {
|
||||
const context: ValueClickContext = {
|
||||
data: {
|
||||
data: mockDataPoints,
|
||||
},
|
||||
embeddable: mockEmbeddable,
|
||||
embeddable: mockEmbeddableApi,
|
||||
};
|
||||
|
||||
const url = await urlDrilldown.getHref(config, context);
|
||||
|
@ -221,11 +221,11 @@ describe('UrlDrilldown', () => {
|
|||
encodeUrl: true,
|
||||
};
|
||||
|
||||
const context: ActionContext = {
|
||||
const context: ValueClickContext = {
|
||||
data: {
|
||||
data: mockDataPoints,
|
||||
},
|
||||
embeddable: mockEmbeddable,
|
||||
embeddable: mockEmbeddableApi,
|
||||
};
|
||||
|
||||
await expect(urlDrilldown.getHref(config, context)).rejects.toThrowError();
|
||||
|
@ -244,11 +244,11 @@ describe('UrlDrilldown', () => {
|
|||
encodeUrl: true,
|
||||
};
|
||||
|
||||
const context: ActionContext = {
|
||||
const context: ValueClickContext = {
|
||||
data: {
|
||||
data: mockDataPoints,
|
||||
},
|
||||
embeddable: mockEmbeddable,
|
||||
embeddable: mockEmbeddableApi,
|
||||
};
|
||||
|
||||
const url = await drilldown1.getHref(config, context);
|
||||
|
@ -272,16 +272,12 @@ describe('UrlDrilldown', () => {
|
|||
});
|
||||
|
||||
describe('variables', () => {
|
||||
const embeddable1 = new TestEmbeddable(
|
||||
{
|
||||
id: 'test',
|
||||
title: 'The Title',
|
||||
savedObjectId: 'SAVED_OBJECT_IDxx',
|
||||
},
|
||||
{
|
||||
indexPatterns: [{ id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' }],
|
||||
}
|
||||
);
|
||||
const embeddable1 = {
|
||||
dataViews: new BehaviorSubject([{ id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' }]),
|
||||
panelTitle: new BehaviorSubject('The Title'),
|
||||
savedObjectId: new BehaviorSubject('SAVED_OBJECT_IDxx'),
|
||||
uuid: 'test',
|
||||
};
|
||||
const data = {
|
||||
data: [
|
||||
createPoint({ field: 'field0', value: 'value0' }),
|
||||
|
@ -290,18 +286,13 @@ describe('UrlDrilldown', () => {
|
|||
],
|
||||
};
|
||||
|
||||
const embeddable2 = new TestEmbeddable(
|
||||
{
|
||||
id: 'the-id',
|
||||
query: {
|
||||
language: 'C++',
|
||||
query: 'std::cout << 123;',
|
||||
},
|
||||
timeRange: {
|
||||
from: 'FROM',
|
||||
to: 'TO',
|
||||
},
|
||||
filters: [
|
||||
const embeddable2 = {
|
||||
dataViews: new BehaviorSubject([
|
||||
{ id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' },
|
||||
{ id: 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' },
|
||||
]),
|
||||
parentApi: {
|
||||
localFilters: new BehaviorSubject([
|
||||
{
|
||||
meta: {
|
||||
alias: 'asdf',
|
||||
|
@ -309,17 +300,17 @@ describe('UrlDrilldown', () => {
|
|||
negate: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
savedObjectId: 'SAVED_OBJECT_ID',
|
||||
]),
|
||||
localQuery: new BehaviorSubject({
|
||||
language: 'C++',
|
||||
query: 'std::cout << 123;',
|
||||
}),
|
||||
localTimeRange: new BehaviorSubject({ from: 'FROM', to: 'TO' }),
|
||||
},
|
||||
{
|
||||
title: 'The Title',
|
||||
indexPatterns: [
|
||||
{ id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' },
|
||||
{ id: 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' },
|
||||
],
|
||||
}
|
||||
);
|
||||
panelTitle: new BehaviorSubject('The Title'),
|
||||
savedObjectId: new BehaviorSubject('SAVED_OBJECT_ID'),
|
||||
uuid: 'the-id',
|
||||
};
|
||||
|
||||
describe('getRuntimeVariables()', () => {
|
||||
test('builds runtime variables for VALUE_CLICK_TRIGGER trigger', () => {
|
||||
|
@ -497,11 +488,11 @@ describe('UrlDrilldown', () => {
|
|||
|
||||
describe('encoding', () => {
|
||||
const urlDrilldown = createDrilldown();
|
||||
const context: ActionContext = {
|
||||
const context: ValueClickContext = {
|
||||
data: {
|
||||
data: mockDataPoints,
|
||||
},
|
||||
embeddable: mockEmbeddable,
|
||||
embeddable: mockEmbeddableApi,
|
||||
};
|
||||
|
||||
test('encodes URL by default', async () => {
|
||||
|
|
|
@ -7,17 +7,15 @@
|
|||
|
||||
import React from 'react';
|
||||
import { IExternalUrl, ThemeServiceStart } from '@kbn/core/public';
|
||||
import type { EmbeddableApiContext } from '@kbn/presentation-publishing';
|
||||
import {
|
||||
ChartActionContext,
|
||||
CONTEXT_MENU_TRIGGER,
|
||||
IEmbeddable,
|
||||
EmbeddableInput,
|
||||
SELECT_RANGE_TRIGGER,
|
||||
VALUE_CLICK_TRIGGER,
|
||||
} from '@kbn/embeddable-plugin/public';
|
||||
import { IMAGE_CLICK_TRIGGER } from '@kbn/image-embeddable-plugin/public';
|
||||
import { ActionExecutionContext, ROW_CLICK_TRIGGER } from '@kbn/ui-actions-plugin/public';
|
||||
import type { Query, Filter, TimeRange } from '@kbn/es-query';
|
||||
import type { CollectConfigProps as CollectConfigPropsBase } from '@kbn/kibana-utils-plugin/public';
|
||||
import { UrlTemplateEditorVariable, KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import {
|
||||
|
@ -36,15 +34,6 @@ import { getEventVariableList, getEventScopeValues } from './variables/event_var
|
|||
import { getContextVariableList, getContextScopeValues } from './variables/context_variables';
|
||||
import { getGlobalVariableList } from './variables/global_variables';
|
||||
|
||||
interface EmbeddableQueryInput extends EmbeddableInput {
|
||||
query?: Query;
|
||||
filters?: Filter[];
|
||||
timeRange?: TimeRange;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export type EmbeddableWithQueryInput = IEmbeddable<EmbeddableQueryInput>;
|
||||
|
||||
interface UrlDrilldownDeps {
|
||||
externalUrl: IExternalUrl;
|
||||
getGlobalScope: () => UrlDrilldownGlobalScope;
|
||||
|
@ -55,7 +44,6 @@ interface UrlDrilldownDeps {
|
|||
theme: () => ThemeServiceStart;
|
||||
}
|
||||
|
||||
export type ActionContext = ChartActionContext<EmbeddableWithQueryInput>;
|
||||
export type Config = UrlDrilldownConfig;
|
||||
export type UrlTrigger =
|
||||
| typeof VALUE_CLICK_TRIGGER
|
||||
|
@ -64,14 +52,13 @@ export type UrlTrigger =
|
|||
| typeof CONTEXT_MENU_TRIGGER
|
||||
| typeof IMAGE_CLICK_TRIGGER;
|
||||
|
||||
export interface ActionFactoryContext extends BaseActionFactoryContext {
|
||||
embeddable?: EmbeddableWithQueryInput;
|
||||
}
|
||||
export type ActionFactoryContext = Partial<EmbeddableApiContext> & BaseActionFactoryContext;
|
||||
|
||||
export type CollectConfigProps = CollectConfigPropsBase<Config, ActionFactoryContext>;
|
||||
|
||||
const URL_DRILLDOWN = 'URL_DRILLDOWN';
|
||||
|
||||
export class UrlDrilldown implements Drilldown<Config, ActionContext, ActionFactoryContext> {
|
||||
export class UrlDrilldown implements Drilldown<Config, ChartActionContext, ActionFactoryContext> {
|
||||
public readonly id = URL_DRILLDOWN;
|
||||
|
||||
constructor(private readonly deps: UrlDrilldownDeps) {}
|
||||
|
@ -85,7 +72,7 @@ export class UrlDrilldown implements Drilldown<Config, ActionContext, ActionFact
|
|||
|
||||
public readonly actionMenuItem: React.FC<{
|
||||
config: Omit<SerializedAction<UrlDrilldownConfig>, 'factoryId'>;
|
||||
context: ActionContext | ActionExecutionContext<ActionContext>;
|
||||
context: ChartActionContext | ActionExecutionContext<ChartActionContext>;
|
||||
}> = ({ config, context }) => {
|
||||
const [title, setTitle] = React.useState(config.name);
|
||||
React.useEffect(() => {
|
||||
|
@ -152,7 +139,7 @@ export class UrlDrilldown implements Drilldown<Config, ActionContext, ActionFact
|
|||
return !!config.url.template;
|
||||
};
|
||||
|
||||
public readonly isCompatible = async (config: Config, context: ActionContext) => {
|
||||
public readonly isCompatible = async (config: Config, context: ChartActionContext) => {
|
||||
const scope = this.getRuntimeVariables(context);
|
||||
const { isValid, error } = await urlDrilldownValidateUrlTemplate(config.url, scope);
|
||||
|
||||
|
@ -173,7 +160,7 @@ export class UrlDrilldown implements Drilldown<Config, ActionContext, ActionFact
|
|||
return true;
|
||||
};
|
||||
|
||||
private async buildUrl(config: Config, context: ActionContext): Promise<string> {
|
||||
private async buildUrl(config: Config, context: ChartActionContext): Promise<string> {
|
||||
const doEncode = config.encodeUrl ?? true;
|
||||
const url = await urlDrilldownCompileUrl(
|
||||
config.url.template,
|
||||
|
@ -183,7 +170,10 @@ export class UrlDrilldown implements Drilldown<Config, ActionContext, ActionFact
|
|||
return url;
|
||||
}
|
||||
|
||||
public readonly getHref = async (config: Config, context: ActionContext): Promise<string> => {
|
||||
public readonly getHref = async (
|
||||
config: Config,
|
||||
context: ChartActionContext
|
||||
): Promise<string> => {
|
||||
const url = await this.buildUrl(config, context);
|
||||
const validUrl = this.deps.externalUrl.validateUrl(url);
|
||||
if (!validUrl) {
|
||||
|
@ -195,7 +185,7 @@ export class UrlDrilldown implements Drilldown<Config, ActionContext, ActionFact
|
|||
return url;
|
||||
};
|
||||
|
||||
public readonly execute = async (config: Config, context: ActionContext) => {
|
||||
public readonly execute = async (config: Config, context: ChartActionContext) => {
|
||||
const url = await this.getHref(config, context);
|
||||
if (config.openInNewTab) {
|
||||
window.open(url, '_blank', 'noopener');
|
||||
|
@ -204,7 +194,7 @@ export class UrlDrilldown implements Drilldown<Config, ActionContext, ActionFact
|
|||
}
|
||||
};
|
||||
|
||||
public readonly getRuntimeVariables = (context: ActionContext) => {
|
||||
public readonly getRuntimeVariables = (context: ChartActionContext) => {
|
||||
return {
|
||||
event: getEventScopeValues(context),
|
||||
context: getContextScopeValues(context),
|
||||
|
|
|
@ -5,134 +5,21 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { getContextScopeValues } from './context_variables';
|
||||
import { TestEmbeddable } from '../test/data';
|
||||
|
||||
describe('getContextScopeValues()', () => {
|
||||
test('returns only ID for empty embeddable', () => {
|
||||
const embeddable = new TestEmbeddable(
|
||||
{
|
||||
id: 'test',
|
||||
},
|
||||
{}
|
||||
);
|
||||
const vars = getContextScopeValues({ embeddable });
|
||||
|
||||
expect(vars).toEqual({
|
||||
panel: {
|
||||
id: 'test',
|
||||
},
|
||||
test('excludes undefined values', () => {
|
||||
const embeddableApi = {};
|
||||
expect(getContextScopeValues({ embeddable: embeddableApi })).toEqual({
|
||||
panel: {},
|
||||
});
|
||||
});
|
||||
|
||||
test('returns title as specified in input', () => {
|
||||
const embeddable = new TestEmbeddable(
|
||||
{
|
||||
id: 'test',
|
||||
title: 'title1',
|
||||
},
|
||||
{}
|
||||
);
|
||||
const vars = getContextScopeValues({ embeddable });
|
||||
|
||||
expect(vars).toEqual({
|
||||
panel: {
|
||||
id: 'test',
|
||||
title: 'title1',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('returns output title if input and output titles are specified', () => {
|
||||
const embeddable = new TestEmbeddable(
|
||||
{
|
||||
id: 'test',
|
||||
title: 'title1',
|
||||
},
|
||||
{
|
||||
title: 'title2',
|
||||
}
|
||||
);
|
||||
const vars = getContextScopeValues({ embeddable });
|
||||
|
||||
expect(vars).toEqual({
|
||||
panel: {
|
||||
id: 'test',
|
||||
title: 'title2',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('returns title from output if title in input is missing', () => {
|
||||
const embeddable = new TestEmbeddable(
|
||||
{
|
||||
id: 'test',
|
||||
},
|
||||
{
|
||||
title: 'title2',
|
||||
}
|
||||
);
|
||||
const vars = getContextScopeValues({ embeddable });
|
||||
|
||||
expect(vars).toEqual({
|
||||
panel: {
|
||||
id: 'test',
|
||||
title: 'title2',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('returns saved object ID from output', () => {
|
||||
const embeddable = new TestEmbeddable(
|
||||
{
|
||||
id: 'test',
|
||||
savedObjectId: '5678',
|
||||
},
|
||||
{
|
||||
savedObjectId: '1234',
|
||||
}
|
||||
);
|
||||
const vars = getContextScopeValues({ embeddable });
|
||||
|
||||
expect(vars).toEqual({
|
||||
panel: {
|
||||
id: 'test',
|
||||
savedObjectId: '1234',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('returns saved object ID from input if it is not set on output', () => {
|
||||
const embeddable = new TestEmbeddable(
|
||||
{
|
||||
id: 'test',
|
||||
savedObjectId: '5678',
|
||||
},
|
||||
{}
|
||||
);
|
||||
const vars = getContextScopeValues({ embeddable });
|
||||
|
||||
expect(vars).toEqual({
|
||||
panel: {
|
||||
id: 'test',
|
||||
savedObjectId: '5678',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('returns query, timeRange and filters from input', () => {
|
||||
const embeddable = new TestEmbeddable(
|
||||
{
|
||||
id: 'test',
|
||||
query: {
|
||||
language: 'C++',
|
||||
query: 'std::cout << 123;',
|
||||
},
|
||||
timeRange: {
|
||||
from: 'FROM',
|
||||
to: 'TO',
|
||||
},
|
||||
filters: [
|
||||
test('returns values when provided', () => {
|
||||
const embeddableApi = {
|
||||
parentApi: {
|
||||
localFilters: new BehaviorSubject([
|
||||
{
|
||||
meta: {
|
||||
alias: 'asdf',
|
||||
|
@ -140,13 +27,18 @@ describe('getContextScopeValues()', () => {
|
|||
negate: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
]),
|
||||
localQuery: new BehaviorSubject({
|
||||
language: 'C++',
|
||||
query: 'std::cout << 123;',
|
||||
}),
|
||||
localTimeRange: new BehaviorSubject({ from: 'FROM', to: 'TO' }),
|
||||
},
|
||||
{}
|
||||
);
|
||||
const vars = getContextScopeValues({ embeddable });
|
||||
|
||||
expect(vars).toEqual({
|
||||
panelTitle: new BehaviorSubject('title1'),
|
||||
savedObjectId: new BehaviorSubject('1234'),
|
||||
uuid: 'test',
|
||||
};
|
||||
expect(getContextScopeValues({ embeddable: embeddableApi })).toEqual({
|
||||
panel: {
|
||||
id: 'test',
|
||||
query: {
|
||||
|
@ -166,46 +58,32 @@ describe('getContextScopeValues()', () => {
|
|||
},
|
||||
},
|
||||
],
|
||||
savedObjectId: '1234',
|
||||
title: 'title1',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('returns a single index pattern from output', () => {
|
||||
const embeddable = new TestEmbeddable(
|
||||
{
|
||||
id: 'test',
|
||||
},
|
||||
{
|
||||
indexPatterns: [{ id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' }],
|
||||
}
|
||||
);
|
||||
const vars = getContextScopeValues({ embeddable });
|
||||
|
||||
expect(vars).toEqual({
|
||||
const embeddableApi = {
|
||||
dataViews: new BehaviorSubject([{ id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' }]),
|
||||
};
|
||||
expect(getContextScopeValues({ embeddable: embeddableApi })).toEqual({
|
||||
panel: {
|
||||
id: 'test',
|
||||
indexPatternId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('returns multiple index patterns from output', () => {
|
||||
const embeddable = new TestEmbeddable(
|
||||
{
|
||||
id: 'test',
|
||||
},
|
||||
{
|
||||
indexPatterns: [
|
||||
{ id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' },
|
||||
{ id: 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' },
|
||||
],
|
||||
}
|
||||
);
|
||||
const vars = getContextScopeValues({ embeddable });
|
||||
|
||||
expect(vars).toEqual({
|
||||
const embeddableApi = {
|
||||
dataViews: new BehaviorSubject([
|
||||
{ id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' },
|
||||
{ id: 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' },
|
||||
]),
|
||||
};
|
||||
expect(getContextScopeValues({ embeddable: embeddableApi })).toEqual({
|
||||
panel: {
|
||||
id: 'test',
|
||||
indexPatternIds: [
|
||||
'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy',
|
||||
|
|
|
@ -8,26 +8,33 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { monaco } from '@kbn/monaco';
|
||||
import { getFlattenedObject } from '@kbn/std';
|
||||
import type { Filter, Query, TimeRange } from '@kbn/es-query';
|
||||
import { EmbeddableInput, EmbeddableOutput } from '@kbn/embeddable-plugin/public';
|
||||
import type { Filter, AggregateQuery, Query, TimeRange } from '@kbn/es-query';
|
||||
import type {
|
||||
EmbeddableApiContext,
|
||||
HasParentApi,
|
||||
HasUniqueId,
|
||||
PublishesPanelTitle,
|
||||
PublishesSavedObjectId,
|
||||
PublishesLocalUnifiedSearch,
|
||||
PublishesDataViews,
|
||||
} from '@kbn/presentation-publishing';
|
||||
import type { UrlTemplateEditorVariable } from '@kbn/kibana-react-plugin/public';
|
||||
import { txtValue } from './i18n';
|
||||
import type { EmbeddableWithQueryInput } from '../url_drilldown';
|
||||
import { deleteUndefinedKeys } from './util';
|
||||
import type { ActionFactoryContext } from '../url_drilldown';
|
||||
|
||||
/**
|
||||
* Part of context scope extracted from an embeddable
|
||||
* Part of context scope extracted from an api
|
||||
* Expose on the scope as: `{{context.panel.id}}`, `{{context.panel.filters.[0]}}`
|
||||
*/
|
||||
interface PanelValues extends EmbeddableInput {
|
||||
interface PanelValues {
|
||||
/**
|
||||
* ID of the embeddable panel.
|
||||
* ID of the api panel.
|
||||
*/
|
||||
id: string;
|
||||
id?: string;
|
||||
|
||||
/**
|
||||
* Title of the embeddable panel.
|
||||
* Title of the api panel.
|
||||
*/
|
||||
title?: string;
|
||||
|
||||
|
@ -41,7 +48,7 @@ interface PanelValues extends EmbeddableInput {
|
|||
*/
|
||||
indexPatternIds?: string[];
|
||||
|
||||
query?: Query;
|
||||
query?: Query | AggregateQuery;
|
||||
filters?: Filter[];
|
||||
timeRange?: TimeRange;
|
||||
savedObjectId?: string;
|
||||
|
@ -51,65 +58,34 @@ export interface ContextValues {
|
|||
panel: PanelValues;
|
||||
}
|
||||
|
||||
function hasSavedObjectId(obj: Record<string, unknown>): obj is { savedObjectId: string } {
|
||||
return 'savedObjectId' in obj && typeof obj.savedObjectId === 'string';
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Same functionality is implemented in x-pack/plugins/discover_enhanced/public/actions/explore_data/shared.ts,
|
||||
* combine both implementations into a common approach.
|
||||
*/
|
||||
function getIndexPatternIds(output: EmbeddableOutput): string[] {
|
||||
function hasIndexPatterns(
|
||||
_output: unknown
|
||||
): _output is { indexPatterns: Array<{ id?: string }> } {
|
||||
return (
|
||||
typeof _output === 'object' &&
|
||||
!!_output &&
|
||||
Array.isArray((_output as { indexPatterns: unknown[] }).indexPatterns) &&
|
||||
(_output as { indexPatterns: Array<{ id?: string }> }).indexPatterns.length > 0
|
||||
);
|
||||
}
|
||||
return hasIndexPatterns(output)
|
||||
? (output.indexPatterns.map((ip) => ip.id).filter(Boolean) as string[])
|
||||
: [];
|
||||
}
|
||||
|
||||
export function getEmbeddableVariables(embeddable: EmbeddableWithQueryInput): PanelValues {
|
||||
const input = embeddable.getInput();
|
||||
const output = embeddable.getOutput();
|
||||
const indexPatternsIds = getIndexPatternIds(output);
|
||||
|
||||
return deleteUndefinedKeys({
|
||||
id: input.id,
|
||||
title: output.title ?? input.title,
|
||||
savedObjectId:
|
||||
output.savedObjectId ?? (hasSavedObjectId(input) ? input.savedObjectId : undefined),
|
||||
query: input.query,
|
||||
timeRange: input.timeRange,
|
||||
filters: input.filters,
|
||||
indexPatternIds: indexPatternsIds.length > 1 ? indexPatternsIds : undefined,
|
||||
indexPatternId: indexPatternsIds.length === 1 ? indexPatternsIds[0] : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
const getContextPanelScopeValues = (contextScopeInput: unknown): PanelValues => {
|
||||
function hasEmbeddable(val: unknown): val is { embeddable: EmbeddableWithQueryInput } {
|
||||
if (val && typeof val === 'object' && 'embeddable' in val) return true;
|
||||
return false;
|
||||
}
|
||||
if (!hasEmbeddable(contextScopeInput))
|
||||
export const getContextScopeValues = (context: Partial<EmbeddableApiContext>): ContextValues => {
|
||||
if (!context.embeddable)
|
||||
throw new Error(
|
||||
"UrlDrilldown [getContextScope] can't build scope because embeddable object is missing in context"
|
||||
);
|
||||
const embeddable = contextScopeInput.embeddable;
|
||||
const api = context.embeddable as Partial<
|
||||
HasUniqueId &
|
||||
PublishesPanelTitle &
|
||||
PublishesSavedObjectId &
|
||||
PublishesLocalUnifiedSearch &
|
||||
PublishesDataViews &
|
||||
HasParentApi<Partial<PublishesLocalUnifiedSearch>>
|
||||
>;
|
||||
const dataViewIds = api.dataViews?.value
|
||||
? (api.dataViews?.value.map((dataView) => dataView.id).filter(Boolean) as string[])
|
||||
: [];
|
||||
|
||||
return getEmbeddableVariables(embeddable);
|
||||
};
|
||||
|
||||
export const getContextScopeValues = (contextScopeInput: unknown): ContextValues => {
|
||||
return {
|
||||
panel: getContextPanelScopeValues(contextScopeInput),
|
||||
panel: deleteUndefinedKeys({
|
||||
id: api.uuid,
|
||||
title: api.panelTitle?.value ?? api.defaultPanelTitle?.value,
|
||||
savedObjectId: api.savedObjectId?.value,
|
||||
query: api.parentApi?.localQuery?.value,
|
||||
timeRange: api.localTimeRange?.value ?? api.parentApi?.localTimeRange?.value,
|
||||
filters: api.parentApi?.localFilters?.value,
|
||||
indexPatternIds: dataViewIds.length > 1 ? dataViewIds : undefined,
|
||||
indexPatternId: dataViewIds.length === 1 ? dataViewIds[0] : undefined,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { monaco } from '@kbn/monaco';
|
||||
import type { PublishesPanelTitle } from '@kbn/presentation-publishing';
|
||||
import {
|
||||
ChartActionContext,
|
||||
isRangeSelectTriggerContext,
|
||||
isValueClickTriggerContext,
|
||||
isRowClickTriggerContext,
|
||||
|
@ -19,11 +21,7 @@ import {
|
|||
} from '@kbn/embeddable-plugin/public';
|
||||
import { RowClickContext, ROW_CLICK_TRIGGER } from '@kbn/ui-actions-plugin/public';
|
||||
import type { UrlTemplateEditorVariable } from '@kbn/kibana-react-plugin/public';
|
||||
import type {
|
||||
ActionContext,
|
||||
ActionFactoryContext,
|
||||
EmbeddableWithQueryInput,
|
||||
} from '../url_drilldown';
|
||||
import type { ActionFactoryContext } from '../url_drilldown';
|
||||
import { deleteUndefinedKeys, toPrimitiveOrUndefined, Primitive } from './util';
|
||||
|
||||
/**
|
||||
|
@ -35,8 +33,6 @@ export type UrlDrilldownEventScope =
|
|||
| RowClickTriggerEventScope
|
||||
| ContextMenuTriggerEventScope;
|
||||
|
||||
export type EventScopeInput = ActionContext;
|
||||
|
||||
export interface ValueClickTriggerEventScope {
|
||||
key?: string;
|
||||
value: Primitive;
|
||||
|
@ -95,7 +91,7 @@ const getEventScopeFromRowClickTriggerContext = (
|
|||
ctx: RowClickContext
|
||||
): RowClickTriggerEventScope => {
|
||||
const { data } = ctx;
|
||||
const embeddable = ctx.embeddable as EmbeddableWithQueryInput;
|
||||
const api = ctx.embeddable as Partial<PublishesPanelTitle>;
|
||||
|
||||
const { rowIndex } = data;
|
||||
const columns = data.columns || data.table.columns.map(({ id }) => id);
|
||||
|
@ -109,7 +105,7 @@ const getEventScopeFromRowClickTriggerContext = (
|
|||
if (!column) {
|
||||
// This should never happe, but in case it does we log data necessary for debugging.
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(data, embeddable ? `Embeddable [${embeddable.getTitle()}]` : null);
|
||||
console.error(data, api?.panelTitle ? `Embeddable [${api.panelTitle.value}]` : null);
|
||||
throw new Error('Could not find a datatable column.');
|
||||
}
|
||||
values.push(row[columnId]);
|
||||
|
@ -127,14 +123,14 @@ const getEventScopeFromRowClickTriggerContext = (
|
|||
return scope;
|
||||
};
|
||||
|
||||
export const getEventScopeValues = (eventScopeInput: EventScopeInput): UrlDrilldownEventScope => {
|
||||
if (isRangeSelectTriggerContext(eventScopeInput)) {
|
||||
return getEventScopeFromRangeSelectTriggerContext(eventScopeInput);
|
||||
} else if (isValueClickTriggerContext(eventScopeInput)) {
|
||||
return getEventScopeFromValueClickTriggerContext(eventScopeInput);
|
||||
} else if (isRowClickTriggerContext(eventScopeInput)) {
|
||||
return getEventScopeFromRowClickTriggerContext(eventScopeInput);
|
||||
} else if (isContextMenuTriggerContext(eventScopeInput)) {
|
||||
export const getEventScopeValues = (context: ChartActionContext): UrlDrilldownEventScope => {
|
||||
if (isRangeSelectTriggerContext(context)) {
|
||||
return getEventScopeFromRangeSelectTriggerContext(context);
|
||||
} else if (isValueClickTriggerContext(context)) {
|
||||
return getEventScopeFromValueClickTriggerContext(context);
|
||||
} else if (isRowClickTriggerContext(context)) {
|
||||
return getEventScopeFromRowClickTriggerContext(context);
|
||||
} else if (isContextMenuTriggerContext(context)) {
|
||||
return {};
|
||||
} else {
|
||||
throw new Error("UrlDrilldown [getEventScope] can't build scope from not supported trigger");
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
"@kbn/image-embeddable-plugin",
|
||||
"@kbn/core-ui-settings-browser-mocks",
|
||||
"@kbn/core-ui-settings-browser",
|
||||
"@kbn/core-theme-browser-mocks"
|
||||
"@kbn/core-theme-browser-mocks",
|
||||
"@kbn/presentation-publishing"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { CellValueContext } from '@kbn/embeddable-plugin/public';
|
||||
import type { CellValueContext, IEmbeddable } from '@kbn/embeddable-plugin/public';
|
||||
import { isErrorEmbeddable, isFilterableEmbeddable } from '@kbn/embeddable-plugin/public';
|
||||
import { createAction } from '@kbn/ui-actions-plugin/public';
|
||||
import { KibanaServices } from '../../../common/lib/kibana';
|
||||
|
@ -82,9 +82,9 @@ export const createAddToTimelineLensAction = ({
|
|||
getIconType: () => ADD_TO_TIMELINE_ICON,
|
||||
getDisplayName: () => ADD_TO_TIMELINE,
|
||||
isCompatible: async ({ embeddable, data }) =>
|
||||
!isErrorEmbeddable(embeddable) &&
|
||||
isLensEmbeddable(embeddable) &&
|
||||
isFilterableEmbeddable(embeddable) &&
|
||||
!isErrorEmbeddable(embeddable as IEmbeddable) &&
|
||||
isLensEmbeddable(embeddable as IEmbeddable) &&
|
||||
isFilterableEmbeddable(embeddable as IEmbeddable) &&
|
||||
isDataColumnsFilterable(data) &&
|
||||
isInSecurityApp(currentAppId),
|
||||
execute: async ({ data }) => {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { CellValueContext } from '@kbn/embeddable-plugin/public';
|
||||
import type { CellValueContext, IEmbeddable } from '@kbn/embeddable-plugin/public';
|
||||
import { isErrorEmbeddable } from '@kbn/embeddable-plugin/public';
|
||||
import { createAction } from '@kbn/ui-actions-plugin/public';
|
||||
import copy from 'copy-to-clipboard';
|
||||
|
@ -37,8 +37,8 @@ export const createCopyToClipboardLensAction = ({ order }: { order?: number }) =
|
|||
getIconType: () => COPY_TO_CLIPBOARD_ICON,
|
||||
getDisplayName: () => COPY_TO_CLIPBOARD,
|
||||
isCompatible: async ({ embeddable, data }) =>
|
||||
!isErrorEmbeddable(embeddable) &&
|
||||
isLensEmbeddable(embeddable) &&
|
||||
!isErrorEmbeddable(embeddable as IEmbeddable) &&
|
||||
isLensEmbeddable(embeddable as IEmbeddable) &&
|
||||
isDataColumnsValid(data) &&
|
||||
isInSecurityApp(currentAppId),
|
||||
execute: async ({ data }) => {
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
filterOutNullableValues,
|
||||
} from '@kbn/cell-actions/src/actions/utils';
|
||||
import { isErrorEmbeddable } from '@kbn/embeddable-plugin/public';
|
||||
import type { CellValueContext } from '@kbn/embeddable-plugin/public';
|
||||
import type { CellValueContext, IEmbeddable } from '@kbn/embeddable-plugin/public';
|
||||
import { createAction } from '@kbn/ui-actions-plugin/public';
|
||||
import { ACTION_INCOMPATIBLE_VALUE_WARNING } from '@kbn/cell-actions/src/actions/translations';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
@ -66,8 +66,8 @@ export const createFilterLensAction = ({
|
|||
}),
|
||||
type: DefaultCellActionTypes.FILTER,
|
||||
isCompatible: async ({ embeddable, data }) =>
|
||||
!isErrorEmbeddable(embeddable) &&
|
||||
isLensEmbeddable(embeddable) &&
|
||||
!isErrorEmbeddable(embeddable as IEmbeddable) &&
|
||||
isLensEmbeddable(embeddable as IEmbeddable) &&
|
||||
isDataColumnsValid(data) &&
|
||||
isInSecurityApp(currentAppId),
|
||||
execute: async ({ data }) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue