mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[Reporting] fix dashboard "Copy Post URL" action (#192530)
## Summary Closes https://github.com/elastic/kibana/issues/191673 Closes https://github.com/elastic/kibana/issues/183566 Fixes the ability for the POST URL used to automate generation of reports by adding a `generateExportUrl` function to the ShareMenuItemV2 interface. This function returns a dynamic export URL for PDF generation by using the selected layout option. Other changes: provides more strictness in type definitions by: * splitting the types that define `ShareMenuProvider`: * `ShareMenuProviderV2` provides the `getShareMenuItems` function * `ShareMenuProviderLegacy` provides the `getShareMenuItemsLegacy` function ### Release note Fixed an issue with the export options for PNG/PDF reports in a dashboard. ### Checklist Delete any items that are not applicable to this PR. - [x] Use the `generateExportUrl` function inputs to return a POST URL that is aware of the layout mode (`print` or `preserve_layout`) and screen dimensions - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] Flaky test runner: https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6986
This commit is contained in:
parent
28d6a22263
commit
38407ae6b0
22 changed files with 220 additions and 257 deletions
|
@ -22,7 +22,7 @@ export class ShareDemoPlugin implements Plugin<void, void, SetupDeps, StartDeps>
|
||||||
public setup(core: CoreSetup<StartDeps>, { share }: SetupDeps) {
|
public setup(core: CoreSetup<StartDeps>, { share }: SetupDeps) {
|
||||||
share.register({
|
share.register({
|
||||||
id: 'demo',
|
id: 'demo',
|
||||||
getShareMenuItems: (context) => [
|
getShareMenuItemsLegacy: (context) => [
|
||||||
{
|
{
|
||||||
panel: {
|
panel: {
|
||||||
id: 'demo',
|
id: 'demo',
|
||||||
|
|
|
@ -227,6 +227,9 @@ export class ReportingAPIClient implements IReportingAPI {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the browserTimezone and kibana version to report job params
|
||||||
|
*/
|
||||||
public getDecoratedJobParams<T extends AppParams>(baseParams: T): BaseParams {
|
public getDecoratedJobParams<T extends AppParams>(baseParams: T): BaseParams {
|
||||||
// If the TZ is set to the default "Browser", it will not be useful for
|
// If the TZ is set to the default "Browser", it will not be useful for
|
||||||
// server-side export. We need to derive the timezone and pass it as a param
|
// server-side export. We need to derive the timezone and pass it as a param
|
||||||
|
|
|
@ -11,7 +11,6 @@ import * as Rx from 'rxjs';
|
||||||
|
|
||||||
import type { ApplicationStart, CoreStart } from '@kbn/core/public';
|
import type { ApplicationStart, CoreStart } from '@kbn/core/public';
|
||||||
import { ILicense } from '@kbn/licensing-plugin/public';
|
import { ILicense } from '@kbn/licensing-plugin/public';
|
||||||
import type { LayoutParams } from '@kbn/screenshotting-plugin/common';
|
|
||||||
|
|
||||||
import type { ReportingAPIClient } from '../../reporting_api_client';
|
import type { ReportingAPIClient } from '../../reporting_api_client';
|
||||||
|
|
||||||
|
@ -47,13 +46,16 @@ export interface ExportPanelShareOpts {
|
||||||
|
|
||||||
export interface ReportingSharingData {
|
export interface ReportingSharingData {
|
||||||
title: string;
|
title: string;
|
||||||
layout: LayoutParams;
|
|
||||||
reportingDisabled?: boolean;
|
reportingDisabled?: boolean;
|
||||||
[key: string]: unknown;
|
locatorParams: {
|
||||||
|
id: string;
|
||||||
|
params: unknown;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JobParamsProviderOptions {
|
export interface JobParamsProviderOptions {
|
||||||
sharingData: ReportingSharingData;
|
sharingData: ReportingSharingData;
|
||||||
shareableUrl?: string;
|
shareableUrl?: string;
|
||||||
objectType: string;
|
objectType: string;
|
||||||
|
optimizedForPrinting?: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { CSV_JOB_TYPE, CSV_JOB_TYPE_V2 } from '@kbn/reporting-export-types-csv-c
|
||||||
|
|
||||||
import type { SearchSourceFields } from '@kbn/data-plugin/common';
|
import type { SearchSourceFields } from '@kbn/data-plugin/common';
|
||||||
import { FormattedMessage, InjectedIntl } from '@kbn/i18n-react';
|
import { FormattedMessage, InjectedIntl } from '@kbn/i18n-react';
|
||||||
import { ShareContext, ShareMenuItem } from '@kbn/share-plugin/public';
|
import { ShareContext, ShareMenuItemV2 } from '@kbn/share-plugin/public';
|
||||||
import type { ExportModalShareOpts } from '.';
|
import type { ExportModalShareOpts } from '.';
|
||||||
import { checkLicense } from '../..';
|
import { checkLicense } from '../..';
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ export const reportingCsvShareProvider = ({
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const shareActions: ShareMenuItem[] = [];
|
const shareActions: ShareMenuItemV2[] = [];
|
||||||
|
|
||||||
const licenseCheck = checkLicense(license.check('reporting', 'basic'));
|
const licenseCheck = checkLicense(license.check('reporting', 'basic'));
|
||||||
const licenseToolTipContent = licenseCheck.message;
|
const licenseToolTipContent = licenseCheck.message;
|
||||||
|
@ -177,8 +177,8 @@ export const reportingCsvShareProvider = ({
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
generateExport: generateReportingJobCSV,
|
generateExport: generateReportingJobCSV,
|
||||||
|
generateExportUrl: () => absoluteUrl,
|
||||||
generateCopyUrl: reportingUrl,
|
generateCopyUrl: reportingUrl,
|
||||||
absoluteUrl,
|
|
||||||
renderCopyURLButton: true,
|
renderCopyURLButton: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,31 +8,28 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { FormattedMessage, InjectedIntl } from '@kbn/i18n-react';
|
import { FormattedMessage } from '@kbn/i18n-react';
|
||||||
import { toMountPoint } from '@kbn/react-kibana-mount';
|
import { toMountPoint } from '@kbn/react-kibana-mount';
|
||||||
import { ShareContext, ShareMenuItem, ShareMenuProvider } from '@kbn/share-plugin/public';
|
import { ShareContext, ShareMenuItemV2, ShareMenuProvider } from '@kbn/share-plugin/public';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import {
|
import { ScreenshotExportOpts } from '@kbn/share-plugin/public/types';
|
||||||
ExportModalShareOpts,
|
import { ExportModalShareOpts, JobParamsProviderOptions, ReportingSharingData } from '.';
|
||||||
ExportPanelShareOpts,
|
|
||||||
JobParamsProviderOptions,
|
|
||||||
ReportingSharingData,
|
|
||||||
} from '.';
|
|
||||||
import { checkLicense } from '../../license_check';
|
import { checkLicense } from '../../license_check';
|
||||||
import { ScreenCapturePanelContent } from './screen_capture_panel_content_lazy';
|
|
||||||
|
|
||||||
const getJobParams = (opts: JobParamsProviderOptions, type: 'pngV2' | 'printablePdfV2') => () => {
|
const getJobParams = (opts: JobParamsProviderOptions, type: 'pngV2' | 'printablePdfV2') => () => {
|
||||||
const {
|
const {
|
||||||
objectType,
|
objectType,
|
||||||
sharingData: { title, layout, locatorParams },
|
sharingData: { title, locatorParams },
|
||||||
|
optimizedForPrinting,
|
||||||
} = opts;
|
} = opts;
|
||||||
|
|
||||||
const baseParams = {
|
const el = document.querySelector('[data-shared-items-container]');
|
||||||
objectType,
|
const { height, width } = el ? el.getBoundingClientRect() : { height: 768, width: 1024 };
|
||||||
layout,
|
const dimensions = { height, width };
|
||||||
title,
|
const layoutId = optimizedForPrinting ? ('print' as const) : ('preserve_layout' as const);
|
||||||
};
|
const layout = { id: layoutId, dimensions };
|
||||||
|
const baseParams = { objectType, layout, title };
|
||||||
|
|
||||||
if (type === 'printablePdfV2') {
|
if (type === 'printablePdfV2') {
|
||||||
// multi locator for PDF V2
|
// multi locator for PDF V2
|
||||||
|
@ -43,154 +40,8 @@ const getJobParams = (opts: JobParamsProviderOptions, type: 'pngV2' | 'printable
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is used by Canvas
|
* This is used by Dashboard and Visualize apps (sharing modal)
|
||||||
*/
|
*/
|
||||||
export const reportingScreenshotShareProvider = ({
|
|
||||||
apiClient,
|
|
||||||
license,
|
|
||||||
application,
|
|
||||||
usesUiCapabilities,
|
|
||||||
startServices$,
|
|
||||||
}: ExportPanelShareOpts): ShareMenuProvider => {
|
|
||||||
const getShareMenuItems = ({
|
|
||||||
objectType,
|
|
||||||
objectId,
|
|
||||||
isDirty,
|
|
||||||
onClose,
|
|
||||||
shareableUrl,
|
|
||||||
shareableUrlForSavedObject,
|
|
||||||
...shareOpts
|
|
||||||
}: ShareContext) => {
|
|
||||||
const { enableLinks, showLinks, message } = checkLicense(license.check('reporting', 'gold'));
|
|
||||||
const licenseToolTipContent = message;
|
|
||||||
const licenseHasScreenshotReporting = showLinks;
|
|
||||||
const licenseDisabled = !enableLinks;
|
|
||||||
|
|
||||||
let capabilityHasDashboardScreenshotReporting = false;
|
|
||||||
let capabilityHasVisualizeScreenshotReporting = false;
|
|
||||||
if (usesUiCapabilities) {
|
|
||||||
capabilityHasDashboardScreenshotReporting =
|
|
||||||
application.capabilities.dashboard?.generateScreenshot === true;
|
|
||||||
capabilityHasVisualizeScreenshotReporting =
|
|
||||||
application.capabilities.visualize?.generateScreenshot === true;
|
|
||||||
} else {
|
|
||||||
// deprecated
|
|
||||||
capabilityHasDashboardScreenshotReporting = true;
|
|
||||||
capabilityHasVisualizeScreenshotReporting = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!licenseHasScreenshotReporting) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const isSupportedType = ['dashboard', 'visualization', 'lens'].includes(objectType);
|
|
||||||
|
|
||||||
if (!isSupportedType) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objectType === 'dashboard' && !capabilityHasDashboardScreenshotReporting) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
isSupportedType &&
|
|
||||||
!capabilityHasVisualizeScreenshotReporting &&
|
|
||||||
!capabilityHasDashboardScreenshotReporting
|
|
||||||
) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const { sharingData } = shareOpts as unknown as { sharingData: ReportingSharingData };
|
|
||||||
const shareActions: ShareMenuItem[] = [];
|
|
||||||
|
|
||||||
const pngPanelTitle = i18n.translate('reporting.share.contextMenu.pngReportsButtonLabel', {
|
|
||||||
defaultMessage: 'PNG Reports',
|
|
||||||
});
|
|
||||||
|
|
||||||
const jobProviderOptions: JobParamsProviderOptions = {
|
|
||||||
shareableUrl: isDirty ? shareableUrl : shareableUrlForSavedObject ?? shareableUrl,
|
|
||||||
objectType,
|
|
||||||
sharingData,
|
|
||||||
};
|
|
||||||
const isJobV2Params = ({
|
|
||||||
sharingData: _sharingData,
|
|
||||||
}: {
|
|
||||||
sharingData: Record<string, unknown>;
|
|
||||||
}) => _sharingData.locatorParams != null;
|
|
||||||
|
|
||||||
const isV2Job = isJobV2Params(jobProviderOptions);
|
|
||||||
const requiresSavedState = !isV2Job;
|
|
||||||
|
|
||||||
const panelPng = {
|
|
||||||
shareMenuItem: {
|
|
||||||
name: pngPanelTitle,
|
|
||||||
icon: 'document',
|
|
||||||
toolTipContent: licenseToolTipContent,
|
|
||||||
disabled: licenseDisabled || sharingData.reportingDisabled,
|
|
||||||
['data-test-subj']: 'PNGReports',
|
|
||||||
sortOrder: 10,
|
|
||||||
},
|
|
||||||
panel: {
|
|
||||||
id: 'reportingPngPanel',
|
|
||||||
title: pngPanelTitle,
|
|
||||||
content: (
|
|
||||||
<ScreenCapturePanelContent
|
|
||||||
apiClient={apiClient}
|
|
||||||
startServices$={startServices$}
|
|
||||||
reportType={'pngV2'}
|
|
||||||
objectId={objectId}
|
|
||||||
requiresSavedState={requiresSavedState}
|
|
||||||
getJobParams={getJobParams(jobProviderOptions, 'pngV2')}
|
|
||||||
isDirty={isDirty}
|
|
||||||
onClose={onClose}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const pdfPanelTitle = i18n.translate('reporting.share.contextMenu.pdfReportsButtonLabel', {
|
|
||||||
defaultMessage: 'PDF Reports',
|
|
||||||
});
|
|
||||||
|
|
||||||
const panelPdf = {
|
|
||||||
shareMenuItem: {
|
|
||||||
name: pdfPanelTitle,
|
|
||||||
icon: 'document',
|
|
||||||
toolTipContent: licenseToolTipContent,
|
|
||||||
disabled: licenseDisabled || sharingData.reportingDisabled,
|
|
||||||
['data-test-subj']: 'PDFReports',
|
|
||||||
sortOrder: 10,
|
|
||||||
},
|
|
||||||
panel: {
|
|
||||||
id: 'reportingPdfPanel',
|
|
||||||
title: pdfPanelTitle,
|
|
||||||
content: (
|
|
||||||
<ScreenCapturePanelContent
|
|
||||||
apiClient={apiClient}
|
|
||||||
startServices$={startServices$}
|
|
||||||
reportType={'printablePdfV2'}
|
|
||||||
objectId={objectId}
|
|
||||||
requiresSavedState={requiresSavedState}
|
|
||||||
layoutOption={objectType === 'dashboard' ? 'print' : undefined}
|
|
||||||
getJobParams={getJobParams(jobProviderOptions, 'printablePdfV2')}
|
|
||||||
isDirty={isDirty}
|
|
||||||
onClose={onClose}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
shareActions.push(panelPng);
|
|
||||||
shareActions.push(panelPdf);
|
|
||||||
return shareActions;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: 'screenCaptureReports',
|
|
||||||
getShareMenuItems,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const reportingExportModalProvider = ({
|
export const reportingExportModalProvider = ({
|
||||||
apiClient,
|
apiClient,
|
||||||
license,
|
license,
|
||||||
|
@ -249,7 +100,7 @@ export const reportingExportModalProvider = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const { sharingData } = shareOpts as unknown as { sharingData: ReportingSharingData };
|
const { sharingData } = shareOpts as unknown as { sharingData: ReportingSharingData };
|
||||||
const shareActions: ShareMenuItem[] = [];
|
const shareActions: ShareMenuItemV2[] = [];
|
||||||
|
|
||||||
const jobProviderOptions: JobParamsProviderOptions = {
|
const jobProviderOptions: JobParamsProviderOptions = {
|
||||||
shareableUrl: isDirty ? shareableUrl : shareableUrlForSavedObject ?? shareableUrl,
|
shareableUrl: isDirty ? shareableUrl : shareableUrlForSavedObject ?? shareableUrl,
|
||||||
|
@ -259,32 +110,9 @@ export const reportingExportModalProvider = ({
|
||||||
|
|
||||||
const requiresSavedState = sharingData.locatorParams === null;
|
const requiresSavedState = sharingData.locatorParams === null;
|
||||||
|
|
||||||
const relativePathPDF = apiClient.getReportingPublicJobPath(
|
const generateReportPDF = ({ intl, optimizedForPrinting = false }: ScreenshotExportOpts) => {
|
||||||
'printablePdfV2',
|
|
||||||
apiClient.getDecoratedJobParams(getJobParams(jobProviderOptions, 'printablePdfV2')())
|
|
||||||
);
|
|
||||||
|
|
||||||
const relativePathPNG = apiClient.getReportingPublicJobPath(
|
|
||||||
'pngV2',
|
|
||||||
apiClient.getDecoratedJobParams(getJobParams(jobProviderOptions, 'pngV2')())
|
|
||||||
);
|
|
||||||
|
|
||||||
const generateReportPDF = ({
|
|
||||||
intl,
|
|
||||||
optimizedForPrinting = false,
|
|
||||||
}: {
|
|
||||||
intl: InjectedIntl;
|
|
||||||
optimizedForPrinting?: boolean;
|
|
||||||
}) => {
|
|
||||||
const el = document.querySelector('[data-shared-items-container]');
|
|
||||||
const { height, width } = el ? el.getBoundingClientRect() : { height: 768, width: 1024 };
|
|
||||||
const dimensions = { height, width };
|
|
||||||
|
|
||||||
const decoratedJobParams = apiClient.getDecoratedJobParams({
|
const decoratedJobParams = apiClient.getDecoratedJobParams({
|
||||||
...getJobParams(jobProviderOptions, 'printablePdfV2')(),
|
...getJobParams({ ...jobProviderOptions, optimizedForPrinting }, 'printablePdfV2')(),
|
||||||
layout: { id: optimizedForPrinting ? 'print' : 'preserve_layout', dimensions },
|
|
||||||
objectType,
|
|
||||||
title: sharingData.title,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return apiClient
|
return apiClient
|
||||||
|
@ -330,19 +158,27 @@ export const reportingExportModalProvider = ({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateReportPNG = ({ intl }: { intl: InjectedIntl }) => {
|
const generateExportUrlPDF = ({ optimizedForPrinting }: ScreenshotExportOpts) => {
|
||||||
const { layout: outerLayout } = getJobParams(jobProviderOptions, 'pngV2')();
|
const jobParams = apiClient.getDecoratedJobParams(
|
||||||
let dimensions = outerLayout?.dimensions;
|
getJobParams({ ...jobProviderOptions, optimizedForPrinting }, 'printablePdfV2')()
|
||||||
if (!dimensions) {
|
);
|
||||||
const el = document.querySelector('[data-shared-items-container]');
|
const relativePathPDF = apiClient.getReportingPublicJobPath('printablePdfV2', jobParams);
|
||||||
const { height, width } = el ? el.getBoundingClientRect() : { height: 768, width: 1024 };
|
|
||||||
dimensions = { height, width };
|
return new URL(relativePathPDF, window.location.href).toString();
|
||||||
}
|
};
|
||||||
|
|
||||||
|
const generateExportUrlPNG = () => {
|
||||||
|
const jobParams = apiClient.getDecoratedJobParams(
|
||||||
|
getJobParams(jobProviderOptions, 'pngV2')()
|
||||||
|
);
|
||||||
|
const relativePathPNG = apiClient.getReportingPublicJobPath('pngV2', jobParams);
|
||||||
|
|
||||||
|
return new URL(relativePathPNG, window.location.href).toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateReportPNG = ({ intl }: ScreenshotExportOpts) => {
|
||||||
const decoratedJobParams = apiClient.getDecoratedJobParams({
|
const decoratedJobParams = apiClient.getDecoratedJobParams({
|
||||||
...getJobParams(jobProviderOptions, 'pngV2')(),
|
...getJobParams(jobProviderOptions, 'pngV2')(),
|
||||||
layout: { id: 'preserve_layout', dimensions },
|
|
||||||
objectType,
|
|
||||||
title: sharingData.title,
|
|
||||||
});
|
});
|
||||||
return apiClient
|
return apiClient
|
||||||
.createReportingJob('pngV2', decoratedJobParams)
|
.createReportingJob('pngV2', decoratedJobParams)
|
||||||
|
@ -398,6 +234,7 @@ export const reportingExportModalProvider = ({
|
||||||
},
|
},
|
||||||
label: 'PDF' as const,
|
label: 'PDF' as const,
|
||||||
generateExport: generateReportPDF,
|
generateExport: generateReportPDF,
|
||||||
|
generateExportUrl: generateExportUrlPDF,
|
||||||
reportType: 'printablePdfV2',
|
reportType: 'printablePdfV2',
|
||||||
requiresSavedState,
|
requiresSavedState,
|
||||||
helpText: (
|
helpText: (
|
||||||
|
@ -415,7 +252,6 @@ export const reportingExportModalProvider = ({
|
||||||
layoutOption: objectType === 'dashboard' ? ('print' as const) : undefined,
|
layoutOption: objectType === 'dashboard' ? ('print' as const) : undefined,
|
||||||
renderLayoutOptionSwitch: objectType === 'dashboard',
|
renderLayoutOptionSwitch: objectType === 'dashboard',
|
||||||
renderCopyURLButton: true,
|
renderCopyURLButton: true,
|
||||||
absoluteUrl: new URL(relativePathPDF, window.location.href).toString(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
shareActions.push({
|
shareActions.push({
|
||||||
|
@ -429,6 +265,7 @@ export const reportingExportModalProvider = ({
|
||||||
},
|
},
|
||||||
label: 'PNG' as const,
|
label: 'PNG' as const,
|
||||||
generateExport: generateReportPNG,
|
generateExport: generateReportPNG,
|
||||||
|
generateExportUrl: generateExportUrlPNG,
|
||||||
reportType: 'pngV2',
|
reportType: 'pngV2',
|
||||||
requiresSavedState,
|
requiresSavedState,
|
||||||
helpText: (
|
helpText: (
|
||||||
|
@ -442,7 +279,6 @@ export const reportingExportModalProvider = ({
|
||||||
),
|
),
|
||||||
layoutOption: objectType === 'dashboard' ? ('print' as const) : undefined,
|
layoutOption: objectType === 'dashboard' ? ('print' as const) : undefined,
|
||||||
renderCopyURLButton: true,
|
renderCopyURLButton: true,
|
||||||
absoluteUrl: new URL(relativePathPNG, window.location.href).toString(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return shareActions;
|
return shareActions;
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { createContext, useContext } from 'react';
|
||||||
|
|
||||||
import { AnonymousAccessServiceContract } from '../../../common';
|
import { AnonymousAccessServiceContract } from '../../../common';
|
||||||
import type {
|
import type {
|
||||||
ShareMenuItem,
|
ShareMenuItemV2,
|
||||||
UrlParamExtension,
|
UrlParamExtension,
|
||||||
BrowserUrlService,
|
BrowserUrlService,
|
||||||
ShareContext,
|
ShareContext,
|
||||||
|
@ -24,7 +24,7 @@ export type { ShareMenuItemV2 } from '../../types';
|
||||||
export interface IShareContext extends ShareContext {
|
export interface IShareContext extends ShareContext {
|
||||||
allowEmbed: boolean;
|
allowEmbed: boolean;
|
||||||
allowShortUrl: boolean;
|
allowShortUrl: boolean;
|
||||||
shareMenuItems: ShareMenuItem[];
|
shareMenuItems: ShareMenuItemV2[];
|
||||||
embedUrlParamExtensions?: UrlParamExtension[];
|
embedUrlParamExtensions?: UrlParamExtension[];
|
||||||
anonymousAccess?: AnonymousAccessServiceContract;
|
anonymousAccess?: AnonymousAccessServiceContract;
|
||||||
urlService: BrowserUrlService;
|
urlService: BrowserUrlService;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ShareMenuItem } from '../types';
|
import { ShareMenuItemLegacy } from '../types';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallow } from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
|
@ -40,7 +40,7 @@ test('should disable the share URL when set', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('shareContextMenuExtensions', () => {
|
describe('shareContextMenuExtensions', () => {
|
||||||
const shareContextMenuItems: ShareMenuItem[] = [
|
const shareContextMenuItems: ShareMenuItemLegacy[] = [
|
||||||
{
|
{
|
||||||
panel: {
|
panel: {
|
||||||
id: '1',
|
id: '1',
|
||||||
|
|
|
@ -17,7 +17,7 @@ import type { Capabilities } from '@kbn/core/public';
|
||||||
|
|
||||||
import type { LocatorPublic } from '../../common';
|
import type { LocatorPublic } from '../../common';
|
||||||
import { UrlPanelContent } from './url_panel_content';
|
import { UrlPanelContent } from './url_panel_content';
|
||||||
import { ShareMenuItem, ShareContextMenuPanelItem, UrlParamExtension } from '../types';
|
import { ShareMenuItemLegacy, ShareContextMenuPanelItem, UrlParamExtension } from '../types';
|
||||||
import { AnonymousAccessServiceContract } from '../../common/anonymous_access';
|
import { AnonymousAccessServiceContract } from '../../common/anonymous_access';
|
||||||
import type { BrowserUrlService } from '../types';
|
import type { BrowserUrlService } from '../types';
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ export interface ShareContextMenuProps {
|
||||||
locator: LocatorPublic<any>;
|
locator: LocatorPublic<any>;
|
||||||
params: any;
|
params: any;
|
||||||
};
|
};
|
||||||
shareMenuItems: ShareMenuItem[];
|
shareMenuItems: ShareMenuItemLegacy[];
|
||||||
sharingData: any;
|
sharingData: any;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
embedUrlParamExtensions?: UrlParamExtension[];
|
embedUrlParamExtensions?: UrlParamExtension[];
|
||||||
|
|
|
@ -62,12 +62,19 @@ const mockShareContext = {
|
||||||
toasts: toastsServiceMock.createStartContract(),
|
toasts: toastsServiceMock.createStartContract(),
|
||||||
i18n: i18nServiceMock.createStartContract(),
|
i18n: i18nServiceMock.createStartContract(),
|
||||||
};
|
};
|
||||||
|
const mockGenerateExport = jest.fn();
|
||||||
|
const mockGenerateExportUrl = jest.fn().mockImplementation(() => 'generated-export-url');
|
||||||
const CSV = 'CSV' as const;
|
const CSV = 'CSV' as const;
|
||||||
const PNG = 'PNG' as const;
|
const PNG = 'PNG' as const;
|
||||||
describe('Share modal tabs', () => {
|
describe('Share modal tabs', () => {
|
||||||
it('should render export tab when there are share menu items that are not disabled', async () => {
|
it('should render export tab when there are share menu items that are not disabled', async () => {
|
||||||
const testItem = [
|
const testItem = [
|
||||||
{ shareMenuItem: { name: 'test', disabled: false }, label: CSV, generateExport: jest.fn() },
|
{
|
||||||
|
shareMenuItem: { name: 'test', disabled: false },
|
||||||
|
label: CSV,
|
||||||
|
generateExport: mockGenerateExport,
|
||||||
|
generateExportUrl: mockGenerateExportUrl,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
const wrapper = mountWithIntl(
|
const wrapper = mountWithIntl(
|
||||||
<ShareTabsContext.Provider value={{ ...mockShareContext, shareMenuItems: testItem }}>
|
<ShareTabsContext.Provider value={{ ...mockShareContext, shareMenuItems: testItem }}>
|
||||||
|
@ -78,7 +85,12 @@ describe('Share modal tabs', () => {
|
||||||
});
|
});
|
||||||
it('should not render export tab when the license is disabled', async () => {
|
it('should not render export tab when the license is disabled', async () => {
|
||||||
const testItems = [
|
const testItems = [
|
||||||
{ shareMenuItem: { name: 'test', disabled: true }, label: CSV, generateExport: jest.fn() },
|
{
|
||||||
|
shareMenuItem: { name: 'test', disabled: true },
|
||||||
|
label: CSV,
|
||||||
|
generateExport: mockGenerateExport,
|
||||||
|
generateExportUrl: mockGenerateExportUrl,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
const wrapper = mountWithIntl(
|
const wrapper = mountWithIntl(
|
||||||
<ShareTabsContext.Provider value={{ ...mockShareContext, shareMenuItems: testItems }}>
|
<ShareTabsContext.Provider value={{ ...mockShareContext, shareMenuItems: testItems }}>
|
||||||
|
@ -90,8 +102,18 @@ describe('Share modal tabs', () => {
|
||||||
|
|
||||||
it('should render export tab is at least one is not disabled', async () => {
|
it('should render export tab is at least one is not disabled', async () => {
|
||||||
const testItem = [
|
const testItem = [
|
||||||
{ shareMenuItem: { name: 'test', disabled: false }, label: CSV, generateExport: jest.fn() },
|
{
|
||||||
{ shareMenuItem: { name: 'test', disabled: true }, label: PNG, generateExport: jest.fn() },
|
shareMenuItem: { name: 'test', disabled: false },
|
||||||
|
label: CSV,
|
||||||
|
generateExport: mockGenerateExport,
|
||||||
|
generateExportUrl: mockGenerateExportUrl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
shareMenuItem: { name: 'test', disabled: true },
|
||||||
|
label: PNG,
|
||||||
|
generateExport: mockGenerateExport,
|
||||||
|
generateExportUrl: mockGenerateExportUrl,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
const wrapper = mountWithIntl(
|
const wrapper = mountWithIntl(
|
||||||
<ShareTabsContext.Provider value={{ ...mockShareContext, shareMenuItems: testItem }}>
|
<ShareTabsContext.Provider value={{ ...mockShareContext, shareMenuItems: testItem }}>
|
||||||
|
|
|
@ -64,7 +64,7 @@ const ExportContentUi = ({
|
||||||
helpText,
|
helpText,
|
||||||
renderCopyURLButton,
|
renderCopyURLButton,
|
||||||
generateExport,
|
generateExport,
|
||||||
absoluteUrl,
|
generateExportUrl,
|
||||||
renderLayoutOptionSwitch,
|
renderLayoutOptionSwitch,
|
||||||
} = useMemo(() => {
|
} = useMemo(() => {
|
||||||
return aggregateReportTypes?.find(({ reportType }) => reportType === selectedRadio)!;
|
return aggregateReportTypes?.find(({ reportType }) => reportType === selectedRadio)!;
|
||||||
|
@ -124,7 +124,8 @@ const ExportContentUi = ({
|
||||||
}, [usePrintLayout, renderLayoutOptionSwitch, handlePrintLayoutChange]);
|
}, [usePrintLayout, renderLayoutOptionSwitch, handlePrintLayoutChange]);
|
||||||
|
|
||||||
const showCopyURLButton = useCallback(() => {
|
const showCopyURLButton = useCallback(() => {
|
||||||
if (renderCopyURLButton && publicAPIEnabled)
|
if (renderCopyURLButton && publicAPIEnabled) {
|
||||||
|
const absoluteUrl = generateExportUrl?.({ intl, optimizedForPrinting: usePrintLayout });
|
||||||
return (
|
return (
|
||||||
<EuiFlexGroup alignItems="center" gutterSize="xs" responsive={false} css={{ flexGrow: 0 }}>
|
<EuiFlexGroup alignItems="center" gutterSize="xs" responsive={false} css={{ flexGrow: 0 }}>
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem grow={false}>
|
||||||
|
@ -160,7 +161,8 @@ const ExportContentUi = ({
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
</EuiFlexGroup>
|
</EuiFlexGroup>
|
||||||
);
|
);
|
||||||
}, [absoluteUrl, renderCopyURLButton, publicAPIEnabled]);
|
}
|
||||||
|
}, [renderCopyURLButton, publicAPIEnabled, usePrintLayout, generateExportUrl, intl]);
|
||||||
|
|
||||||
const renderGenerateReportButton = useCallback(() => {
|
const renderGenerateReportButton = useCallback(() => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { type IModalTabDeclaration } from '@kbn/shared-ux-tabbed-modal';
|
import { type IModalTabDeclaration } from '@kbn/shared-ux-tabbed-modal';
|
||||||
import { ExportContent } from './export_content';
|
import { ExportContent } from './export_content';
|
||||||
import { useShareTabsContext, type ShareMenuItemV2 } from '../../context';
|
import { useShareTabsContext } from '../../context';
|
||||||
|
|
||||||
type IExportTab = IModalTabDeclaration;
|
type IExportTab = IModalTabDeclaration;
|
||||||
|
|
||||||
|
@ -23,8 +23,7 @@ const ExportTabContent = () => {
|
||||||
objectType={objectType}
|
objectType={objectType}
|
||||||
isDirty={isDirty}
|
isDirty={isDirty}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
// we are guaranteed that shareMenuItems will be a ShareMenuItem V2 variant
|
aggregateReportTypes={shareMenuItems}
|
||||||
aggregateReportTypes={shareMenuItems as unknown as ShareMenuItemV2[]}
|
|
||||||
publicAPIEnabled={publicAPIEnabled ?? true}
|
publicAPIEnabled={publicAPIEnabled ?? true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -23,7 +23,8 @@ export type {
|
||||||
export type {
|
export type {
|
||||||
ShareContext,
|
ShareContext,
|
||||||
ShareMenuProvider,
|
ShareMenuProvider,
|
||||||
ShareMenuItem,
|
ShareMenuItemLegacy,
|
||||||
|
ShareMenuItemV2,
|
||||||
ShowShareMenuOptions,
|
ShowShareMenuOptions,
|
||||||
ShareContextMenuPanelItem,
|
ShareContextMenuPanelItem,
|
||||||
BrowserUrlService,
|
BrowserUrlService,
|
||||||
|
|
|
@ -11,10 +11,10 @@ import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { toMountPoint } from '@kbn/react-kibana-mount';
|
import { toMountPoint } from '@kbn/react-kibana-mount';
|
||||||
import { CoreStart, ThemeServiceStart, ToastsSetup } from '@kbn/core/public';
|
import { CoreStart, ThemeServiceStart, ToastsSetup } from '@kbn/core/public';
|
||||||
import { ShareMenuItem, ShowShareMenuOptions } from '../types';
|
import { ShowShareMenuOptions } from '../types';
|
||||||
import { ShareMenuRegistryStart } from './share_menu_registry';
|
import { ShareMenuRegistryStart } from './share_menu_registry';
|
||||||
import { AnonymousAccessServiceContract } from '../../common/anonymous_access';
|
import { AnonymousAccessServiceContract } from '../../common/anonymous_access';
|
||||||
import type { BrowserUrlService } from '../types';
|
import type { BrowserUrlService, ShareMenuItemV2 } from '../types';
|
||||||
import { ShareMenu } from '../components/share_tabs';
|
import { ShareMenu } from '../components/share_tabs';
|
||||||
|
|
||||||
export class ShareMenuManager {
|
export class ShareMenuManager {
|
||||||
|
@ -89,7 +89,7 @@ export class ShareMenuManager {
|
||||||
publicAPIEnabled,
|
publicAPIEnabled,
|
||||||
}: ShowShareMenuOptions & {
|
}: ShowShareMenuOptions & {
|
||||||
anchorElement: HTMLElement;
|
anchorElement: HTMLElement;
|
||||||
menuItems: ShareMenuItem[];
|
menuItems: ShareMenuItemV2[];
|
||||||
urlService: BrowserUrlService;
|
urlService: BrowserUrlService;
|
||||||
anonymousAccess: AnonymousAccessServiceContract | undefined;
|
anonymousAccess: AnonymousAccessServiceContract | undefined;
|
||||||
theme: ThemeServiceStart;
|
theme: ThemeServiceStart;
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
ShareMenuRegistrySetup,
|
ShareMenuRegistrySetup,
|
||||||
ShareMenuRegistryStart,
|
ShareMenuRegistryStart,
|
||||||
} from './share_menu_registry';
|
} from './share_menu_registry';
|
||||||
import { ShareMenuItem, ShareContext } from '../types';
|
import { ShareMenuItemV2, ShareContext } from '../types';
|
||||||
|
|
||||||
const createSetupMock = (): jest.Mocked<ShareMenuRegistrySetup> => {
|
const createSetupMock = (): jest.Mocked<ShareMenuRegistrySetup> => {
|
||||||
const setup = {
|
const setup = {
|
||||||
|
@ -24,7 +24,7 @@ const createSetupMock = (): jest.Mocked<ShareMenuRegistrySetup> => {
|
||||||
|
|
||||||
const createStartMock = (): jest.Mocked<ShareMenuRegistryStart> => {
|
const createStartMock = (): jest.Mocked<ShareMenuRegistryStart> => {
|
||||||
const start = {
|
const start = {
|
||||||
getShareMenuItems: jest.fn((props: ShareContext) => [] as ShareMenuItem[]),
|
getShareMenuItems: jest.fn((_props: ShareContext) => [] as ShareMenuItemV2[]),
|
||||||
};
|
};
|
||||||
return start;
|
return start;
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ShareMenuRegistry } from './share_menu_registry';
|
import { ShareMenuRegistry } from './share_menu_registry';
|
||||||
import { ShareMenuItem, ShareContext } from '../types';
|
import { ShareMenuItemV2, ShareContext } from '../types';
|
||||||
|
|
||||||
describe('ShareActionsRegistry', () => {
|
describe('ShareActionsRegistry', () => {
|
||||||
describe('setup', () => {
|
describe('setup', () => {
|
||||||
|
@ -34,9 +34,9 @@ describe('ShareActionsRegistry', () => {
|
||||||
test('returns a flat list of actions returned by all providers', () => {
|
test('returns a flat list of actions returned by all providers', () => {
|
||||||
const service = new ShareMenuRegistry();
|
const service = new ShareMenuRegistry();
|
||||||
const registerFunction = service.setup().register;
|
const registerFunction = service.setup().register;
|
||||||
const shareAction1 = {} as ShareMenuItem;
|
const shareAction1 = {} as ShareMenuItemV2;
|
||||||
const shareAction2 = {} as ShareMenuItem;
|
const shareAction2 = {} as ShareMenuItemV2;
|
||||||
const shareAction3 = {} as ShareMenuItem;
|
const shareAction3 = {} as ShareMenuItemV2;
|
||||||
const provider1Callback = jest.fn(() => [shareAction1]);
|
const provider1Callback = jest.fn(() => [shareAction1]);
|
||||||
const provider2Callback = jest.fn(() => [shareAction2, shareAction3]);
|
const provider2Callback = jest.fn(() => [shareAction2, shareAction3]);
|
||||||
registerFunction({
|
registerFunction({
|
||||||
|
|
|
@ -7,7 +7,12 @@
|
||||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ShareContext, ShareMenuProvider } from '../types';
|
import {
|
||||||
|
ShareContext,
|
||||||
|
ShareMenuProvider,
|
||||||
|
ShareMenuProviderV2,
|
||||||
|
ShareMenuProviderLegacy,
|
||||||
|
} from '../types';
|
||||||
|
|
||||||
export class ShareMenuRegistry {
|
export class ShareMenuRegistry {
|
||||||
private readonly shareMenuProviders = new Map<string, ShareMenuProvider>();
|
private readonly shareMenuProviders = new Map<string, ShareMenuProvider>();
|
||||||
|
@ -36,7 +41,10 @@ export class ShareMenuRegistry {
|
||||||
return {
|
return {
|
||||||
getShareMenuItems: (context: ShareContext) =>
|
getShareMenuItems: (context: ShareContext) =>
|
||||||
Array.from(this.shareMenuProviders.values()).flatMap((shareActionProvider) =>
|
Array.from(this.shareMenuProviders.values()).flatMap((shareActionProvider) =>
|
||||||
shareActionProvider.getShareMenuItems(context)
|
(
|
||||||
|
(shareActionProvider as ShareMenuProviderV2).getShareMenuItems ??
|
||||||
|
(shareActionProvider as ShareMenuProviderLegacy).getShareMenuItemsLegacy
|
||||||
|
).call(shareActionProvider, context)
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,10 +100,16 @@ export type SupportedExportTypes =
|
||||||
interface ShareMenuItemBase {
|
interface ShareMenuItemBase {
|
||||||
shareMenuItem?: ShareContextMenuPanelItem;
|
shareMenuItem?: ShareContextMenuPanelItem;
|
||||||
}
|
}
|
||||||
interface ShareMenuItemLegacy extends ShareMenuItemBase {
|
|
||||||
|
export interface ShareMenuItemLegacy extends ShareMenuItemBase {
|
||||||
panel?: EuiContextMenuPanelDescriptor;
|
panel?: EuiContextMenuPanelDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ScreenshotExportOpts {
|
||||||
|
optimizedForPrinting?: boolean;
|
||||||
|
intl: InjectedIntl;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ShareMenuItemV2 extends ShareMenuItemBase {
|
export interface ShareMenuItemV2 extends ShareMenuItemBase {
|
||||||
// extended props to support share modal
|
// extended props to support share modal
|
||||||
label: 'PDF' | 'CSV' | 'PNG';
|
label: 'PDF' | 'CSV' | 'PNG';
|
||||||
|
@ -112,21 +118,31 @@ export interface ShareMenuItemV2 extends ShareMenuItemBase {
|
||||||
helpText?: ReactElement;
|
helpText?: ReactElement;
|
||||||
copyURLButton?: { id: string; dataTestSubj: string; label: string };
|
copyURLButton?: { id: string; dataTestSubj: string; label: string };
|
||||||
generateExportButton?: ReactElement;
|
generateExportButton?: ReactElement;
|
||||||
generateExport: (args: {
|
/**
|
||||||
intl: InjectedIntl;
|
* Function to trigger an export
|
||||||
optimizedForPrinting?: boolean;
|
*/
|
||||||
}) => Promise<unknown>;
|
generateExport: (args: ScreenshotExportOpts) => Promise<unknown>;
|
||||||
|
/**
|
||||||
|
* Function to generate a URL to be used for automating export
|
||||||
|
* Not applicable for exports that do not call a remote API (i.e Lens CSV export)
|
||||||
|
*/
|
||||||
|
generateExportUrl?: (args: ScreenshotExportOpts) => string | undefined;
|
||||||
theme?: ThemeServiceSetup;
|
theme?: ThemeServiceSetup;
|
||||||
renderLayoutOptionSwitch?: boolean;
|
renderLayoutOptionSwitch?: boolean;
|
||||||
layoutOption?: 'print';
|
layoutOption?: 'print';
|
||||||
absoluteUrl?: string;
|
|
||||||
generateCopyUrl?: URL;
|
generateCopyUrl?: URL;
|
||||||
renderCopyURLButton?: boolean;
|
renderCopyURLButton?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ShareMenuItem = ShareMenuItemLegacy | ShareMenuItemV2;
|
export interface ShareMenuProviderV2 {
|
||||||
|
readonly id: string;
|
||||||
|
getShareMenuItems: (context: ShareContext) => Array<Omit<ShareMenuItemV2, 'intl'>>;
|
||||||
|
}
|
||||||
|
export interface ShareMenuProviderLegacy {
|
||||||
|
readonly id: string;
|
||||||
|
getShareMenuItemsLegacy: (context: ShareContext) => ShareMenuItemLegacy[];
|
||||||
|
}
|
||||||
|
|
||||||
type ShareMenuItemType = Omit<ShareMenuItem, 'intl'>;
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
* A source for additional menu items shown in the share context menu. Any provider
|
* A source for additional menu items shown in the share context menu. Any provider
|
||||||
|
@ -134,10 +150,7 @@ type ShareMenuItemType = Omit<ShareMenuItem, 'intl'>;
|
||||||
* menu. Returned `ShareMenuItem`s will be shown in the context menu together with the
|
* menu. Returned `ShareMenuItem`s will be shown in the context menu together with the
|
||||||
* default built-in share options. Each share provider needs a globally unique id.
|
* default built-in share options. Each share provider needs a globally unique id.
|
||||||
* */
|
* */
|
||||||
export interface ShareMenuProvider {
|
export type ShareMenuProvider = ShareMenuProviderV2 | ShareMenuProviderLegacy;
|
||||||
readonly id: string;
|
|
||||||
getShareMenuItems: (context: ShareContext) => ShareMenuItemType[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UrlParamExtensionProps {
|
interface UrlParamExtensionProps {
|
||||||
setParamValue: (values: {}) => void;
|
setParamValue: (values: {}) => void;
|
||||||
|
|
|
@ -144,8 +144,8 @@ export const downloadCsvShareProvider = ({
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
...menuItemMetadata,
|
...menuItemMetadata,
|
||||||
label: 'CSV',
|
label: 'CSV' as const,
|
||||||
reportType: 'lens_csv',
|
reportType: 'lens_csv' as const,
|
||||||
generateExport: downloadCSVHandler,
|
generateExport: downloadCSVHandler,
|
||||||
...(atLeastGold()
|
...(atLeastGold()
|
||||||
? {
|
? {
|
||||||
|
|
|
@ -6122,8 +6122,6 @@
|
||||||
"reporting.printablePdfV2.generateButtonLabel": "Exporter un fichier",
|
"reporting.printablePdfV2.generateButtonLabel": "Exporter un fichier",
|
||||||
"reporting.printablePdfV2.helpText": "Sélectionnez le type de fichier que vous souhaitez exporter pour cette visualisation.",
|
"reporting.printablePdfV2.helpText": "Sélectionnez le type de fichier que vous souhaitez exporter pour cette visualisation.",
|
||||||
"reporting.share.contextMenu.export.csvReportsButtonLabel": "Exporter",
|
"reporting.share.contextMenu.export.csvReportsButtonLabel": "Exporter",
|
||||||
"reporting.share.contextMenu.pdfReportsButtonLabel": "Rapports PDF",
|
|
||||||
"reporting.share.contextMenu.pngReportsButtonLabel": "Rapports PNG",
|
|
||||||
"reporting.share.csv.reporting.helpTextCSV": "Exporter un fichier CSV à partir de ce {objectType}.",
|
"reporting.share.csv.reporting.helpTextCSV": "Exporter un fichier CSV à partir de ce {objectType}.",
|
||||||
"reporting.share.generateButtonLabelCSV": "Générer un CSV",
|
"reporting.share.generateButtonLabelCSV": "Générer un CSV",
|
||||||
"reporting.share.modalContent.notification.reportingErrorTitle": "Impossible de créer le rapport",
|
"reporting.share.modalContent.notification.reportingErrorTitle": "Impossible de créer le rapport",
|
||||||
|
|
|
@ -5876,8 +5876,6 @@
|
||||||
"reporting.printablePdfV2.generateButtonLabel": "ファイルのエクスポート",
|
"reporting.printablePdfV2.generateButtonLabel": "ファイルのエクスポート",
|
||||||
"reporting.printablePdfV2.helpText": "このビジュアライゼーションでエクスポートするファイルタイプを選択します。",
|
"reporting.printablePdfV2.helpText": "このビジュアライゼーションでエクスポートするファイルタイプを選択します。",
|
||||||
"reporting.share.contextMenu.export.csvReportsButtonLabel": "エクスポート",
|
"reporting.share.contextMenu.export.csvReportsButtonLabel": "エクスポート",
|
||||||
"reporting.share.contextMenu.pdfReportsButtonLabel": "PDF レポート",
|
|
||||||
"reporting.share.contextMenu.pngReportsButtonLabel": "PNG レポート",
|
|
||||||
"reporting.share.csv.reporting.helpTextCSV": "この{objectType}のCSVをエクスポートします。",
|
"reporting.share.csv.reporting.helpTextCSV": "この{objectType}のCSVをエクスポートします。",
|
||||||
"reporting.share.generateButtonLabelCSV": "CSVを生成",
|
"reporting.share.generateButtonLabelCSV": "CSVを生成",
|
||||||
"reporting.share.modalContent.notification.reportingErrorTitle": "レポートを作成できません",
|
"reporting.share.modalContent.notification.reportingErrorTitle": "レポートを作成できません",
|
||||||
|
|
|
@ -5889,8 +5889,6 @@
|
||||||
"reporting.printablePdfV2.generateButtonLabel": "导出文件",
|
"reporting.printablePdfV2.generateButtonLabel": "导出文件",
|
||||||
"reporting.printablePdfV2.helpText": "为此可视化选择您要导出的文件类型。",
|
"reporting.printablePdfV2.helpText": "为此可视化选择您要导出的文件类型。",
|
||||||
"reporting.share.contextMenu.export.csvReportsButtonLabel": "导出",
|
"reporting.share.contextMenu.export.csvReportsButtonLabel": "导出",
|
||||||
"reporting.share.contextMenu.pdfReportsButtonLabel": "PDF 报告",
|
|
||||||
"reporting.share.contextMenu.pngReportsButtonLabel": "PNG 报告",
|
|
||||||
"reporting.share.csv.reporting.helpTextCSV": "导出此 {objectType} 的 CSV。",
|
"reporting.share.csv.reporting.helpTextCSV": "导出此 {objectType} 的 CSV。",
|
||||||
"reporting.share.generateButtonLabelCSV": "生成 CSV",
|
"reporting.share.generateButtonLabelCSV": "生成 CSV",
|
||||||
"reporting.share.modalContent.notification.reportingErrorTitle": "无法创建报告",
|
"reporting.share.modalContent.notification.reportingErrorTitle": "无法创建报告",
|
||||||
|
|
|
@ -127,6 +127,34 @@ export default function ({
|
||||||
expect(res.get('content-type')).to.equal('application/pdf');
|
expect(res.get('content-type')).to.equal('application/pdf');
|
||||||
await share.closeShareModal();
|
await share.closeShareModal();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('provides a button to copy POST URL', async () => {
|
||||||
|
// The "clipboard-read" permission of the Permissions API must be granted
|
||||||
|
if (!(await browser.checkBrowserPermission('clipboard-read'))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await dashboard.navigateToApp();
|
||||||
|
await dashboard.loadSavedDashboard('Ecom Dashboard');
|
||||||
|
await reporting.openExportTab();
|
||||||
|
await reporting.checkUsePrintLayout();
|
||||||
|
await testSubjects.click('shareReportingCopyURL');
|
||||||
|
|
||||||
|
const postUrl = await browser.getClipboardValue();
|
||||||
|
expect(postUrl).to.contain('printablePdfV2');
|
||||||
|
|
||||||
|
const [, jobParams] = postUrl.split('jobParams=');
|
||||||
|
expect(decodeURIComponent(jobParams)).to.contain('browserTimezone:UTC,');
|
||||||
|
expect(decodeURIComponent(jobParams)).to.match(
|
||||||
|
/layout:\(dimensions:\(height:1\d{3},width:1\d{3}\),id:print\),/
|
||||||
|
);
|
||||||
|
expect(decodeURIComponent(jobParams)).to.match(
|
||||||
|
/objectType:dashboard,title:'Ecom Dashboard',/
|
||||||
|
);
|
||||||
|
expect(decodeURIComponent(jobParams)).to.match(
|
||||||
|
/locatorParams:.*id:DASHBOARD_APP_LOCATOR,params:\(dashboardId:'6c263e00-1c6d-11ea-a100-8589bb9d7c6b',/
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Print PNG button', () => {
|
describe('Print PNG button', () => {
|
||||||
|
@ -153,9 +181,37 @@ export default function ({
|
||||||
expect(await reporting.isGenerateReportButtonDisabled()).to.be(null);
|
expect(await reporting.isGenerateReportButtonDisabled()).to.be(null);
|
||||||
await (await testSubjects.find('kibanaChrome')).clickMouseButton(); // close popover
|
await (await testSubjects.find('kibanaChrome')).clickMouseButton(); // close popover
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('provides a button to copy POST URL', async () => {
|
||||||
|
// The "clipboard-read" permission of the Permissions API must be granted
|
||||||
|
if (!(await browser.checkBrowserPermission('clipboard-read'))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await dashboard.navigateToApp();
|
||||||
|
await dashboard.loadSavedDashboard('Ecom Dashboard');
|
||||||
|
await reporting.openExportTab();
|
||||||
|
await testSubjects.click('pngV2-radioOption');
|
||||||
|
await testSubjects.click('shareReportingCopyURL');
|
||||||
|
|
||||||
|
const postUrl = await browser.getClipboardValue();
|
||||||
|
expect(postUrl).to.contain('pngV2');
|
||||||
|
|
||||||
|
const [, jobParams] = postUrl.split('jobParams=');
|
||||||
|
expect(decodeURIComponent(jobParams)).to.contain('browserTimezone:UTC,');
|
||||||
|
expect(decodeURIComponent(jobParams)).to.match(
|
||||||
|
/layout:\(dimensions:\(height:1\d{3},width:1\d{3}\),id:preserve_layout\),/
|
||||||
|
);
|
||||||
|
expect(decodeURIComponent(jobParams)).to.match(
|
||||||
|
/objectType:dashboard,title:'Ecom Dashboard',/
|
||||||
|
);
|
||||||
|
expect(decodeURIComponent(jobParams)).to.match(
|
||||||
|
/locatorParams:.*id:DASHBOARD_APP_LOCATOR,params:\(dashboardId:'6c263e00-1c6d-11ea-a100-8589bb9d7c6b',/
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe.skip('Preserve Layout', () => {
|
describe('Preserve Layout', () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await loadEcommerce();
|
await loadEcommerce();
|
||||||
});
|
});
|
||||||
|
@ -180,6 +236,33 @@ export default function ({
|
||||||
expect(res.get('content-type')).to.equal('application/pdf');
|
expect(res.get('content-type')).to.equal('application/pdf');
|
||||||
await kibanaServer.uiSettings.replace({});
|
await kibanaServer.uiSettings.replace({});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('provides a button to copy POST URL', async () => {
|
||||||
|
// The "clipboard-read" permission of the Permissions API must be granted
|
||||||
|
if (!(await browser.checkBrowserPermission('clipboard-read'))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await dashboard.navigateToApp();
|
||||||
|
await dashboard.loadSavedDashboard('Ecom Dashboard');
|
||||||
|
await reporting.openExportTab();
|
||||||
|
await testSubjects.click('shareReportingCopyURL');
|
||||||
|
|
||||||
|
const postUrl = await browser.getClipboardValue();
|
||||||
|
expect(postUrl).to.contain('printablePdfV2');
|
||||||
|
|
||||||
|
const [, jobParams] = postUrl.split('jobParams=');
|
||||||
|
expect(decodeURIComponent(jobParams)).to.contain('browserTimezone:UTC,');
|
||||||
|
expect(decodeURIComponent(jobParams)).to.match(
|
||||||
|
/layout:\(dimensions:\(height:1\d{3},width:1\d{3}\),id:preserve_layout\),/
|
||||||
|
);
|
||||||
|
expect(decodeURIComponent(jobParams)).to.match(
|
||||||
|
/objectType:dashboard,title:'Ecom Dashboard',/
|
||||||
|
);
|
||||||
|
expect(decodeURIComponent(jobParams)).to.match(
|
||||||
|
/locatorParams:.*id:DASHBOARD_APP_LOCATOR,params:\(dashboardId:'6c263e00-1c6d-11ea-a100-8589bb9d7c6b',/
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Sample data from Kibana 7.6', () => {
|
describe('Sample data from Kibana 7.6', () => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue