Use "Apply_filter_trigger" in dashboard drilldown (#71468)

* attach dashboard drilldown to apply filter trigger

* fix types

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Anton Dosov 2020-07-28 09:57:04 +02:00 committed by GitHub
parent 9b570a9bf1
commit abfda1f792
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 225 additions and 196 deletions

View file

@ -52,5 +52,6 @@ esFilters: {
convertRangeFilterToTimeRangeString: typeof convertRangeFilterToTimeRangeString;
mapAndFlattenFilters: (filters: import("../common").Filter[]) => import("../common").Filter[];
extractTimeFilter: typeof extractTimeFilter;
extractTimeRange: typeof extractTimeRange;
}
```

View file

@ -32,7 +32,11 @@ export {
export { DashboardConstants, createDashboardEditUrl } from './dashboard_constants';
export { DashboardStart, DashboardUrlGenerator } from './plugin';
export { DASHBOARD_APP_URL_GENERATOR, createDashboardUrlGenerator } from './url_generator';
export {
DASHBOARD_APP_URL_GENERATOR,
createDashboardUrlGenerator,
DashboardUrlGeneratorState,
} from './url_generator';
export { addEmbeddableToDashboardUrl } from './url_utils/url_helper';
export { SavedObjectDashboard } from './saved_dashboards';
export { SavedDashboardPanel } from './types';

View file

@ -65,6 +65,7 @@ import {
ACTION_REPLACE_PANEL,
ClonePanelAction,
ClonePanelActionContext,
createDashboardContainerByValueRenderer,
DASHBOARD_CONTAINER_TYPE,
DashboardContainerFactory,
DashboardContainerFactoryDefinition,
@ -77,17 +78,17 @@ import {
import {
createDashboardUrlGenerator,
DASHBOARD_APP_URL_GENERATOR,
DashboardAppLinkGeneratorState,
DashboardUrlGeneratorState,
} from './url_generator';
import { createSavedDashboardLoader } from './saved_dashboards';
import { DashboardConstants } from './dashboard_constants';
import { addEmbeddableToDashboardUrl } from './url_utils/url_helper';
import { PlaceholderEmbeddableFactory } from './application/embeddable/placeholder';
import { createDashboardContainerByValueRenderer } from './application';
import { UrlGeneratorState } from '../../share/public';
declare module '../../share/public' {
export interface UrlGeneratorStateMapping {
[DASHBOARD_APP_URL_GENERATOR]: DashboardAppLinkGeneratorState;
[DASHBOARD_APP_URL_GENERATOR]: UrlGeneratorState<DashboardUrlGeneratorState>;
}
}

View file

@ -26,7 +26,7 @@ import {
RefreshInterval,
} from '../../data/public';
import { setStateToKbnUrl } from '../../kibana_utils/public';
import { UrlGeneratorsDefinition, UrlGeneratorState } from '../../share/public';
import { UrlGeneratorsDefinition } from '../../share/public';
import { SavedObjectLoader } from '../../saved_objects/public';
import { ViewMode } from '../../embeddable/public';
@ -35,7 +35,7 @@ export const GLOBAL_STATE_STORAGE_KEY = '_g';
export const DASHBOARD_APP_URL_GENERATOR = 'DASHBOARD_APP_URL_GENERATOR';
export type DashboardAppLinkGeneratorState = UrlGeneratorState<{
export interface DashboardUrlGeneratorState {
/**
* If given, the dashboard saved object with this id will be loaded. If not given,
* a new, unsaved dashboard will be loaded up.
@ -79,7 +79,7 @@ export type DashboardAppLinkGeneratorState = UrlGeneratorState<{
* View mode of the dashboard.
*/
viewMode?: ViewMode;
}>;
}
export const createDashboardUrlGenerator = (
getStartServices: () => Promise<{

View file

@ -58,6 +58,7 @@ import {
changeTimeFilter,
mapAndFlattenFilters,
extractTimeFilter,
extractTimeRange,
convertRangeFilterToTimeRangeString,
} from './query';
@ -99,6 +100,7 @@ export const esFilters = {
convertRangeFilterToTimeRangeString,
mapAndFlattenFilters,
extractTimeFilter,
extractTimeRange,
};
export {

View file

@ -499,6 +499,7 @@ export const esFilters: {
convertRangeFilterToTimeRangeString: typeof convertRangeFilterToTimeRangeString;
mapAndFlattenFilters: (filters: import("../common").Filter[]) => import("../common").Filter[];
extractTimeFilter: typeof extractTimeFilter;
extractTimeRange: typeof extractTimeRange;
};
// Warning: (ae-missing-release-tag) "esKuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@ -1973,52 +1974,53 @@ export const UI_SETTINGS: {
// src/plugins/data/common/es_query/filters/match_all_filter.ts:28:3 - (ae-forgotten-export) The symbol "MatchAllFilterMeta" needs to be exported by the entry point index.d.ts
// src/plugins/data/common/es_query/filters/phrase_filter.ts:33:3 - (ae-forgotten-export) The symbol "PhraseFilterMeta" needs to be exported by the entry point index.d.ts
// src/plugins/data/common/es_query/filters/phrases_filter.ts:31:3 - (ae-forgotten-export) The symbol "PhrasesFilterMeta" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "FilterLabel" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "FILTERS" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "getDisplayValueFromFilter" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "generateFilters" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "changeTimeFilter" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "convertRangeFilterToTimeRangeString" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "extractTimeFilter" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:136:21 - (ae-forgotten-export) The symbol "buildEsQuery" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:136:21 - (ae-forgotten-export) The symbol "getEsQueryConfig" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:136:21 - (ae-forgotten-export) The symbol "luceneStringToDsl" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:136:21 - (ae-forgotten-export) The symbol "decorateQuery" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "FieldFormatsRegistry" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "BoolFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "BytesFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "ColorFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "DurationFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "IpFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "NumberFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "PercentFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "RelativeDateFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "SourceFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "StaticLookupFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "UrlFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "StringFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:232:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:232:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:232:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:232:27 - (ae-forgotten-export) The symbol "getFromSavedObject" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:232:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:232:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:369:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:369:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:369:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:369:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:371:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:372:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:381:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:382:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:383:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:384:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:392:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:393:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:396:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "FilterLabel" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "FILTERS" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "getDisplayValueFromFilter" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "generateFilters" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "changeTimeFilter" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "convertRangeFilterToTimeRangeString" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "extractTimeFilter" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "extractTimeRange" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:138:21 - (ae-forgotten-export) The symbol "buildEsQuery" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:138:21 - (ae-forgotten-export) The symbol "getEsQueryConfig" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:138:21 - (ae-forgotten-export) The symbol "luceneStringToDsl" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:138:21 - (ae-forgotten-export) The symbol "decorateQuery" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "FieldFormatsRegistry" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "BoolFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "BytesFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "ColorFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "DurationFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "IpFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "NumberFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "PercentFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "RelativeDateFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "SourceFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "StaticLookupFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "UrlFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "StringFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "getFromSavedObject" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:371:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:371:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:371:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:371:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:373:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:374:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:383:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:384:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:385:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:386:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:390:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:394:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:395:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:398:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/query/state_sync/connect_to_query_state.ts:41:60 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/types.ts:54:5 - (ae-forgotten-export) The symbol "createFiltersFromValueClickAction" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/types.ts:55:5 - (ae-forgotten-export) The symbol "createFiltersFromRangeSelectAction" needs to be exported by the entry point index.d.ts

View file

@ -23,5 +23,5 @@ export * from './types';
export { Timefilter, TimefilterContract } from './timefilter';
export { TimeHistory, TimeHistoryContract } from './time_history';
export { changeTimeFilter, convertRangeFilterToTimeRangeString } from './lib/change_time_filter';
export { extractTimeFilter } from './lib/extract_time_filter';
export { extractTimeFilter, extractTimeRange } from './lib/extract_time_filter';
export { validateTimeRange } from './lib/validate_timerange';

View file

@ -18,7 +18,8 @@
*/
import { keys, partition } from 'lodash';
import { Filter, isRangeFilter, RangeFilter } from '../../../../common';
import { Filter, isRangeFilter, RangeFilter, TimeRange } from '../../../../common';
import { convertRangeFilterToTimeRangeString } from './change_time_filter';
export function extractTimeFilter(timeFieldName: string, filters: Filter[]) {
const [timeRangeFilter, restOfFilters] = partition(filters, (obj: Filter) => {
@ -36,3 +37,15 @@ export function extractTimeFilter(timeFieldName: string, filters: Filter[]) {
timeRangeFilter: timeRangeFilter[0] as RangeFilter | undefined,
};
}
export function extractTimeRange(
filters: Filter[],
timeFieldName?: string
): { restOfFilters: Filter[]; timeRange?: TimeRange } {
if (!timeFieldName) return { restOfFilters: filters, timeRange: undefined };
const { timeRangeFilter, restOfFilters } = extractTimeFilter(timeFieldName, filters);
return {
restOfFilters,
timeRange: timeRangeFilter ? convertRangeFilterToTimeRangeString(timeRangeFilter) : undefined,
};
}

View file

@ -8,6 +8,7 @@
"requiredBundles": [
"kibanaUtils",
"embeddableEnhanced",
"kibanaReact"
"kibanaReact",
"uiActions"
]
}

View file

@ -6,7 +6,12 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
import { ActionByType } from '../../../../../../../../src/plugins/ui_actions/public';
import {
ActionByType,
APPLY_FILTER_TRIGGER,
SELECT_RANGE_TRIGGER,
VALUE_CLICK_TRIGGER,
} from '../../../../../../../../src/plugins/ui_actions/public';
import { toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public';
import { isEnhancedEmbeddable } from '../../../../../../embeddable_enhanced/public';
import { EmbeddableContext } from '../../../../../../../../src/plugins/embeddable/public';
@ -42,7 +47,9 @@ export class FlyoutCreateDrilldownAction implements ActionByType<typeof OPEN_FLY
if (!supportedTriggers || !supportedTriggers.length) return false;
if (context.embeddable.getRoot().type !== 'dashboard') return false;
return supportedTriggers.indexOf('VALUE_CLICK_TRIGGER') > -1;
return supportedTriggers.some((trigger) =>
[VALUE_CLICK_TRIGGER, SELECT_RANGE_TRIGGER, APPLY_FILTER_TRIGGER].includes(trigger)
);
}
public async isCompatible(context: EmbeddableContext) {

View file

@ -4,4 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
/**
* note:
* don't change this string without carefull consideration,
* because it is stored in saved objects.
* Also temporary dashboard drilldown migration code inside embeddable plugin relies on it
* x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.ts
*/
export const DASHBOARD_TO_DASHBOARD_DRILLDOWN = 'DASHBOARD_TO_DASHBOARD_DRILLDOWN';

View file

@ -5,9 +5,8 @@
*/
import { DashboardToDashboardDrilldown } from './drilldown';
import { savedObjectsServiceMock, coreMock } from '../../../../../../../src/core/public/mocks';
import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks';
import { ActionContext, Config } from './types';
import { Config } from './types';
import { coreMock, savedObjectsServiceMock } from '../../../../../../../src/core/public/mocks';
import {
Filter,
FilterStateStore,
@ -15,16 +14,13 @@ import {
RangeFilter,
TimeRange,
} from '../../../../../../../src/plugins/data/common';
import { esFilters } from '../../../../../../../src/plugins/data/public';
import {
ApplyGlobalFilterActionContext,
esFilters,
} from '../../../../../../../src/plugins/data/public';
// convenient to use real implementation here.
import { createDashboardUrlGenerator } from '../../../../../../../src/plugins/dashboard/public/url_generator';
import { UrlGeneratorsService } from '../../../../../../../src/plugins/share/public/url_generators';
import { VisualizeEmbeddableContract } from '../../../../../../../src/plugins/visualizations/public';
import {
RangeSelectContext,
ValueClickContext,
} from '../../../../../../../src/plugins/embeddable/public';
import { StartDependencies } from '../../../plugin';
import { SavedObjectLoader } from '../../../../../../../src/plugins/saved_objects/public';
import { StartServicesGetter } from '../../../../../../../src/plugins/kibana_utils/public/core';
@ -82,11 +78,10 @@ describe('.execute() & getHref', () => {
config: Partial<Config>,
embeddableInput: { filters?: Filter[]; timeRange?: TimeRange; query?: Query },
filtersFromEvent: Filter[],
useRangeEvent = false
timeFieldName?: string
) {
const navigateToApp = jest.fn();
const getUrlForApp = jest.fn((app, opt) => `${app}/${opt.path}`);
const dataPluginActions = dataPluginMock.createStartContract().actions;
const savedObjectsClient = savedObjectsServiceMock.createStartContract().client;
const drilldown = new DashboardToDashboardDrilldown({
@ -102,9 +97,6 @@ describe('.execute() & getHref', () => {
},
plugins: {
uiActionsEnhanced: {},
data: {
actions: dataPluginActions,
},
},
self: {},
})) as unknown) as StartServicesGetter<Pick<StartDependencies, 'data' | 'uiActionsEnhanced'>>,
@ -119,12 +111,6 @@ describe('.execute() & getHref', () => {
)
),
});
const selectRangeFiltersSpy = jest
.spyOn(dataPluginActions, 'createFiltersFromRangeSelectAction')
.mockImplementation(() => Promise.resolve(filtersFromEvent));
const valueClickFiltersSpy = jest
.spyOn(dataPluginActions, 'createFiltersFromValueClickAction')
.mockImplementation(() => Promise.resolve(filtersFromEvent));
const completeConfig: Config = {
dashboardId: 'id',
@ -134,12 +120,7 @@ describe('.execute() & getHref', () => {
};
const context = ({
data: {
...(useRangeEvent
? ({ range: {} } as RangeSelectContext['data'])
: ({ data: [] } as ValueClickContext['data'])),
timeFieldName: 'order_date',
},
filters: filtersFromEvent,
embeddable: {
getInput: () => ({
filters: [],
@ -148,18 +129,11 @@ describe('.execute() & getHref', () => {
...embeddableInput,
}),
},
} as unknown) as ActionContext<VisualizeEmbeddableContract>;
timeFieldName,
} as unknown) as ApplyGlobalFilterActionContext;
await drilldown.execute(completeConfig, context);
if (useRangeEvent) {
expect(selectRangeFiltersSpy).toBeCalledTimes(1);
expect(valueClickFiltersSpy).toBeCalledTimes(0);
} else {
expect(selectRangeFiltersSpy).toBeCalledTimes(0);
expect(valueClickFiltersSpy).toBeCalledTimes(1);
}
expect(navigateToApp).toBeCalledTimes(1);
expect(navigateToApp.mock.calls[0][0]).toBe('dashboards');
@ -180,8 +154,7 @@ describe('.execute() & getHref', () => {
dashboardId: testDashboardId,
},
{},
[],
false
[]
);
expect(href).toEqual(expect.stringContaining(`view/${testDashboardId}`));
@ -289,8 +262,7 @@ describe('.execute() & getHref', () => {
to: 'now',
},
},
[],
false
[]
);
expect(href).not.toEqual(expect.stringContaining('now-300m'));
@ -308,7 +280,7 @@ describe('.execute() & getHref', () => {
},
},
[getMockTimeRangeFilter()],
true
getMockTimeRangeFilter().meta.key
);
expect(href).not.toEqual(expect.stringContaining('now-300m'));

View file

@ -6,20 +6,24 @@
import React from 'react';
import { reactToUiComponent } from '../../../../../../../src/plugins/kibana_react/public';
import { DashboardUrlGenerator } from '../../../../../../../src/plugins/dashboard/public';
import { ActionContext, Config } from './types';
import {
DashboardUrlGenerator,
DashboardUrlGeneratorState,
} from '../../../../../../../src/plugins/dashboard/public';
import { CollectConfigContainer } from './components';
import { DASHBOARD_TO_DASHBOARD_DRILLDOWN } from './constants';
import { UiActionsEnhancedDrilldownDefinition as Drilldown } from '../../../../../ui_actions_enhanced/public';
import { txtGoToDashboard } from './i18n';
import { esFilters } from '../../../../../../../src/plugins/data/public';
import { VisualizeEmbeddableContract } from '../../../../../../../src/plugins/visualizations/public';
import {
isRangeSelectTriggerContext,
isValueClickTriggerContext,
} from '../../../../../../../src/plugins/embeddable/public';
ApplyGlobalFilterActionContext,
esFilters,
isFilters,
isQuery,
isTimeRange,
} from '../../../../../../../src/plugins/data/public';
import { StartServicesGetter } from '../../../../../../../src/plugins/kibana_utils/public';
import { StartDependencies } from '../../../plugin';
import { Config } from './types';
export interface Params {
start: StartServicesGetter<Pick<StartDependencies, 'data' | 'uiActionsEnhanced'>>;
@ -27,7 +31,7 @@ export interface Params {
}
export class DashboardToDashboardDrilldown
implements Drilldown<Config, ActionContext<VisualizeEmbeddableContract>> {
implements Drilldown<Config, ApplyGlobalFilterActionContext> {
constructor(protected readonly params: Params) {}
public readonly id = DASHBOARD_TO_DASHBOARD_DRILLDOWN;
@ -57,15 +61,12 @@ export class DashboardToDashboardDrilldown
public readonly getHref = async (
config: Config,
context: ActionContext<VisualizeEmbeddableContract>
context: ApplyGlobalFilterActionContext
): Promise<string> => {
return this.getDestinationUrl(config, context);
};
public readonly execute = async (
config: Config,
context: ActionContext<VisualizeEmbeddableContract>
) => {
public readonly execute = async (config: Config, context: ApplyGlobalFilterActionContext) => {
const dashboardPath = await this.getDestinationUrl(config, context);
const dashboardHash = dashboardPath.split('#')[1];
@ -76,73 +77,43 @@ export class DashboardToDashboardDrilldown
private getDestinationUrl = async (
config: Config,
context: ActionContext<VisualizeEmbeddableContract>
context: ApplyGlobalFilterActionContext
): Promise<string> => {
const {
createFiltersFromRangeSelectAction,
createFiltersFromValueClickAction,
} = this.params.start().plugins.data.actions;
const {
timeRange: currentTimeRange,
query,
filters: currentFilters,
} = context.embeddable!.getInput();
const state: DashboardUrlGeneratorState = {
dashboardId: config.dashboardId,
};
// if useCurrentDashboardFilters enabled, then preserve all the filters (pinned and unpinned)
// otherwise preserve only pinned
const existingFilters =
(config.useCurrentFilters
? currentFilters
: currentFilters?.filter((f) => esFilters.isFilterPinned(f))) ?? [];
if (context.embeddable) {
const input = context.embeddable.getInput();
if (isQuery(input.query) && config.useCurrentFilters) state.query = input.query;
// if useCurrentDashboardDataRange is enabled, then preserve current time range
// if undefined is passed, then destination dashboard will figure out time range itself
// for brush event this time range would be overwritten
let timeRange = config.useCurrentDateRange ? currentTimeRange : undefined;
let filtersFromEvent = await (async () => {
try {
if (isRangeSelectTriggerContext(context))
return await createFiltersFromRangeSelectAction(context.data);
if (isValueClickTriggerContext(context))
return await createFiltersFromValueClickAction(context.data);
// if useCurrentDashboardDataRange is enabled, then preserve current time range
// if undefined is passed, then destination dashboard will figure out time range itself
// for brush event this time range would be overwritten
if (isTimeRange(input.timeRange) && config.useCurrentDateRange)
state.timeRange = input.timeRange;
// eslint-disable-next-line no-console
console.warn(
`
DashboardToDashboard drilldown: can't extract filters from action.
Is it not supported action?`,
context
);
return [];
} catch (e) {
// eslint-disable-next-line no-console
console.warn(
`
DashboardToDashboard drilldown: error extracting filters from action.
Continuing without applying filters from event`,
e
);
return [];
}
})();
if (context.data.timeFieldName) {
const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter(
context.data.timeFieldName,
filtersFromEvent
);
filtersFromEvent = restOfFilters;
if (timeRangeFilter) {
timeRange = esFilters.convertRangeFilterToTimeRangeString(timeRangeFilter);
}
// if useCurrentDashboardFilters enabled, then preserve all the filters (pinned and unpinned)
// otherwise preserve only pinned
if (isFilters(input.filters))
state.filters = config.useCurrentFilters
? input.filters
: input.filters?.filter((f) => esFilters.isFilterPinned(f));
}
return this.params.getDashboardUrlGenerator().createUrl({
dashboardId: config.dashboardId,
query: config.useCurrentFilters ? query : undefined,
timeRange,
filters: [...existingFilters, ...filtersFromEvent],
});
const {
restOfFilters: filtersFromEvent,
timeRange: timeRangeFromEvent,
} = esFilters.extractTimeRange(context.filters, context.timeFieldName);
if (filtersFromEvent) {
state.filters = [...(state.filters ?? []), ...filtersFromEvent];
}
if (timeRangeFromEvent) {
state.timeRange = timeRangeFromEvent;
}
return this.params.getDashboardUrlGenerator().createUrl(state);
};
}

View file

@ -9,7 +9,4 @@ export {
DashboardToDashboardDrilldown,
Params as DashboardToDashboardDrilldownParams,
} from './drilldown';
export {
ActionContext as DashboardToDashboardActionContext,
Config as DashboardToDashboardConfig,
} from './types';
export { Config } from './types';

View file

@ -4,16 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import {
ValueClickContext,
RangeSelectContext,
IEmbeddable,
} from '../../../../../../../src/plugins/embeddable/public';
export type ActionContext<T extends IEmbeddable = IEmbeddable> =
| ValueClickContext<T>
| RangeSelectContext<T>;
export interface Config {
dashboardId?: string;
useCurrentFilters: boolean;

View file

@ -11,6 +11,9 @@ import {
} from './embeddable_action_storage';
import { UiActionsEnhancedSerializedEvent } from '../../../ui_actions_enhanced/public';
import { of } from '../../../../../src/plugins/kibana_utils/public';
// use real const to make test fail in case someone accidentally changes it
import { DASHBOARD_TO_DASHBOARD_DRILLDOWN } from '../../../dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown';
import { APPLY_FILTER_TRIGGER } from '../../../../../src/plugins/ui_actions/public';
class TestEmbeddable extends Embeddable<EmbeddableWithDynamicActionsInput> {
public readonly type = 'test';
@ -539,4 +542,42 @@ describe('EmbeddableActionStorage', () => {
expect(await storage.list()).toEqual([]);
});
});
describe('migrate', () => {
test('DASHBOARD_TO_DASHBOARD_DRILLDOWN triggers migration', async () => {
const embeddable = new TestEmbeddable();
const OTHER_TRIGGER = 'OTHER_TRIGGER';
embeddable.updateInput({
enhancements: {
dynamicActions: {
events: [
{
eventId: '1',
triggers: [OTHER_TRIGGER],
action: {
factoryId: DASHBOARD_TO_DASHBOARD_DRILLDOWN,
name: '',
config: {},
},
},
{
eventId: '2',
triggers: [OTHER_TRIGGER],
action: {
factoryId: 'SOME_OTHER',
name: '',
config: {},
},
},
],
},
},
});
const storage = new EmbeddableActionStorage(embeddable);
const [event1, event2] = await storage.list();
expect(event1.triggers).toEqual([APPLY_FILTER_TRIGGER]);
expect(event2.triggers).toEqual([OTHER_TRIGGER]);
});
});
});

View file

@ -46,7 +46,7 @@ export class EmbeddableActionStorage extends AbstractActionStorage {
public async create(event: SerializedEvent) {
const input = this.embbeddable.getInput();
const events = input.enhancements?.dynamicActions?.events || [];
const events = this.getEventsFromEmbeddable();
const exists = !!events.find(({ eventId }) => eventId === event.eventId);
if (exists) {
@ -61,7 +61,7 @@ export class EmbeddableActionStorage extends AbstractActionStorage {
public async update(event: SerializedEvent) {
const input = this.embbeddable.getInput();
const events = input.enhancements?.dynamicActions?.events || [];
const events = this.getEventsFromEmbeddable();
const index = events.findIndex(({ eventId }) => eventId === event.eventId);
if (index === -1) {
@ -77,7 +77,7 @@ export class EmbeddableActionStorage extends AbstractActionStorage {
public async remove(eventId: string) {
const input = this.embbeddable.getInput();
const events = input.enhancements?.dynamicActions?.events || [];
const events = this.getEventsFromEmbeddable();
const index = events.findIndex((event) => eventId === event.eventId);
if (index === -1) {
@ -93,7 +93,7 @@ export class EmbeddableActionStorage extends AbstractActionStorage {
public async read(eventId: string): Promise<SerializedEvent> {
const input = this.embbeddable.getInput();
const events = input.enhancements?.dynamicActions?.events || [];
const events = this.getEventsFromEmbeddable();
const event = events.find((ev) => eventId === ev.eventId);
if (!event) {
@ -107,8 +107,28 @@ export class EmbeddableActionStorage extends AbstractActionStorage {
}
public async list(): Promise<SerializedEvent[]> {
return this.getEventsFromEmbeddable();
}
private getEventsFromEmbeddable() {
const input = this.embbeddable.getInput();
const events = input.enhancements?.dynamicActions?.events || [];
return events;
return this.migrate(events);
}
// TODO: https://github.com/elastic/kibana/issues/71431
// Migration implementation should use registry
// Action factories implementations should register own migrations
private migrate(events: SerializedEvent[]): SerializedEvent[] {
return events.map((event) => {
// Initially dashboard drilldown relied on VALUE_CLICK & RANGE_SELECT
if (event.action.factoryId === 'DASHBOARD_TO_DASHBOARD_DRILLDOWN') {
return {
...event,
triggers: ['FILTER_TRIGGER'],
};
}
return event;
});
}
}

View file

@ -11,9 +11,8 @@ import { DrilldownWizardConfig, FlyoutDrilldownWizard } from '../flyout_drilldow
import { FlyoutListManageDrilldowns } from '../flyout_list_manage_drilldowns';
import { IStorageWrapper } from '../../../../../../../src/plugins/kibana_utils/public';
import {
VALUE_CLICK_TRIGGER,
SELECT_RANGE_TRIGGER,
TriggerContextMapping,
APPLY_FILTER_TRIGGER,
} from '../../../../../../../src/plugins/ui_actions/public';
import { useContainerState } from '../../../../../../../src/plugins/kibana_utils/public';
import { DrilldownListItem } from '../list_manage_drilldowns';
@ -67,8 +66,9 @@ export function createFlyoutManageDrilldowns({
return (props: ConnectedFlyoutManageDrilldownsProps) => {
const isCreateOnly = props.viewMode === 'create';
// TODO: https://github.com/elastic/kibana/issues/59569
const selectedTriggers: Array<keyof TriggerContextMapping> = React.useMemo(
() => [VALUE_CLICK_TRIGGER, SELECT_RANGE_TRIGGER],
() => [APPLY_FILTER_TRIGGER],
[]
);