Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Reporting] fix dashboard "Copy Post URL" action #192530

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/share_examples/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class ShareDemoPlugin implements Plugin<void, void, SetupDeps, StartDeps>
public setup(core: CoreSetup<StartDeps>, { share }: SetupDeps) {
share.register({
id: 'demo',
getShareMenuItems: (context) => [
getShareMenuItemsLegacy: (context) => [
{
panel: {
id: 'demo',
Expand Down
3 changes: 3 additions & 0 deletions packages/kbn-reporting/public/reporting_api_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
// 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import * as Rx from 'rxjs';

import type { ApplicationStart, CoreStart } from '@kbn/core/public';
import { ILicense } from '@kbn/licensing-plugin/public';
import type { LayoutParams } from '@kbn/screenshotting-plugin/common';

import type { ReportingAPIClient } from '../../reporting_api_client';

Expand Down Expand Up @@ -47,13 +46,16 @@ export interface ExportPanelShareOpts {

export interface ReportingSharingData {
title: string;
layout: LayoutParams;
reportingDisabled?: boolean;
[key: string]: unknown;
locatorParams: {
id: string;
params: unknown;
};
}

export interface JobParamsProviderOptions {
sharingData: ReportingSharingData;
shareableUrl?: string;
objectType: string;
optimizedForPrinting?: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 { 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 { checkLicense } from '../..';

Expand Down Expand Up @@ -69,7 +69,7 @@ export const reportingCsvShareProvider = ({
};
};

const shareActions: ShareMenuItem[] = [];
const shareActions: ShareMenuItemV2[] = [];

const licenseCheck = checkLicense(license.check('reporting', 'basic'));
const licenseToolTipContent = licenseCheck.message;
Expand Down Expand Up @@ -177,8 +177,8 @@ export const reportingCsvShareProvider = ({
/>
),
generateExport: generateReportingJobCSV,
generateExportUrl: () => absoluteUrl,
generateCopyUrl: reportingUrl,
absoluteUrl,
renderCopyURLButton: true,
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,28 @@
*/

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 { ShareContext, ShareMenuItem, ShareMenuProvider } from '@kbn/share-plugin/public';
import { ShareContext, ShareMenuItemV2, ShareMenuProvider } from '@kbn/share-plugin/public';
import React from 'react';
import { firstValueFrom } from 'rxjs';
import {
ExportModalShareOpts,
ExportPanelShareOpts,
JobParamsProviderOptions,
ReportingSharingData,
} from '.';
import { ScreenshotExportOpts } from '@kbn/share-plugin/public/types';
import { ExportModalShareOpts, JobParamsProviderOptions, ReportingSharingData } from '.';
import { checkLicense } from '../../license_check';
import { ScreenCapturePanelContent } from './screen_capture_panel_content_lazy';

const getJobParams = (opts: JobParamsProviderOptions, type: 'pngV2' | 'printablePdfV2') => () => {
const {
objectType,
sharingData: { title, layout, locatorParams },
sharingData: { title, locatorParams },
optimizedForPrinting,
} = opts;

const baseParams = {
objectType,
layout,
title,
};
const el = document.querySelector('[data-shared-items-container]');
const { height, width } = el ? el.getBoundingClientRect() : { height: 768, width: 1024 };
const dimensions = { height, width };
const layoutId = optimizedForPrinting ? ('print' as const) : ('preserve_layout' as const);
const layout = { id: layoutId, dimensions };
const baseParams = { objectType, layout, title };
Comment on lines +27 to +32
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for consolidating this logic!


if (type === 'printablePdfV2') {
// multi locator for PDF V2
Expand All @@ -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 = ({
Copy link
Member Author

@tsullivan tsullivan Sep 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Canvas integration doesn't use this export, and doesn't use the Sharing services for its menu. See x-pack/plugins/canvas/public/components/workpad_header/share_menu/share_menu.tsx

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 = ({
apiClient,
license,
Expand Down Expand Up @@ -249,7 +100,7 @@ export const reportingExportModalProvider = ({
}

const { sharingData } = shareOpts as unknown as { sharingData: ReportingSharingData };
const shareActions: ShareMenuItem[] = [];
const shareActions: ShareMenuItemV2[] = [];

const jobProviderOptions: JobParamsProviderOptions = {
shareableUrl: isDirty ? shareableUrl : shareableUrlForSavedObject ?? shareableUrl,
Expand All @@ -259,32 +110,9 @@ export const reportingExportModalProvider = ({

const requiresSavedState = sharingData.locatorParams === null;

const relativePathPDF = apiClient.getReportingPublicJobPath(
'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 generateReportPDF = ({ intl, optimizedForPrinting = false }: ScreenshotExportOpts) => {
const decoratedJobParams = apiClient.getDecoratedJobParams({
...getJobParams(jobProviderOptions, 'printablePdfV2')(),
layout: { id: optimizedForPrinting ? 'print' : 'preserve_layout', dimensions },
objectType,
title: sharingData.title,
...getJobParams({ ...jobProviderOptions, optimizedForPrinting }, 'printablePdfV2')(),
});

return apiClient
Expand Down Expand Up @@ -330,19 +158,27 @@ export const reportingExportModalProvider = ({
});
};

const generateReportPNG = ({ intl }: { intl: InjectedIntl }) => {
const { layout: outerLayout } = getJobParams(jobProviderOptions, 'pngV2')();
let dimensions = outerLayout?.dimensions;
if (!dimensions) {
const el = document.querySelector('[data-shared-items-container]');
const { height, width } = el ? el.getBoundingClientRect() : { height: 768, width: 1024 };
dimensions = { height, width };
}
const generateExportUrlPDF = ({ optimizedForPrinting }: ScreenshotExportOpts) => {
const jobParams = apiClient.getDecoratedJobParams(
getJobParams({ ...jobProviderOptions, optimizedForPrinting }, 'printablePdfV2')()
);
const relativePathPDF = apiClient.getReportingPublicJobPath('printablePdfV2', jobParams);

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({
...getJobParams(jobProviderOptions, 'pngV2')(),
layout: { id: 'preserve_layout', dimensions },
objectType,
title: sharingData.title,
});
return apiClient
.createReportingJob('pngV2', decoratedJobParams)
Expand Down Expand Up @@ -398,6 +234,7 @@ export const reportingExportModalProvider = ({
},
label: 'PDF' as const,
generateExport: generateReportPDF,
generateExportUrl: generateExportUrlPDF,
reportType: 'printablePdfV2',
requiresSavedState,
helpText: (
Expand All @@ -415,7 +252,6 @@ export const reportingExportModalProvider = ({
layoutOption: objectType === 'dashboard' ? ('print' as const) : undefined,
renderLayoutOptionSwitch: objectType === 'dashboard',
renderCopyURLButton: true,
absoluteUrl: new URL(relativePathPDF, window.location.href).toString(),
});

shareActions.push({
Expand All @@ -429,6 +265,7 @@ export const reportingExportModalProvider = ({
},
label: 'PNG' as const,
generateExport: generateReportPNG,
generateExportUrl: generateExportUrlPNG,
reportType: 'pngV2',
requiresSavedState,
helpText: (
Expand All @@ -442,7 +279,6 @@ export const reportingExportModalProvider = ({
),
layoutOption: objectType === 'dashboard' ? ('print' as const) : undefined,
renderCopyURLButton: true,
absoluteUrl: new URL(relativePathPNG, window.location.href).toString(),
});

return shareActions;
Expand Down
Loading