[Reporting/Dashboard] Update integration to use v2 reports (#108553)
* very wip, updating dashboard integration to use v2 reports. at the moment time filters are not working correctly * added missing dependency to hook * added tests and refined ForwadedAppState interface * remove unused import * updated test because generating a report from an unsaved report is possible * migrated locator to forward state on history only, reordered methods on react component * remove unused import * update locator test and use panel index number if panelIndex does not exist * ensure locator params are serializable * - moved getSerializableRecord to locator.ts to ensure that the values we get from it will never contain something that cannot be passed to history.push - updated types to remove some `& SerializableRecord` instances - fixed embeddable drilldown Jest tests given that we no longer expect state to be in the URL * update generated api docs * remove unused variable * - removed SerializedRecord extension from dashboard locator params interface - factored out state conversion logic from the locator getLocation * updated locator jest tests and SerializableRecord types * explicitly map values to dashboardlocatorparams and export serializable params type * use serializable params type in embeddable * factored out logic for converting panels to dashboard panels map * use "type =" instead of "interface" * big update to locator params: type fixes and added options key * added comment about why we are using "type" alias instead of "interface" declaration * simplify is v2 job param check Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
de7ae4138d
commit
9e04d2c5c7
|
@ -80,7 +80,6 @@
|
|||
| [QuerySuggestionField](./kibana-plugin-plugins-data-public.querysuggestionfield.md) | \* |
|
||||
| [QuerySuggestionGetFnArgs](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.md) | \* |
|
||||
| [Reason](./kibana-plugin-plugins-data-public.reason.md) | |
|
||||
| [RefreshInterval](./kibana-plugin-plugins-data-public.refreshinterval.md) | |
|
||||
| [SavedQuery](./kibana-plugin-plugins-data-public.savedquery.md) | |
|
||||
| [SavedQueryService](./kibana-plugin-plugins-data-public.savedqueryservice.md) | |
|
||||
| [SearchSessionInfoProvider](./kibana-plugin-plugins-data-public.searchsessioninfoprovider.md) | Provide info about current search session to be stored in the Search Session saved object |
|
||||
|
@ -176,6 +175,7 @@
|
|||
| [RangeFilter](./kibana-plugin-plugins-data-public.rangefilter.md) | |
|
||||
| [RangeFilterMeta](./kibana-plugin-plugins-data-public.rangefiltermeta.md) | |
|
||||
| [RangeFilterParams](./kibana-plugin-plugins-data-public.rangefilterparams.md) | |
|
||||
| [RefreshInterval](./kibana-plugin-plugins-data-public.refreshinterval.md) | |
|
||||
| [SavedQueryTimeFilter](./kibana-plugin-plugins-data-public.savedquerytimefilter.md) | |
|
||||
| [SearchBarProps](./kibana-plugin-plugins-data-public.searchbarprops.md) | |
|
||||
| [StatefulSearchBarProps](./kibana-plugin-plugins-data-public.statefulsearchbarprops.md) | |
|
||||
|
|
|
@ -2,18 +2,13 @@
|
|||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RefreshInterval](./kibana-plugin-plugins-data-public.refreshinterval.md)
|
||||
|
||||
## RefreshInterval interface
|
||||
## RefreshInterval type
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface RefreshInterval
|
||||
export declare type RefreshInterval = {
|
||||
pause: boolean;
|
||||
value: number;
|
||||
};
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [pause](./kibana-plugin-plugins-data-public.refreshinterval.pause.md) | <code>boolean</code> | |
|
||||
| [value](./kibana-plugin-plugins-data-public.refreshinterval.value.md) | <code>number</code> | |
|
||||
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RefreshInterval](./kibana-plugin-plugins-data-public.refreshinterval.md) > [pause](./kibana-plugin-plugins-data-public.refreshinterval.pause.md)
|
||||
|
||||
## RefreshInterval.pause property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
pause: boolean;
|
||||
```
|
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RefreshInterval](./kibana-plugin-plugins-data-public.refreshinterval.md) > [value](./kibana-plugin-plugins-data-public.refreshinterval.value.md)
|
||||
|
||||
## RefreshInterval.value property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
value: number;
|
||||
```
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import { SavedObjectReference } from 'kibana/public';
|
||||
import type { Serializable } from '@kbn/utility-types';
|
||||
|
||||
import { GridData } from '../';
|
||||
|
||||
|
@ -110,7 +111,7 @@ export type RawSavedDashboardPanel630 = RawSavedDashboardPanel620;
|
|||
// In 6.2 we added an inplace migration, moving uiState into each panel's new embeddableConfig property.
|
||||
// Source: https://github.com/elastic/kibana/pull/14949
|
||||
export type RawSavedDashboardPanel620 = RawSavedDashboardPanel610 & {
|
||||
embeddableConfig: { [key: string]: unknown };
|
||||
embeddableConfig: { [key: string]: Serializable };
|
||||
version: string;
|
||||
};
|
||||
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export interface GridData {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||
export type GridData = {
|
||||
w: number;
|
||||
h: number;
|
||||
x: number;
|
||||
y: number;
|
||||
i: string;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { SerializableRecord } from '@kbn/utility-types';
|
||||
import semverSatisfies from 'semver/functions/satisfies';
|
||||
import uuid from 'uuid';
|
||||
import {
|
||||
|
@ -80,7 +81,7 @@ function migratePre61PanelToLatest(
|
|||
panel: RawSavedDashboardPanelTo60,
|
||||
version: string,
|
||||
useMargins: boolean,
|
||||
uiState?: { [key: string]: { [key: string]: unknown } }
|
||||
uiState?: { [key: string]: SerializableRecord }
|
||||
): RawSavedDashboardPanel730ToLatest {
|
||||
if (panel.col === undefined || panel.row === undefined) {
|
||||
throw new Error(
|
||||
|
@ -138,7 +139,7 @@ function migrate610PanelToLatest(
|
|||
panel: RawSavedDashboardPanel610,
|
||||
version: string,
|
||||
useMargins: boolean,
|
||||
uiState?: { [key: string]: { [key: string]: unknown } }
|
||||
uiState?: { [key: string]: SerializableRecord }
|
||||
): RawSavedDashboardPanel730ToLatest {
|
||||
(['w', 'x', 'h', 'y'] as Array<keyof GridData>).forEach((key) => {
|
||||
if (panel.gridData[key] === undefined) {
|
||||
|
@ -273,7 +274,7 @@ export function migratePanelsTo730(
|
|||
>,
|
||||
version: string,
|
||||
useMargins: boolean,
|
||||
uiState?: { [key: string]: { [key: string]: unknown } }
|
||||
uiState?: { [key: string]: SerializableRecord }
|
||||
): RawSavedDashboardPanel730ToLatest[] {
|
||||
return panels.map((panel) => {
|
||||
if (isPre61Panel(panel)) {
|
||||
|
|
|
@ -25,7 +25,9 @@ import {
|
|||
DashboardRedirect,
|
||||
DashboardState,
|
||||
} from '../../types';
|
||||
import { DashboardAppLocatorParams } from '../../locator';
|
||||
import {
|
||||
loadDashboardHistoryLocationState,
|
||||
tryDestroyDashboardContainer,
|
||||
syncDashboardContainerInput,
|
||||
savedObjectToDashboardState,
|
||||
|
@ -88,6 +90,7 @@ export const useDashboardAppState = ({
|
|||
savedObjectsTagging,
|
||||
dashboardCapabilities,
|
||||
dashboardSessionStorage,
|
||||
scopedHistory,
|
||||
} = services;
|
||||
const { docTitle } = chrome;
|
||||
const { notifications } = core;
|
||||
|
@ -149,10 +152,15 @@ export const useDashboardAppState = ({
|
|||
*/
|
||||
const dashboardSessionStorageState = dashboardSessionStorage.getState(savedDashboardId) || {};
|
||||
const dashboardURLState = loadDashboardUrlState(dashboardBuildContext);
|
||||
const forwardedAppState = loadDashboardHistoryLocationState(
|
||||
scopedHistory()?.location?.state as undefined | DashboardAppLocatorParams
|
||||
);
|
||||
|
||||
const initialDashboardState = {
|
||||
...savedDashboardState,
|
||||
...dashboardSessionStorageState,
|
||||
...dashboardURLState,
|
||||
...forwardedAppState,
|
||||
|
||||
// if there is an incoming embeddable, dashboard always needs to be in edit mode to receive it.
|
||||
...(incomingEmbeddable ? { viewMode: ViewMode.EDIT } : {}),
|
||||
|
@ -312,6 +320,7 @@ export const useDashboardAppState = ({
|
|||
getStateTransfer,
|
||||
savedDashboards,
|
||||
usageCollection,
|
||||
scopedHistory,
|
||||
notifications,
|
||||
indexPatterns,
|
||||
kibanaVersion,
|
||||
|
|
|
@ -11,19 +11,15 @@ import type { KibanaExecutionContext } from 'src/core/public';
|
|||
import { DashboardSavedObject } from '../../saved_dashboards';
|
||||
import { getTagsFromSavedDashboard, migrateAppState } from '.';
|
||||
import { EmbeddablePackageState, ViewMode } from '../../services/embeddable';
|
||||
import {
|
||||
convertPanelStateToSavedDashboardPanel,
|
||||
convertSavedDashboardPanelToPanelState,
|
||||
} from '../../../common/embeddable/embeddable_saved_object_converters';
|
||||
import { convertPanelStateToSavedDashboardPanel } from '../../../common/embeddable/embeddable_saved_object_converters';
|
||||
import {
|
||||
DashboardState,
|
||||
RawDashboardState,
|
||||
DashboardPanelMap,
|
||||
SavedDashboardPanel,
|
||||
DashboardAppServices,
|
||||
DashboardContainerInput,
|
||||
DashboardBuildContext,
|
||||
} from '../../types';
|
||||
import { convertSavedPanelsToPanelMap } from './convert_saved_panels_to_panel_map';
|
||||
|
||||
interface SavedObjectToDashboardStateProps {
|
||||
version: string;
|
||||
|
@ -77,11 +73,7 @@ export const savedObjectToDashboardState = ({
|
|||
usageCollection
|
||||
);
|
||||
|
||||
const panels: DashboardPanelMap = {};
|
||||
rawState.panels?.forEach((panel: SavedDashboardPanel) => {
|
||||
panels[panel.panelIndex] = convertSavedDashboardPanelToPanelState(panel);
|
||||
});
|
||||
return { ...rawState, panels };
|
||||
return { ...rawState, panels: convertSavedPanelsToPanelMap(rawState.panels) };
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { convertSavedDashboardPanelToPanelState } from '../../../common/embeddable/embeddable_saved_object_converters';
|
||||
import type { SavedDashboardPanel, DashboardPanelMap } from '../../types';
|
||||
|
||||
export const convertSavedPanelsToPanelMap = (panels?: SavedDashboardPanel[]): DashboardPanelMap => {
|
||||
const panelsMap: DashboardPanelMap = {};
|
||||
panels?.forEach((panel, idx) => {
|
||||
panelsMap![panel.panelIndex ?? String(idx)] = convertSavedDashboardPanelToPanelState(panel);
|
||||
});
|
||||
return panelsMap;
|
||||
};
|
|
@ -20,6 +20,7 @@ export { syncDashboardFilterState } from './sync_dashboard_filter_state';
|
|||
export { syncDashboardIndexPatterns } from './sync_dashboard_index_patterns';
|
||||
export { syncDashboardContainerInput } from './sync_dashboard_container_input';
|
||||
export { diffDashboardContainerInput, diffDashboardState } from './diff_dashboard_state';
|
||||
export { loadDashboardHistoryLocationState } from './load_dashboard_history_location_state';
|
||||
export { buildDashboardContainer, tryDestroyDashboardContainer } from './build_dashboard_container';
|
||||
export {
|
||||
stateToDashboardContainerInput,
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ForwardedDashboardState } from '../../locator';
|
||||
import { DashboardState } from '../../types';
|
||||
import { convertSavedPanelsToPanelMap } from './convert_saved_panels_to_panel_map';
|
||||
|
||||
export const loadDashboardHistoryLocationState = (
|
||||
state?: ForwardedDashboardState
|
||||
): Partial<DashboardState> => {
|
||||
if (!state) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const { panels, ...restOfState } = state;
|
||||
if (!panels?.length) {
|
||||
return restOfState;
|
||||
}
|
||||
|
||||
return {
|
||||
...restOfState,
|
||||
...{ panels: convertSavedPanelsToPanelMap(panels) },
|
||||
};
|
||||
};
|
|
@ -11,15 +11,14 @@ import _ from 'lodash';
|
|||
import { migrateAppState } from '.';
|
||||
import { replaceUrlHashQuery } from '../../../../kibana_utils/public';
|
||||
import { DASHBOARD_STATE_STORAGE_KEY } from '../../dashboard_constants';
|
||||
import { convertSavedDashboardPanelToPanelState } from '../../../common/embeddable/embeddable_saved_object_converters';
|
||||
import {
|
||||
import type {
|
||||
DashboardBuildContext,
|
||||
DashboardPanelMap,
|
||||
DashboardState,
|
||||
RawDashboardState,
|
||||
SavedDashboardPanel,
|
||||
} from '../../types';
|
||||
import { migrateLegacyQuery } from './migrate_legacy_query';
|
||||
import { convertSavedPanelsToPanelMap } from './convert_saved_panels_to_panel_map';
|
||||
|
||||
/**
|
||||
* Loads any dashboard state from the URL, and removes the state from the URL.
|
||||
|
@ -32,12 +31,10 @@ export const loadDashboardUrlState = ({
|
|||
const rawAppStateInUrl = kbnUrlStateStorage.get<RawDashboardState>(DASHBOARD_STATE_STORAGE_KEY);
|
||||
if (!rawAppStateInUrl) return {};
|
||||
|
||||
const panelsMap: DashboardPanelMap = {};
|
||||
let panelsMap: DashboardPanelMap = {};
|
||||
if (rawAppStateInUrl.panels && rawAppStateInUrl.panels.length > 0) {
|
||||
const rawState = migrateAppState(rawAppStateInUrl, kibanaVersion, usageCollection);
|
||||
rawState.panels?.forEach((panel: SavedDashboardPanel) => {
|
||||
panelsMap[panel.panelIndex] = convertSavedDashboardPanelToPanelState(panel);
|
||||
});
|
||||
panelsMap = convertSavedPanelsToPanelMap(rawState.panels);
|
||||
}
|
||||
|
||||
const migratedQuery = rawAppStateInUrl.query
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import semverSatisfies from 'semver/functions/satisfies';
|
||||
import type { SerializableRecord } from '@kbn/utility-types';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { METRIC_TYPE } from '@kbn/analytics';
|
||||
|
||||
|
@ -75,7 +76,7 @@ export function migrateAppState(
|
|||
>,
|
||||
kibanaVersion,
|
||||
appState.useMargins as boolean,
|
||||
appState.uiState as Record<string, Record<string, unknown>>
|
||||
appState.uiState as { [key: string]: SerializableRecord }
|
||||
) as SavedDashboardPanel[];
|
||||
delete appState.uiState;
|
||||
}
|
||||
|
|
|
@ -404,6 +404,7 @@ export function DashboardTopNav({
|
|||
(anchorElement: HTMLElement) => {
|
||||
if (!share) return;
|
||||
const currentState = dashboardAppState.getLatestDashboardState();
|
||||
const timeRange = timefilter.getTime();
|
||||
ShowShareModal({
|
||||
share,
|
||||
kibanaVersion,
|
||||
|
@ -412,9 +413,10 @@ export function DashboardTopNav({
|
|||
currentDashboardState: currentState,
|
||||
savedDashboard: dashboardAppState.savedDashboard,
|
||||
isDirty: Boolean(dashboardAppState.hasUnsavedChanges),
|
||||
timeRange,
|
||||
});
|
||||
},
|
||||
[dashboardAppState, dashboardCapabilities, share, kibanaVersion]
|
||||
[dashboardAppState, dashboardCapabilities, share, kibanaVersion, timefilter]
|
||||
);
|
||||
|
||||
const dashboardTopNavActions = useMemo(() => {
|
||||
|
|
|
@ -6,16 +6,20 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { Capabilities } from 'src/core/public';
|
||||
import { EuiCheckboxGroup } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { ReactElement, useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import moment from 'moment';
|
||||
import React, { ReactElement, useState } from 'react';
|
||||
import type { Capabilities } from 'src/core/public';
|
||||
import { DashboardSavedObject } from '../..';
|
||||
import { shareModalStrings } from '../../dashboard_strings';
|
||||
import { DashboardAppLocatorParams, DASHBOARD_APP_LOCATOR } from '../../locator';
|
||||
import { TimeRange } from '../../services/data';
|
||||
import { ViewMode } from '../../services/embeddable';
|
||||
import { setStateToKbnUrl, unhashUrl } from '../../services/kibana_utils';
|
||||
import { SharePluginStart } from '../../services/share';
|
||||
import { dashboardUrlParams } from '../dashboard_router';
|
||||
import { shareModalStrings } from '../../dashboard_strings';
|
||||
import { DashboardAppCapabilities, DashboardState } from '../../types';
|
||||
import { dashboardUrlParams } from '../dashboard_router';
|
||||
import { stateToRawDashboardState } from '../lib/convert_dashboard_state';
|
||||
|
||||
const showFilterBarId = 'showFilterBar';
|
||||
|
@ -28,6 +32,7 @@ interface ShowShareModalProps {
|
|||
savedDashboard: DashboardSavedObject;
|
||||
currentDashboardState: DashboardState;
|
||||
dashboardCapabilities: DashboardAppCapabilities;
|
||||
timeRange: TimeRange;
|
||||
}
|
||||
|
||||
export const showPublicUrlSwitch = (anonymousUserCapabilities: Capabilities) => {
|
||||
|
@ -46,6 +51,7 @@ export function ShowShareModal({
|
|||
savedDashboard,
|
||||
dashboardCapabilities,
|
||||
currentDashboardState,
|
||||
timeRange,
|
||||
}: ShowShareModalProps) {
|
||||
const EmbedUrlParamExtension = ({
|
||||
setParamValue,
|
||||
|
@ -104,6 +110,25 @@ export function ShowShareModal({
|
|||
);
|
||||
};
|
||||
|
||||
const rawDashboardState = stateToRawDashboardState({
|
||||
state: currentDashboardState,
|
||||
version: kibanaVersion,
|
||||
});
|
||||
|
||||
const locatorParams: DashboardAppLocatorParams = {
|
||||
dashboardId: savedDashboard.id,
|
||||
filters: rawDashboardState.filters,
|
||||
preserveSavedFilters: true,
|
||||
query: rawDashboardState.query,
|
||||
savedQuery: rawDashboardState.savedQuery,
|
||||
useHash: false,
|
||||
panels: rawDashboardState.panels,
|
||||
timeRange,
|
||||
viewMode: ViewMode.VIEW, // For share locators we always load the dashboard in view mode
|
||||
refreshInterval: undefined, // We don't share refresh interval externally
|
||||
options: rawDashboardState.options,
|
||||
};
|
||||
|
||||
share.toggleShareContextMenu({
|
||||
isDirty,
|
||||
anchorElement,
|
||||
|
@ -111,14 +136,24 @@ export function ShowShareModal({
|
|||
allowShortUrl: dashboardCapabilities.createShortUrl,
|
||||
shareableUrl: setStateToKbnUrl(
|
||||
'_a',
|
||||
stateToRawDashboardState({ state: currentDashboardState, version: kibanaVersion }),
|
||||
rawDashboardState,
|
||||
{ useHash: false, storeInHashQuery: true },
|
||||
unhashUrl(window.location.href)
|
||||
),
|
||||
objectId: savedDashboard.id,
|
||||
objectType: 'dashboard',
|
||||
sharingData: {
|
||||
title: savedDashboard.title,
|
||||
title:
|
||||
savedDashboard.title ||
|
||||
i18n.translate('dashboard.share.defaultDashboardTitle', {
|
||||
defaultMessage: 'Dashboard [{date}]',
|
||||
values: { date: moment().toISOString(true) },
|
||||
}),
|
||||
locatorParams: {
|
||||
id: DASHBOARD_APP_LOCATOR,
|
||||
version: kibanaVersion,
|
||||
params: locatorParams,
|
||||
},
|
||||
},
|
||||
embedUrlParamExtensions: [
|
||||
{
|
||||
|
|
|
@ -17,7 +17,7 @@ describe('dashboard locator', () => {
|
|||
hashedItemStore.storage = mockStorage;
|
||||
});
|
||||
|
||||
test('creates a link to a saved dashboard', async () => {
|
||||
test('creates a link to an unsaved dashboard', async () => {
|
||||
const definition = new DashboardAppLocatorDefinition({
|
||||
useHashedUrl: false,
|
||||
getDashboardFilterFields: async (dashboardId: string) => [],
|
||||
|
@ -26,7 +26,7 @@ describe('dashboard locator', () => {
|
|||
|
||||
expect(location).toMatchObject({
|
||||
app: 'dashboards',
|
||||
path: '#/create?_a=()&_g=()',
|
||||
path: '#/create?_g=()',
|
||||
state: {},
|
||||
});
|
||||
});
|
||||
|
@ -42,8 +42,14 @@ describe('dashboard locator', () => {
|
|||
|
||||
expect(location).toMatchObject({
|
||||
app: 'dashboards',
|
||||
path: '#/create?_a=()&_g=(time:(from:now-15m,mode:relative,to:now))',
|
||||
state: {},
|
||||
path: '#/create?_g=(time:(from:now-15m,mode:relative,to:now))',
|
||||
state: {
|
||||
timeRange: {
|
||||
from: 'now-15m',
|
||||
mode: 'relative',
|
||||
to: 'now',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -82,8 +88,47 @@ describe('dashboard locator', () => {
|
|||
|
||||
expect(location).toMatchObject({
|
||||
app: 'dashboards',
|
||||
path: `#/view/123?_a=(filters:!((meta:(alias:!n,disabled:!f,negate:!f),query:(query:hi))),query:(language:kuery,query:bye))&_g=(filters:!(('$state':(store:globalState),meta:(alias:!n,disabled:!f,negate:!f),query:(query:hi))),refreshInterval:(pause:!f,value:300),time:(from:now-15m,mode:relative,to:now))`,
|
||||
state: {},
|
||||
path: `#/view/123?_g=(filters:!(('$state':(store:globalState),meta:(alias:!n,disabled:!f,negate:!f),query:(query:hi))),refreshInterval:(pause:!f,value:300),time:(from:now-15m,mode:relative,to:now))`,
|
||||
state: {
|
||||
filters: [
|
||||
{
|
||||
meta: {
|
||||
alias: null,
|
||||
disabled: false,
|
||||
negate: false,
|
||||
},
|
||||
query: {
|
||||
query: 'hi',
|
||||
},
|
||||
},
|
||||
{
|
||||
$state: {
|
||||
store: 'globalState',
|
||||
},
|
||||
meta: {
|
||||
alias: null,
|
||||
disabled: false,
|
||||
negate: false,
|
||||
},
|
||||
query: {
|
||||
query: 'hi',
|
||||
},
|
||||
},
|
||||
],
|
||||
query: {
|
||||
language: 'kuery',
|
||||
query: 'bye',
|
||||
},
|
||||
refreshInterval: {
|
||||
pause: false,
|
||||
value: 300,
|
||||
},
|
||||
timeRange: {
|
||||
from: 'now-15m',
|
||||
mode: 'relative',
|
||||
to: 'now',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -103,8 +148,23 @@ describe('dashboard locator', () => {
|
|||
|
||||
expect(location).toMatchObject({
|
||||
app: 'dashboards',
|
||||
path: `#/view/123?_a=(filters:!(),query:(language:kuery,query:bye))&_g=(filters:!(),refreshInterval:(pause:!f,value:300),time:(from:now-15m,mode:relative,to:now))&searchSessionId=__sessionSearchId__`,
|
||||
state: {},
|
||||
path: `#/view/123?_g=(filters:!(),refreshInterval:(pause:!f,value:300),time:(from:now-15m,mode:relative,to:now))&searchSessionId=__sessionSearchId__`,
|
||||
state: {
|
||||
filters: [],
|
||||
query: {
|
||||
language: 'kuery',
|
||||
query: 'bye',
|
||||
},
|
||||
refreshInterval: {
|
||||
pause: false,
|
||||
value: 300,
|
||||
},
|
||||
timeRange: {
|
||||
from: 'now-15m',
|
||||
mode: 'relative',
|
||||
to: 'now',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -119,10 +179,11 @@ describe('dashboard locator', () => {
|
|||
|
||||
expect(location).toMatchObject({
|
||||
app: 'dashboards',
|
||||
path: `#/create?_a=(savedQuery:__savedQueryId__)&_g=()`,
|
||||
state: {},
|
||||
path: `#/create?_g=()`,
|
||||
state: {
|
||||
savedQuery: '__savedQueryId__',
|
||||
},
|
||||
});
|
||||
expect(location.path).toContain('__savedQueryId__');
|
||||
});
|
||||
|
||||
test('panels', async () => {
|
||||
|
@ -136,8 +197,10 @@ describe('dashboard locator', () => {
|
|||
|
||||
expect(location).toMatchObject({
|
||||
app: 'dashboards',
|
||||
path: `#/create?_a=(panels:!((fakePanelContent:fakePanelContent)))&_g=()`,
|
||||
state: {},
|
||||
path: `#/create?_g=()`,
|
||||
state: {
|
||||
panels: [{ fakePanelContent: 'fakePanelContent' }],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -224,16 +287,62 @@ describe('dashboard locator', () => {
|
|||
filters: [appliedFilter],
|
||||
});
|
||||
|
||||
expect(location1.path).toEqual(expect.stringContaining('query:savedfilter1'));
|
||||
expect(location1.path).toEqual(expect.stringContaining('query:appliedfilter'));
|
||||
expect(location1.path).toMatchInlineSnapshot(`"#/view/dashboard1?_g=(filters:!())"`);
|
||||
expect(location1.state).toMatchObject({
|
||||
filters: [
|
||||
{
|
||||
meta: {
|
||||
alias: null,
|
||||
disabled: false,
|
||||
negate: false,
|
||||
},
|
||||
query: {
|
||||
query: 'savedfilter1',
|
||||
},
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
alias: null,
|
||||
disabled: false,
|
||||
negate: false,
|
||||
},
|
||||
query: {
|
||||
query: 'appliedfilter',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const location2 = await definition.getLocation({
|
||||
dashboardId: 'dashboard2',
|
||||
filters: [appliedFilter],
|
||||
});
|
||||
|
||||
expect(location2.path).toEqual(expect.stringContaining('query:savedfilter2'));
|
||||
expect(location2.path).toEqual(expect.stringContaining('query:appliedfilter'));
|
||||
expect(location2.path).toMatchInlineSnapshot(`"#/view/dashboard2?_g=(filters:!())"`);
|
||||
expect(location2.state).toMatchObject({
|
||||
filters: [
|
||||
{
|
||||
meta: {
|
||||
alias: null,
|
||||
disabled: false,
|
||||
negate: false,
|
||||
},
|
||||
query: {
|
||||
query: 'savedfilter2',
|
||||
},
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
alias: null,
|
||||
disabled: false,
|
||||
negate: false,
|
||||
},
|
||||
query: {
|
||||
query: 'appliedfilter',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test("doesn't fail if can't retrieve filters from destination dashboard", async () => {
|
||||
|
@ -252,8 +361,21 @@ describe('dashboard locator', () => {
|
|||
filters: [appliedFilter],
|
||||
});
|
||||
|
||||
expect(location.path).not.toEqual(expect.stringContaining('query:savedfilter1'));
|
||||
expect(location.path).toEqual(expect.stringContaining('query:appliedfilter'));
|
||||
expect(location.path).toMatchInlineSnapshot(`"#/view/dashboard1?_g=(filters:!())"`);
|
||||
expect(location.state).toMatchObject({
|
||||
filters: [
|
||||
{
|
||||
meta: {
|
||||
alias: null,
|
||||
disabled: false,
|
||||
negate: false,
|
||||
},
|
||||
query: {
|
||||
query: 'appliedfilter',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('can enforce empty filters', async () => {
|
||||
|
@ -273,11 +395,10 @@ describe('dashboard locator', () => {
|
|||
preserveSavedFilters: false,
|
||||
});
|
||||
|
||||
expect(location.path).not.toEqual(expect.stringContaining('query:savedfilter1'));
|
||||
expect(location.path).not.toEqual(expect.stringContaining('query:appliedfilter'));
|
||||
expect(location.path).toMatchInlineSnapshot(
|
||||
`"#/view/dashboard1?_a=(filters:!())&_g=(filters:!())"`
|
||||
);
|
||||
expect(location.path).toMatchInlineSnapshot(`"#/view/dashboard1?_g=(filters:!())"`);
|
||||
expect(location.state).toMatchObject({
|
||||
filters: [],
|
||||
});
|
||||
});
|
||||
|
||||
test('no filters in result url if no filters applied', async () => {
|
||||
|
@ -295,8 +416,8 @@ describe('dashboard locator', () => {
|
|||
dashboardId: 'dashboard1',
|
||||
});
|
||||
|
||||
expect(location.path).not.toEqual(expect.stringContaining('filters'));
|
||||
expect(location.path).toMatchInlineSnapshot(`"#/view/dashboard1?_a=()&_g=()"`);
|
||||
expect(location.path).toMatchInlineSnapshot(`"#/view/dashboard1?_g=()"`);
|
||||
expect(location.state).toMatchObject({});
|
||||
});
|
||||
|
||||
test('can turn off preserving filters', async () => {
|
||||
|
@ -316,8 +437,21 @@ describe('dashboard locator', () => {
|
|||
preserveSavedFilters: false,
|
||||
});
|
||||
|
||||
expect(location.path).not.toEqual(expect.stringContaining('query:savedfilter1'));
|
||||
expect(location.path).toEqual(expect.stringContaining('query:appliedfilter'));
|
||||
expect(location.path).toMatchInlineSnapshot(`"#/view/dashboard1?_g=(filters:!())"`);
|
||||
expect(location.state).toMatchObject({
|
||||
filters: [
|
||||
{
|
||||
meta: {
|
||||
alias: null,
|
||||
disabled: false,
|
||||
negate: false,
|
||||
},
|
||||
query: {
|
||||
query: 'appliedfilter',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,14 +7,21 @@
|
|||
*/
|
||||
|
||||
import type { SerializableRecord } from '@kbn/utility-types';
|
||||
import { flow } from 'lodash';
|
||||
import type { TimeRange, Filter, Query, QueryState, RefreshInterval } from '../../data/public';
|
||||
import type { LocatorDefinition, LocatorPublic } from '../../share/public';
|
||||
import type { SavedDashboardPanel } from '../common/types';
|
||||
import type { RawDashboardState } from './types';
|
||||
import { esFilters } from '../../data/public';
|
||||
import { setStateToKbnUrl } from '../../kibana_utils/public';
|
||||
import { ViewMode } from '../../embeddable/public';
|
||||
import { DashboardConstants } from './dashboard_constants';
|
||||
|
||||
/**
|
||||
* Useful for ensuring that we don't pass any non-serializable values to history.push (for example, functions).
|
||||
*/
|
||||
const getSerializableRecord: <O>(o: O) => O & SerializableRecord = flow(JSON.stringify, JSON.parse);
|
||||
|
||||
const cleanEmptyKeys = (stateObj: Record<string, unknown>) => {
|
||||
Object.keys(stateObj).forEach((key) => {
|
||||
if (stateObj[key] === undefined) {
|
||||
|
@ -26,7 +33,12 @@ const cleanEmptyKeys = (stateObj: Record<string, unknown>) => {
|
|||
|
||||
export const DASHBOARD_APP_LOCATOR = 'DASHBOARD_APP_LOCATOR';
|
||||
|
||||
export interface DashboardAppLocatorParams extends SerializableRecord {
|
||||
/**
|
||||
* We use `type` instead of `interface` to avoid having to extend this type with
|
||||
* `SerializableRecord`. See https://github.com/microsoft/TypeScript/issues/15300.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||
export type DashboardAppLocatorParams = {
|
||||
/**
|
||||
* If given, the dashboard saved object with this id will be loaded. If not given,
|
||||
* a new, unsaved dashboard will be loaded up.
|
||||
|
@ -40,7 +52,7 @@ export interface DashboardAppLocatorParams extends SerializableRecord {
|
|||
/**
|
||||
* Optionally set the refresh interval.
|
||||
*/
|
||||
refreshInterval?: RefreshInterval & SerializableRecord;
|
||||
refreshInterval?: RefreshInterval;
|
||||
|
||||
/**
|
||||
* Optionally apply filers. NOTE: if given and used in conjunction with `dashboardId`, and the
|
||||
|
@ -80,13 +92,15 @@ export interface DashboardAppLocatorParams extends SerializableRecord {
|
|||
/**
|
||||
* List of dashboard panels
|
||||
*/
|
||||
panels?: SavedDashboardPanel[] & SerializableRecord;
|
||||
panels?: SavedDashboardPanel[];
|
||||
|
||||
/**
|
||||
* Saved query ID
|
||||
*/
|
||||
savedQuery?: string;
|
||||
}
|
||||
|
||||
options?: RawDashboardState['options'];
|
||||
};
|
||||
|
||||
export type DashboardAppLocator = LocatorPublic<DashboardAppLocatorParams>;
|
||||
|
||||
|
@ -95,17 +109,29 @@ export interface DashboardAppLocatorDependencies {
|
|||
getDashboardFilterFields: (dashboardId: string) => Promise<Filter[]>;
|
||||
}
|
||||
|
||||
export type ForwardedDashboardState = Omit<
|
||||
DashboardAppLocatorParams,
|
||||
'dashboardId' | 'preserveSavedFilters' | 'useHash' | 'searchSessionId'
|
||||
>;
|
||||
|
||||
export class DashboardAppLocatorDefinition implements LocatorDefinition<DashboardAppLocatorParams> {
|
||||
public readonly id = DASHBOARD_APP_LOCATOR;
|
||||
|
||||
constructor(protected readonly deps: DashboardAppLocatorDependencies) {}
|
||||
|
||||
public readonly getLocation = async (params: DashboardAppLocatorParams) => {
|
||||
const useHash = params.useHash ?? this.deps.useHashedUrl;
|
||||
const hash = params.dashboardId ? `view/${params.dashboardId}` : `create`;
|
||||
const {
|
||||
filters,
|
||||
useHash: paramsUseHash,
|
||||
preserveSavedFilters,
|
||||
dashboardId,
|
||||
...restParams
|
||||
} = params;
|
||||
const useHash = paramsUseHash ?? this.deps.useHashedUrl;
|
||||
const hash = dashboardId ? `view/${dashboardId}` : `create`;
|
||||
|
||||
const getSavedFiltersFromDestinationDashboardIfNeeded = async (): Promise<Filter[]> => {
|
||||
if (params.preserveSavedFilters === false) return [];
|
||||
if (preserveSavedFilters === false) return [];
|
||||
if (!params.dashboardId) return [];
|
||||
try {
|
||||
return await this.deps.getDashboardFilterFields(params.dashboardId);
|
||||
|
@ -116,26 +142,16 @@ export class DashboardAppLocatorDefinition implements LocatorDefinition<Dashboar
|
|||
}
|
||||
};
|
||||
|
||||
const state: ForwardedDashboardState = restParams;
|
||||
|
||||
// leave filters `undefined` if no filters was applied
|
||||
// in this case dashboard will restore saved filters on its own
|
||||
const filters = params.filters && [
|
||||
state.filters = params.filters && [
|
||||
...(await getSavedFiltersFromDestinationDashboardIfNeeded()),
|
||||
...params.filters,
|
||||
];
|
||||
|
||||
let path = setStateToKbnUrl(
|
||||
'_a',
|
||||
cleanEmptyKeys({
|
||||
query: params.query,
|
||||
filters: filters?.filter((f) => !esFilters.isFilterPinned(f)),
|
||||
viewMode: params.viewMode,
|
||||
panels: params.panels,
|
||||
savedQuery: params.savedQuery,
|
||||
}),
|
||||
{ useHash },
|
||||
`#/${hash}`
|
||||
);
|
||||
|
||||
let path = `#/${hash}`;
|
||||
path = setStateToKbnUrl<QueryState>(
|
||||
'_g',
|
||||
cleanEmptyKeys({
|
||||
|
@ -154,7 +170,7 @@ export class DashboardAppLocatorDefinition implements LocatorDefinition<Dashboar
|
|||
return {
|
||||
app: DashboardConstants.DASHBOARDS_ID,
|
||||
path,
|
||||
state: {},
|
||||
state: getSerializableRecord(cleanEmptyKeys(state)),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import { SavedObjectLoader, SavedObjectsStart } from './services/saved_objects';
|
|||
import { IKbnUrlStateStorage } from './services/kibana_utils';
|
||||
import { DashboardContainer, DashboardSavedObject } from '.';
|
||||
import { VisualizationsStart } from '../../visualizations/public';
|
||||
import { DashboardAppLocatorParams } from './locator';
|
||||
|
||||
export { SavedDashboardPanel };
|
||||
|
||||
|
@ -123,6 +124,8 @@ export type DashboardBuildContext = Pick<
|
|||
search: DashboardAppServices['data']['search'];
|
||||
notifications: DashboardAppServices['core']['notifications'];
|
||||
|
||||
locatorState?: DashboardAppLocatorParams;
|
||||
|
||||
history: History;
|
||||
kibanaVersion: string;
|
||||
isEmbeddedExternally: boolean;
|
||||
|
@ -135,11 +138,12 @@ export type DashboardBuildContext = Pick<
|
|||
executionContext?: KibanaExecutionContext;
|
||||
};
|
||||
|
||||
export interface DashboardOptions {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||
export type DashboardOptions = {
|
||||
hidePanelTitles: boolean;
|
||||
useMargins: boolean;
|
||||
syncColors: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export type DashboardRedirect = (props: RedirectToProps) => void;
|
||||
export type RedirectToProps =
|
||||
|
|
|
@ -6,14 +6,15 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { Moment } from 'moment';
|
||||
import type { Moment } from 'moment';
|
||||
|
||||
export interface RefreshInterval {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||
export type RefreshInterval = {
|
||||
pause: boolean;
|
||||
value: number;
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||
export type TimeRange = {
|
||||
from: string;
|
||||
to: string;
|
||||
|
|
|
@ -1955,12 +1955,10 @@ export interface Reason {
|
|||
// Warning: (ae-missing-release-tag) "RefreshInterval" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface RefreshInterval {
|
||||
// (undocumented)
|
||||
export type RefreshInterval = {
|
||||
pause: boolean;
|
||||
// (undocumented)
|
||||
value: number;
|
||||
}
|
||||
};
|
||||
|
||||
// Warning: (ae-missing-release-tag) "SavedQuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
|
|
|
@ -88,6 +88,7 @@ describe('.execute() & getHref', () => {
|
|||
useHashedUrl: false,
|
||||
getDashboardFilterFields: async () => [],
|
||||
});
|
||||
const getLocationSpy = jest.spyOn(definition, 'getLocation');
|
||||
const drilldown = new EmbeddableToDashboardDrilldown({
|
||||
start: ((() => ({
|
||||
core: {
|
||||
|
@ -147,9 +148,14 @@ describe('.execute() & getHref', () => {
|
|||
|
||||
return {
|
||||
href,
|
||||
getLocationSpy,
|
||||
};
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('navigates to correct dashboard', async () => {
|
||||
const testDashboardId = 'dashboardId';
|
||||
const { href } = await setupTestBed(
|
||||
|
@ -183,7 +189,7 @@ describe('.execute() & getHref', () => {
|
|||
test('navigates with query if filters are enabled', async () => {
|
||||
const queryString = 'querystring';
|
||||
const queryLanguage = 'kuery';
|
||||
const { href } = await setupTestBed(
|
||||
const { getLocationSpy } = await setupTestBed(
|
||||
{
|
||||
useCurrentFilters: true,
|
||||
},
|
||||
|
@ -193,8 +199,12 @@ describe('.execute() & getHref', () => {
|
|||
[]
|
||||
);
|
||||
|
||||
expect(href).toEqual(expect.stringContaining(queryString));
|
||||
expect(href).toEqual(expect.stringContaining(queryLanguage));
|
||||
const {
|
||||
state: { query },
|
||||
} = await getLocationSpy.mock.results[0].value;
|
||||
|
||||
expect(query.query).toBe(queryString);
|
||||
expect(query.language).toBe(queryLanguage);
|
||||
});
|
||||
|
||||
test('when user chooses to keep current filters, current filters are set on destination dashboard', async () => {
|
||||
|
@ -202,7 +212,7 @@ describe('.execute() & getHref', () => {
|
|||
const existingGlobalFilterKey = 'existingGlobalFilter';
|
||||
const newAppliedFilterKey = 'newAppliedFilter';
|
||||
|
||||
const { href } = await setupTestBed(
|
||||
const { getLocationSpy } = await setupTestBed(
|
||||
{
|
||||
useCurrentFilters: true,
|
||||
},
|
||||
|
@ -212,9 +222,16 @@ describe('.execute() & getHref', () => {
|
|||
[getFilter(false, newAppliedFilterKey)]
|
||||
);
|
||||
|
||||
expect(href).toEqual(expect.stringContaining(existingAppFilterKey));
|
||||
expect(href).toEqual(expect.stringContaining(existingGlobalFilterKey));
|
||||
expect(href).toEqual(expect.stringContaining(newAppliedFilterKey));
|
||||
const {
|
||||
state: { filters },
|
||||
} = await getLocationSpy.mock.results[0].value;
|
||||
|
||||
expect(filters.length).toBe(3);
|
||||
|
||||
const filtersString = JSON.stringify(filters);
|
||||
expect(filtersString).toEqual(expect.stringContaining(existingAppFilterKey));
|
||||
expect(filtersString).toEqual(expect.stringContaining(existingGlobalFilterKey));
|
||||
expect(filtersString).toEqual(expect.stringContaining(newAppliedFilterKey));
|
||||
});
|
||||
|
||||
test('when user chooses to remove current filters, current app filters are remove on destination dashboard', async () => {
|
||||
|
@ -222,7 +239,7 @@ describe('.execute() & getHref', () => {
|
|||
const existingGlobalFilterKey = 'existingGlobalFilter';
|
||||
const newAppliedFilterKey = 'newAppliedFilter';
|
||||
|
||||
const { href } = await setupTestBed(
|
||||
const { getLocationSpy } = await setupTestBed(
|
||||
{
|
||||
useCurrentFilters: false,
|
||||
},
|
||||
|
@ -232,9 +249,16 @@ describe('.execute() & getHref', () => {
|
|||
[getFilter(false, newAppliedFilterKey)]
|
||||
);
|
||||
|
||||
expect(href).not.toEqual(expect.stringContaining(existingAppFilterKey));
|
||||
expect(href).toEqual(expect.stringContaining(existingGlobalFilterKey));
|
||||
expect(href).toEqual(expect.stringContaining(newAppliedFilterKey));
|
||||
const {
|
||||
state: { filters },
|
||||
} = await getLocationSpy.mock.results[0].value;
|
||||
|
||||
expect(filters.length).toBe(2);
|
||||
|
||||
const filtersString = JSON.stringify(filters);
|
||||
expect(filtersString).not.toEqual(expect.stringContaining(existingAppFilterKey));
|
||||
expect(filtersString).toEqual(expect.stringContaining(existingGlobalFilterKey));
|
||||
expect(filtersString).toEqual(expect.stringContaining(newAppliedFilterKey));
|
||||
});
|
||||
|
||||
test('when user chooses to keep current time range, current time range is passed in url', async () => {
|
||||
|
|
|
@ -8,4 +8,4 @@
|
|||
// TODO: Remove this code once everyone is using the new PDF format, then we can also remove the legacy
|
||||
// export type entirely
|
||||
export const isJobV2Params = ({ sharingData }: { sharingData: Record<string, unknown> }): boolean =>
|
||||
Array.isArray(sharingData.locatorParams);
|
||||
sharingData.locatorParams != null;
|
||||
|
|
|
@ -127,6 +127,7 @@ export const reportingScreenshotShareProvider = ({
|
|||
};
|
||||
|
||||
const isV2Job = isJobV2Params(jobProviderOptions);
|
||||
const requiresSavedState = !isV2Job;
|
||||
|
||||
const pngReportType = isV2Job ? 'pngV2' : 'png';
|
||||
|
||||
|
@ -149,7 +150,7 @@ export const reportingScreenshotShareProvider = ({
|
|||
uiSettings={uiSettings}
|
||||
reportType={pngReportType}
|
||||
objectId={objectId}
|
||||
requiresSavedState={true}
|
||||
requiresSavedState={requiresSavedState}
|
||||
getJobParams={getJobParams(apiClient, jobProviderOptions, pngReportType)}
|
||||
isDirty={isDirty}
|
||||
onClose={onClose}
|
||||
|
@ -183,7 +184,7 @@ export const reportingScreenshotShareProvider = ({
|
|||
uiSettings={uiSettings}
|
||||
reportType={pdfReportType}
|
||||
objectId={objectId}
|
||||
requiresSavedState={true}
|
||||
requiresSavedState={requiresSavedState}
|
||||
layoutOption={objectType === 'dashboard' ? 'print' : undefined}
|
||||
getJobParams={getJobParams(apiClient, jobProviderOptions, pdfReportType)}
|
||||
isDirty={isDirty}
|
||||
|
|
|
@ -104,6 +104,10 @@ class ReportingPanelContentUi extends Component<Props, State> {
|
|||
window.addEventListener('resize', this.setAbsoluteReportGenerationUrl);
|
||||
}
|
||||
|
||||
private isNotSaved = () => {
|
||||
return this.props.objectId === undefined || this.props.objectId === '';
|
||||
};
|
||||
|
||||
public render() {
|
||||
if (
|
||||
this.props.requiresSavedState &&
|
||||
|
@ -226,10 +230,6 @@ class ReportingPanelContentUi extends Component<Props, State> {
|
|||
this.setState({ isStale: true });
|
||||
};
|
||||
|
||||
private isNotSaved = () => {
|
||||
return this.props.objectId === undefined || this.props.objectId === '';
|
||||
};
|
||||
|
||||
private setAbsoluteReportGenerationUrl = () => {
|
||||
if (!this.mounted) {
|
||||
return;
|
||||
|
|
|
@ -74,15 +74,15 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
describe('Print PDF button', () => {
|
||||
it('is not available if new', async () => {
|
||||
it('is available if new', async () => {
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
await PageObjects.dashboard.clickNewDashboard();
|
||||
await PageObjects.reporting.openPdfReportingPanel();
|
||||
expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be('true');
|
||||
expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be(null);
|
||||
await (await testSubjects.find('kibanaChrome')).clickMouseButton(); // close popover
|
||||
});
|
||||
|
||||
it('becomes available when saved', async () => {
|
||||
it('is available when saved', async () => {
|
||||
await PageObjects.dashboard.saveDashboard('My PDF Dashboard');
|
||||
await PageObjects.reporting.openPdfReportingPanel();
|
||||
expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be(null);
|
||||
|
@ -109,15 +109,15 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
describe('Print PNG button', () => {
|
||||
it('is not available if new', async () => {
|
||||
it('is available if new', async () => {
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
await PageObjects.dashboard.clickNewDashboard();
|
||||
await PageObjects.reporting.openPngReportingPanel();
|
||||
expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be('true');
|
||||
expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be(null);
|
||||
await (await testSubjects.find('kibanaChrome')).clickMouseButton(); // close popover
|
||||
});
|
||||
|
||||
it('becomes available when saved', async () => {
|
||||
it('is available when saved', async () => {
|
||||
await PageObjects.dashboard.saveDashboard('My PNG Dash');
|
||||
await PageObjects.reporting.openPngReportingPanel();
|
||||
expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be(null);
|
||||
|
|
Loading…
Reference in a new issue