mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Lens] Make open in discover drilldown work (#131237)
* make open in discover drilldown work * cleanup and tests * fix test * fix icon * fix type * fix open in new tab * fix open in new tab * fix test * make it possible to filter out drilldowns from list based on context * review comments * remove isConfigurable from the actionfactory Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Stratoula Kalafateli <efstratia.kalafateli@elastic.co>
This commit is contained in:
parent
58f480b909
commit
a8017dffd4
12 changed files with 328 additions and 24 deletions
|
@ -16,6 +16,7 @@
|
|||
"visualizations",
|
||||
"dashboard",
|
||||
"uiActions",
|
||||
"uiActionsEnhanced",
|
||||
"embeddable",
|
||||
"share",
|
||||
"presentationUtil",
|
||||
|
|
|
@ -44,6 +44,7 @@ import { VISUALIZE_EDITOR_TRIGGER } from '@kbn/visualizations-plugin/public';
|
|||
import { createStartServicesGetter } from '@kbn/kibana-utils-plugin/public';
|
||||
import type { DiscoverSetup, DiscoverStart } from '@kbn/discover-plugin/public';
|
||||
import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
|
||||
import { AdvancedUiActionsSetup } from '@kbn/ui-actions-enhanced-plugin/public';
|
||||
import type { EditorFrameService as EditorFrameServiceType } from './editor_frame_service';
|
||||
import type {
|
||||
IndexPatternDatasource as IndexPatternDatasourceType,
|
||||
|
@ -93,6 +94,7 @@ import type { SaveModalContainerProps } from './app_plugin/save_modal_container'
|
|||
|
||||
import { setupExpressions } from './expressions';
|
||||
import { getSearchProvider } from './search_provider';
|
||||
import { OpenInDiscoverDrilldown } from './trigger_actions/open_in_discover_drilldown';
|
||||
|
||||
export interface LensPluginSetupDependencies {
|
||||
urlForwarding: UrlForwardingSetup;
|
||||
|
@ -106,6 +108,7 @@ export interface LensPluginSetupDependencies {
|
|||
globalSearch?: GlobalSearchPluginSetup;
|
||||
usageCollection?: UsageCollectionSetup;
|
||||
discover?: DiscoverSetup;
|
||||
uiActionsEnhanced: AdvancedUiActionsSetup;
|
||||
}
|
||||
|
||||
export interface LensPluginStartDependencies {
|
||||
|
@ -224,6 +227,7 @@ export class LensPlugin {
|
|||
private heatmapVisualization: HeatmapVisualizationType | undefined;
|
||||
private gaugeVisualization: GaugeVisualizationType | undefined;
|
||||
private topNavMenuEntries: LensTopNavMenuEntryGenerator[] = [];
|
||||
private hasDiscoverAccess: boolean = false;
|
||||
|
||||
private stopReportManager?: () => void;
|
||||
|
||||
|
@ -240,6 +244,8 @@ export class LensPlugin {
|
|||
eventAnnotation,
|
||||
globalSearch,
|
||||
usageCollection,
|
||||
uiActionsEnhanced,
|
||||
discover,
|
||||
}: LensPluginSetupDependencies
|
||||
) {
|
||||
const startServices = createStartServicesGetter(core.getStartServices);
|
||||
|
@ -285,6 +291,15 @@ export class LensPlugin {
|
|||
|
||||
visualizations.registerAlias(getLensAliasConfig());
|
||||
|
||||
if (discover) {
|
||||
uiActionsEnhanced.registerDrilldown(
|
||||
new OpenInDiscoverDrilldown({
|
||||
discover,
|
||||
hasDiscoverAccess: () => this.hasDiscoverAccess,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
setupExpressions(
|
||||
expressions,
|
||||
() => startServices().plugins.fieldFormats.deserialize,
|
||||
|
@ -427,6 +442,7 @@ export class LensPlugin {
|
|||
}
|
||||
|
||||
start(core: CoreStart, startDependencies: LensPluginStartDependencies): LensPublicStart {
|
||||
this.hasDiscoverAccess = core.application.capabilities.discover.show as boolean;
|
||||
// unregisters the Visualize action and registers the lens one
|
||||
if (startDependencies.uiActions.hasAction(ACTION_VISUALIZE_FIELD)) {
|
||||
startDependencies.uiActions.unregisterAction(ACTION_VISUALIZE_FIELD);
|
||||
|
@ -443,10 +459,7 @@ export class LensPlugin {
|
|||
|
||||
startDependencies.uiActions.addTriggerAction(
|
||||
CONTEXT_MENU_TRIGGER,
|
||||
createOpenInDiscoverAction(
|
||||
startDependencies.discover!,
|
||||
core.application.capabilities.discover.show as boolean
|
||||
)
|
||||
createOpenInDiscoverAction(startDependencies.discover!, this.hasDiscoverAccess)
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
|
@ -83,6 +83,7 @@ describe('open in discover action', () => {
|
|||
|
||||
const embeddable = {
|
||||
getViewUnderlyingDataArgs: jest.fn(() => viewUnderlyingDataArgs),
|
||||
type: 'lens',
|
||||
};
|
||||
|
||||
const discoverUrl = 'https://discover-redirect-url';
|
||||
|
|
|
@ -5,17 +5,23 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { IEmbeddable } from '@kbn/embeddable-plugin/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { createAction } from '@kbn/ui-actions-plugin/public';
|
||||
import type { DiscoverStart } from '@kbn/discover-plugin/public';
|
||||
import type { Embeddable } from '../embeddable';
|
||||
import { DOC_TYPE } from '../../common';
|
||||
import { IEmbeddable } from '@kbn/embeddable-plugin/public';
|
||||
import { execute, isCompatible } from './open_in_discover_helpers';
|
||||
|
||||
const ACTION_OPEN_IN_DISCOVER = 'ACTION_OPEN_IN_DISCOVER';
|
||||
|
||||
export const createOpenInDiscoverAction = (discover: DiscoverStart, hasDiscoverAccess: boolean) =>
|
||||
createAction<{ embeddable: IEmbeddable }>({
|
||||
interface Context {
|
||||
embeddable: IEmbeddable;
|
||||
}
|
||||
|
||||
export const createOpenInDiscoverAction = (
|
||||
discover: Pick<DiscoverStart, 'locator'>,
|
||||
hasDiscoverAccess: boolean
|
||||
) =>
|
||||
createAction<Context>({
|
||||
type: ACTION_OPEN_IN_DISCOVER,
|
||||
id: ACTION_OPEN_IN_DISCOVER,
|
||||
order: 19, // right after Inspect which is 20
|
||||
|
@ -24,18 +30,10 @@ export const createOpenInDiscoverAction = (discover: DiscoverStart, hasDiscoverA
|
|||
i18n.translate('xpack.lens.app.exploreDataInDiscover', {
|
||||
defaultMessage: 'Explore data in Discover',
|
||||
}),
|
||||
isCompatible: async (context: { embeddable: IEmbeddable }) => {
|
||||
if (!hasDiscoverAccess) return false;
|
||||
return (
|
||||
context.embeddable.type === DOC_TYPE &&
|
||||
(await (context.embeddable as Embeddable).canViewUnderlyingData())
|
||||
);
|
||||
isCompatible: async (context: Context) => {
|
||||
return isCompatible({ hasDiscoverAccess, discover, embeddable: context.embeddable });
|
||||
},
|
||||
execute: async (context: { embeddable: Embeddable }) => {
|
||||
const args = context.embeddable.getViewUnderlyingDataArgs()!;
|
||||
const discoverUrl = discover.locator?.getRedirectUrl({
|
||||
...args,
|
||||
});
|
||||
window.open(discoverUrl, '_blank');
|
||||
execute: async (context: Context) => {
|
||||
return execute({ ...context, discover, hasDiscoverAccess });
|
||||
},
|
||||
});
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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, { FormEvent } from 'react';
|
||||
import { IEmbeddable, EmbeddableInput } from '@kbn/embeddable-plugin/public';
|
||||
import { DiscoverSetup } from '@kbn/discover-plugin/public';
|
||||
import { execute, isCompatible } from './open_in_discover_helpers';
|
||||
import { mount } from 'enzyme';
|
||||
import { Filter } from '@kbn/es-query';
|
||||
import {
|
||||
ActionFactoryContext,
|
||||
CollectConfigProps,
|
||||
OpenInDiscoverDrilldown,
|
||||
} from './open_in_discover_drilldown';
|
||||
|
||||
jest.mock('./open_in_discover_helpers', () => ({
|
||||
isCompatible: jest.fn(() => true),
|
||||
execute: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('open in discover drilldown', () => {
|
||||
let drilldown: OpenInDiscoverDrilldown;
|
||||
beforeEach(() => {
|
||||
drilldown = new OpenInDiscoverDrilldown({
|
||||
discover: {} as DiscoverSetup,
|
||||
hasDiscoverAccess: () => true,
|
||||
});
|
||||
});
|
||||
it('provides UI to edit config', () => {
|
||||
const Component = (drilldown as unknown as { ReactCollectConfig: React.FC<CollectConfigProps> })
|
||||
.ReactCollectConfig;
|
||||
const setConfig = jest.fn();
|
||||
const instance = mount(
|
||||
<Component
|
||||
config={{ openInNewTab: false }}
|
||||
onConfig={setConfig}
|
||||
context={{} as ActionFactoryContext}
|
||||
/>
|
||||
);
|
||||
instance.find('EuiSwitch').prop('onChange')!({} as unknown as FormEvent<{}>);
|
||||
expect(setConfig).toHaveBeenCalledWith({ openInNewTab: true });
|
||||
});
|
||||
it('calls through to isCompatible helper', () => {
|
||||
const filters: Filter[] = [{ meta: { disabled: false } }];
|
||||
drilldown.isCompatible(
|
||||
{ openInNewTab: true },
|
||||
{ embeddable: { type: 'lens' } as IEmbeddable<EmbeddableInput>, filters }
|
||||
);
|
||||
expect(isCompatible).toHaveBeenCalledWith(expect.objectContaining({ filters }));
|
||||
});
|
||||
it('calls through to execute helper', () => {
|
||||
const filters: Filter[] = [{ meta: { disabled: false } }];
|
||||
drilldown.execute(
|
||||
{ openInNewTab: true },
|
||||
{ embeddable: { type: 'lens' } as IEmbeddable<EmbeddableInput>, filters }
|
||||
);
|
||||
expect(execute).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ filters, openInSameTab: false })
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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 { IEmbeddable, EmbeddableInput } from '@kbn/embeddable-plugin/public';
|
||||
import {
|
||||
Query,
|
||||
Filter,
|
||||
TimeRange,
|
||||
extractTimeRange,
|
||||
APPLY_FILTER_TRIGGER,
|
||||
} from '@kbn/data-plugin/public';
|
||||
import { CollectConfigProps as CollectConfigPropsBase } from '@kbn/kibana-utils-plugin/public';
|
||||
import { reactToUiComponent } from '@kbn/kibana-react-plugin/public';
|
||||
import {
|
||||
UiActionsEnhancedDrilldownDefinition as Drilldown,
|
||||
UiActionsEnhancedBaseActionFactoryContext as BaseActionFactoryContext,
|
||||
} from '@kbn/ui-actions-enhanced-plugin/public';
|
||||
import { EuiFormRow, EuiSwitch } from '@elastic/eui';
|
||||
import { DiscoverSetup } from '@kbn/discover-plugin/public';
|
||||
import { ApplyGlobalFilterActionContext } from '@kbn/unified-search-plugin/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { execute, isCompatible, isLensEmbeddable } from './open_in_discover_helpers';
|
||||
|
||||
interface EmbeddableQueryInput extends EmbeddableInput {
|
||||
query?: Query;
|
||||
filters?: Filter[];
|
||||
timeRange?: TimeRange;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export type EmbeddableWithQueryInput = IEmbeddable<EmbeddableQueryInput>;
|
||||
|
||||
interface UrlDrilldownDeps {
|
||||
discover: Pick<DiscoverSetup, 'locator'>;
|
||||
hasDiscoverAccess: () => boolean;
|
||||
}
|
||||
|
||||
export type ActionContext = ApplyGlobalFilterActionContext;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||
export type Config = {
|
||||
openInNewTab: boolean;
|
||||
};
|
||||
|
||||
export type OpenInDiscoverTrigger = typeof APPLY_FILTER_TRIGGER;
|
||||
|
||||
export interface ActionFactoryContext extends BaseActionFactoryContext {
|
||||
embeddable?: EmbeddableWithQueryInput;
|
||||
}
|
||||
export type CollectConfigProps = CollectConfigPropsBase<Config, ActionFactoryContext>;
|
||||
|
||||
const OPEN_IN_DISCOVER_DRILLDOWN = 'OPEN_IN_DISCOVER_DRILLDOWN';
|
||||
|
||||
export class OpenInDiscoverDrilldown
|
||||
implements Drilldown<Config, ActionContext, ActionFactoryContext>
|
||||
{
|
||||
public readonly id = OPEN_IN_DISCOVER_DRILLDOWN;
|
||||
|
||||
constructor(private readonly deps: UrlDrilldownDeps) {}
|
||||
|
||||
public readonly order = 8;
|
||||
|
||||
public readonly getDisplayName = () =>
|
||||
i18n.translate('xpack.lens.app.exploreDataInDiscoverDrilldown', {
|
||||
defaultMessage: 'Open in Discover',
|
||||
});
|
||||
|
||||
public readonly euiIcon = 'discoverApp';
|
||||
|
||||
supportedTriggers(): OpenInDiscoverTrigger[] {
|
||||
return [APPLY_FILTER_TRIGGER];
|
||||
}
|
||||
|
||||
private readonly ReactCollectConfig: React.FC<CollectConfigProps> = ({
|
||||
config,
|
||||
onConfig,
|
||||
context,
|
||||
}) => {
|
||||
return (
|
||||
<EuiFormRow hasChildLabel={false}>
|
||||
<EuiSwitch
|
||||
id="openInNewTab"
|
||||
name="openInNewTab"
|
||||
label={i18n.translate('xpack.lens.app.exploreDataInDiscoverDrilldown.newTabConfig', {
|
||||
defaultMessage: 'Open in new tab',
|
||||
})}
|
||||
checked={config.openInNewTab}
|
||||
onChange={() => onConfig({ ...config, openInNewTab: !config.openInNewTab })}
|
||||
data-test-subj="openInDiscoverDrilldownOpenInNewTab"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
);
|
||||
};
|
||||
|
||||
public readonly CollectConfig = reactToUiComponent(this.ReactCollectConfig);
|
||||
|
||||
public readonly createConfig = () => ({
|
||||
openInNewTab: true,
|
||||
});
|
||||
|
||||
public readonly isConfigValid = (config: Config): config is Config => {
|
||||
return true;
|
||||
};
|
||||
|
||||
public readonly isCompatible = async (config: Config, context: ActionContext) => {
|
||||
return isCompatible({
|
||||
discover: this.deps.discover,
|
||||
hasDiscoverAccess: this.deps.hasDiscoverAccess(),
|
||||
...context,
|
||||
embeddable: context.embeddable as IEmbeddable,
|
||||
...config,
|
||||
});
|
||||
};
|
||||
|
||||
public readonly isConfigurable = (context: ActionFactoryContext) => {
|
||||
return this.deps.hasDiscoverAccess() && isLensEmbeddable(context.embeddable as IEmbeddable);
|
||||
};
|
||||
|
||||
public readonly execute = async (config: Config, context: ActionContext) => {
|
||||
const { restOfFilters: filters, timeRange: timeRange } = extractTimeRange(
|
||||
context.filters,
|
||||
context.timeFieldName
|
||||
);
|
||||
execute({
|
||||
discover: this.deps.discover,
|
||||
hasDiscoverAccess: this.deps.hasDiscoverAccess(),
|
||||
...context,
|
||||
embeddable: context.embeddable as IEmbeddable,
|
||||
openInSameTab: !config.openInNewTab,
|
||||
filters,
|
||||
timeRange,
|
||||
});
|
||||
};
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { DiscoverSetup } from '@kbn/discover-plugin/public';
|
||||
import { Filter } from '@kbn/es-query';
|
||||
import { TimeRange } from '@kbn/data-plugin/public';
|
||||
import { IEmbeddable } from '@kbn/embeddable-plugin/public';
|
||||
import type { Embeddable } from '../embeddable';
|
||||
import { DOC_TYPE } from '../../common';
|
||||
|
||||
interface Context {
|
||||
embeddable: IEmbeddable;
|
||||
filters?: Filter[];
|
||||
timeRange?: TimeRange;
|
||||
openInSameTab?: boolean;
|
||||
hasDiscoverAccess: boolean;
|
||||
discover: Pick<DiscoverSetup, 'locator'>;
|
||||
}
|
||||
|
||||
export function isLensEmbeddable(embeddable: IEmbeddable): embeddable is Embeddable {
|
||||
return embeddable.type === DOC_TYPE;
|
||||
}
|
||||
|
||||
export async function isCompatible({ hasDiscoverAccess, embeddable }: Context) {
|
||||
if (!hasDiscoverAccess) return false;
|
||||
return isLensEmbeddable(embeddable) && (await embeddable.canViewUnderlyingData());
|
||||
}
|
||||
|
||||
export function execute({ embeddable, discover, timeRange, filters, openInSameTab }: Context) {
|
||||
if (!isLensEmbeddable(embeddable)) {
|
||||
// shouldn't be executed because of the isCompatible check
|
||||
throw new Error('Can only be executed in the context of Lens visualization');
|
||||
}
|
||||
const args = embeddable.getViewUnderlyingDataArgs();
|
||||
if (!args) {
|
||||
// shouldn't be executed because of the isCompatible check
|
||||
throw new Error('Underlying data is not ready');
|
||||
}
|
||||
const discoverUrl = discover.locator?.getRedirectUrl({
|
||||
...args,
|
||||
timeRange: timeRange || args.timeRange,
|
||||
filters: [...(filters || []), ...args.filters],
|
||||
});
|
||||
window.open(discoverUrl, !openInSameTab ? '_blank' : '_self');
|
||||
}
|
|
@ -12,6 +12,7 @@
|
|||
{ "path": "../../../src/core/tsconfig.json" },
|
||||
{ "path": "../task_manager/tsconfig.json" },
|
||||
{ "path": "../global_search/tsconfig.json" },
|
||||
{ "path": "../ui_actions_enhanced/tsconfig.json" },
|
||||
{ "path": "../saved_objects_tagging/tsconfig.json" },
|
||||
{ "path": "../../../src/plugins/data/tsconfig.json" },
|
||||
{ "path": "../../../src/plugins/data_views/tsconfig.json" },
|
||||
|
|
|
@ -95,6 +95,12 @@ export interface DrilldownDefinition<
|
|||
*/
|
||||
isConfigValid: ActionFactoryDefinition<Config, ExecutionContext, FactoryContext>['isConfigValid'];
|
||||
|
||||
/**
|
||||
* Compatibility check during drilldown creation.
|
||||
* Could be used to filter out a drilldown if it's not compatible with the current context.
|
||||
*/
|
||||
isConfigurable?(context: FactoryContext): boolean;
|
||||
|
||||
/**
|
||||
* Name of EUI icon to display when showing this drilldown to user.
|
||||
*/
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiLoadingSpinner } from '@elastic/eui';
|
||||
import { ActionFactoryPicker as ActionFactoryPickerUi } from '../../../../components/action_factory_picker';
|
||||
import { useDrilldownManager } from '../context';
|
||||
import { ActionFactoryView } from '../action_factory_view';
|
||||
|
@ -14,14 +15,19 @@ export const ActionFactoryPicker: React.FC = ({}) => {
|
|||
const drilldowns = useDrilldownManager();
|
||||
const factory = drilldowns.useActionFactory();
|
||||
const context = React.useMemo(() => drilldowns.getActionFactoryContext(), [drilldowns]);
|
||||
const compatibleFactories = drilldowns.useCompatibleActionFactories(context);
|
||||
|
||||
if (!!factory) {
|
||||
return <ActionFactoryView factory={factory} context={context} />;
|
||||
}
|
||||
|
||||
if (!compatibleFactories) {
|
||||
return <EuiLoadingSpinner size="m" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<ActionFactoryPickerUi
|
||||
actionFactories={drilldowns.deps.actionFactories}
|
||||
actionFactories={compatibleFactories}
|
||||
context={context}
|
||||
onSelect={(actionFactory) => {
|
||||
drilldowns.setActionFactory(actionFactory);
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
*/
|
||||
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import type { SerializableRecord } from '@kbn/utility-types';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
PublicDrilldownManagerProps,
|
||||
DrilldownManagerDependencies,
|
||||
|
@ -255,6 +256,24 @@ export class DrilldownManagerState {
|
|||
return context;
|
||||
}
|
||||
|
||||
public getCompatibleActionFactories(
|
||||
context: BaseActionFactoryContext
|
||||
): Observable<ActionFactory[] | undefined> {
|
||||
const compatibleActionFactories$ = new BehaviorSubject<undefined | ActionFactory[]>(undefined);
|
||||
Promise.allSettled(
|
||||
this.deps.actionFactories.map((factory) => factory.isCompatible(context))
|
||||
).then((factoryCompatibility) => {
|
||||
compatibleActionFactories$.next(
|
||||
this.deps.actionFactories.filter((_factory, i) => {
|
||||
const result = factoryCompatibility[i];
|
||||
// treat failed isCompatible checks as non-compatible
|
||||
return result.status === 'fulfilled' && result.value;
|
||||
})
|
||||
);
|
||||
});
|
||||
return compatibleActionFactories$.asObservable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get state object of the drilldown which is currently being created.
|
||||
*/
|
||||
|
@ -478,4 +497,9 @@ export class DrilldownManagerState {
|
|||
public readonly useActionFactory = () =>
|
||||
useObservable(this.actionFactory$, this.actionFactory$.getValue());
|
||||
public readonly useEvents = () => useObservable(this.events$, this.events$.getValue());
|
||||
public readonly useCompatibleActionFactories = (context: BaseActionFactoryContext) =>
|
||||
useObservable(
|
||||
useMemo(() => this.getCompatibleActionFactories(context), [context]),
|
||||
undefined
|
||||
);
|
||||
}
|
||||
|
|
|
@ -116,6 +116,7 @@ export class UiActionsServiceEnhancements
|
|||
licenseFeatureName,
|
||||
supportedTriggers,
|
||||
isCompatible,
|
||||
isConfigurable,
|
||||
telemetry,
|
||||
extract,
|
||||
inject,
|
||||
|
@ -135,7 +136,7 @@ export class UiActionsServiceEnhancements
|
|||
extract,
|
||||
inject,
|
||||
getIconType: () => euiIcon,
|
||||
isCompatible: async () => true,
|
||||
isCompatible: async (context) => !isConfigurable || isConfigurable(context),
|
||||
create: (serializedAction) => ({
|
||||
id: '',
|
||||
type: factoryId,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue