From 3559d375b6795463f601c2362f29aa0fe6cadc8e Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Wed, 9 Jun 2021 17:08:33 -0700 Subject: [PATCH] [Reporting] Make "ScreenCapturePanel" shareable for Canvas (#100623) * use ScreenCapturePanel component in Canvas * use smaller state object * add comment about canvas-specific shared component * fix example * fix toast error * fix i18n * fix data-test-subj Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/components/app.tsx | 91 +- x-pack/plugins/canvas/i18n/components.ts | 87 +- x-pack/plugins/canvas/kibana.json | 1 - .../share_menu.stories.storyshot | 33 +- .../__stories__/pdf_panel.stories.tsx | 36 - .../__stories__/share_menu.stories.tsx | 29 +- .../workpad_header/share_menu/pdf_panel.tsx | 98 -- .../share_menu/share_menu.component.tsx | 63 +- .../workpad_header/share_menu/share_menu.ts | 104 +- .../workpad_header/share_menu/utils.test.ts | 64 +- .../workpad_header/share_menu/utils.ts | 51 +- .../canvas/public/services/reporting.ts | 23 +- .../canvas/public/services/stubs/reporting.ts | 13 +- x-pack/plugins/reporting/common/constants.ts | 1 + ...screen_capture_panel_content.test.tsx.snap | 1191 +++++++++++++++++ .../reporting/public/components/index.ts | 1 + .../components/reporting_panel_content.tsx | 129 +- .../screen_capture_panel_content.test.tsx | 74 + .../screen_capture_panel_content.tsx | 109 +- .../shared/get_shared_components.tsx | 41 + .../public/components/shared/index.tsx | 8 + x-pack/plugins/reporting/public/index.ts | 10 +- x-pack/plugins/reporting/public/mocks.ts | 27 + x-pack/plugins/reporting/public/plugin.ts | 39 +- .../register_csv_reporting.tsx | 9 +- .../register_pdf_png_reporting.tsx | 5 +- .../translations/translations/ja-JP.json | 17 - .../translations/translations/zh-CN.json | 17 - .../functional/page_objects/reporting_page.ts | 2 +- 29 files changed, 1753 insertions(+), 620 deletions(-) delete mode 100644 x-pack/plugins/canvas/public/components/workpad_header/share_menu/__stories__/pdf_panel.stories.tsx delete mode 100644 x-pack/plugins/canvas/public/components/workpad_header/share_menu/pdf_panel.tsx create mode 100644 x-pack/plugins/reporting/public/components/__snapshots__/screen_capture_panel_content.test.tsx.snap create mode 100644 x-pack/plugins/reporting/public/components/screen_capture_panel_content.test.tsx create mode 100644 x-pack/plugins/reporting/public/components/shared/get_shared_components.tsx create mode 100644 x-pack/plugins/reporting/public/components/shared/index.tsx create mode 100644 x-pack/plugins/reporting/public/mocks.ts diff --git a/x-pack/examples/reporting_example/public/components/app.tsx b/x-pack/examples/reporting_example/public/components/app.tsx index 0174ec2a17ad4..5f6f5d17afafb 100644 --- a/x-pack/examples/reporting_example/public/components/app.tsx +++ b/x-pack/examples/reporting_example/public/components/app.tsx @@ -6,8 +6,9 @@ */ import { + EuiButton, EuiCard, - EuiCode, + EuiContextMenu, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, @@ -17,7 +18,7 @@ import { EuiPageContent, EuiPageContentBody, EuiPageHeader, - EuiPanel, + EuiPopover, EuiText, EuiTitle, } from '@elastic/eui'; @@ -50,7 +51,19 @@ export const ReportingExampleApp = ({ reporting, screenshotMode, }: ReportingExampleAppDeps) => { - const { getDefaultLayoutSelectors, ReportingAPIClient } = reporting; + const { getDefaultLayoutSelectors } = reporting; + + // Context Menu + const [isPopoverOpen, setPopover] = useState(false); + const onButtonClick = () => { + setPopover(!isPopoverOpen); + }; + + const closePopover = () => { + setPopover(false); + }; + + // Async Logos const [logos, setLogos] = useState([]); useEffect(() => { @@ -61,7 +74,7 @@ export const ReportingExampleApp = ({ }); }); - const getPDFJobParams = (): JobParamsPDF => { + const getPDFJobParamsDefault = (): JobParamsPDF => { return { layout: { id: constants.LAYOUT_TYPES.PRESERVE_LAYOUT, @@ -73,7 +86,40 @@ export const ReportingExampleApp = ({ }; }; - // Render the application DOM. + const panels = [ + { id: 0, items: [{ name: 'PDF Reports', icon: 'document', panel: 1 }] }, + { + id: 1, + initialFocusedItemIndex: 1, + title: 'PDF Reports', + items: [ + { name: 'No Layout Option', icon: 'document', panel: 2 }, + { name: 'Canvas Layout Option', icon: 'canvasApp', panel: 3 }, + ], + }, + { + id: 2, + title: 'No Layout Option', + content: ( + + ), + }, + { + id: 3, + title: 'Canvas Layout Option', + content: ( + + ), + }, + ]; + return ( @@ -87,34 +133,21 @@ export const ReportingExampleApp = ({ -

- Use the ReportingStart.components.ScreenCapturePanel{' '} - component to add the Reporting panel to your page. -

- - +

Example of a Sharing menu using components from Reporting

- - - - - - - + Share} + isOpen={isPopoverOpen} + closePopover={closePopover} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + -

- The logos below are in a data-shared-items-container element - for Reporting. -

-
{logos.map((item, index) => ( diff --git a/x-pack/plugins/canvas/i18n/components.ts b/x-pack/plugins/canvas/i18n/components.ts index a797a8bda061b..7a23137e7ef60 100644 --- a/x-pack/plugins/canvas/i18n/components.ts +++ b/x-pack/plugins/canvas/i18n/components.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { BOLD_MD_TOKEN, CANVAS, HTML, JSON, KIBANA, PDF, POST, URL, ZIP } from './constants'; +import { BOLD_MD_TOKEN, CANVAS, HTML, JSON, PDF, URL, ZIP } from './constants'; export const ComponentStrings = { AddEmbeddableFlyout: { @@ -1418,95 +1418,10 @@ export const ComponentStrings = { URL, }, }), - getCopyReportingConfigMessage: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.copyReportingConfigMessage', { - defaultMessage: 'Copied reporting configuration to clipboard', - }), getCopyShareConfigMessage: () => i18n.translate('xpack.canvas.workpadHeaderShareMenu.copyShareConfigMessage', { defaultMessage: 'Copied share markup to clipboard', }), - getExportPDFErrorTitle: (workpadName: string) => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.exportPDFErrorMessage', { - defaultMessage: "Failed to create {PDF} for '{workpadName}'", - values: { - PDF, - workpadName, - }, - }), - getExportPDFMessage: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.exportPDFMessage', { - defaultMessage: 'Exporting {PDF}. You can track the progress in Management.', - values: { - PDF, - }, - }), - getExportPDFTitle: (workpadName: string) => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.exportPDFTitle', { - defaultMessage: "{PDF} export of workpad '{workpadName}'", - values: { - PDF, - workpadName, - }, - }), - getPDFFullPageLayoutHelpText: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.FullPageLayoutHelpText', { - defaultMessage: 'Remove borders and footer logo', - }), - getPDFFullPageLayoutLabel: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.FullPageLayoutLabel', { - defaultMessage: 'Full page layout', - }), - getPDFPanelAdvancedOptionsLabel: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.pdfPanelAdvancedOptionsLabel', { - defaultMessage: 'Advanced options', - }), - getPDFPanelCopyAriaLabel: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyAriaLabel', { - defaultMessage: - 'Alternatively, you can generate a {PDF} from a script or with Watcher by using this {URL}. Press Enter to copy the {URL} to clipboard.', - values: { - PDF, - URL, - }, - }), - getPDFPanelCopyButtonLabel: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyButtonLabel', { - defaultMessage: 'Copy {POST} {URL}', - values: { - POST, - URL, - }, - }), - getPDFPanelCopyDescription: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyDescription', { - defaultMessage: - 'Alternatively, copy this {POST} {URL} to call generation from outside {KIBANA} or from Watcher.', - values: { - POST, - KIBANA, - URL, - }, - }), - getPDFPanelGenerateButtonLabel: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.pdfPanelGenerateButtonLabel', { - defaultMessage: 'Generate {PDF}', - values: { - PDF, - }, - }), - getPDFPanelGenerateDescription: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.pdfPanelGenerateDescription', { - defaultMessage: - '{PDF}s can take a minute or two to generate based on the size of your workpad.', - values: { - PDF, - }, - }), - getPDFPanelOptionsLabel: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.pdfPanelOptionsLabel', { - defaultMessage: 'Options', - }), getShareableZipErrorTitle: (workpadName: string) => i18n.translate('xpack.canvas.workpadHeaderShareMenu.shareWebsiteErrorTitle', { defaultMessage: diff --git a/x-pack/plugins/canvas/kibana.json b/x-pack/plugins/canvas/kibana.json index 6213ecb58347c..5faeaefc9e392 100644 --- a/x-pack/plugins/canvas/kibana.json +++ b/x-pack/plugins/canvas/kibana.json @@ -29,7 +29,6 @@ "kibanaUtils", "lens", "maps", - "reporting", "savedObjects", "visualizations" ] diff --git a/x-pack/plugins/canvas/public/components/workpad_header/share_menu/__stories__/__snapshots__/share_menu.stories.storyshot b/x-pack/plugins/canvas/public/components/workpad_header/share_menu/__stories__/__snapshots__/share_menu.stories.storyshot index 302e015ea1d3a..c78ff5bf781ec 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/share_menu/__stories__/__snapshots__/share_menu.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/workpad_header/share_menu/__stories__/__snapshots__/share_menu.stories.storyshot @@ -1,6 +1,37 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Storyshots components/WorkpadHeader/ShareMenu default 1`] = ` +exports[`Storyshots components/WorkpadHeader/ShareMenu minimal 1`] = ` +
+
+
+ +
+
+
+`; + +exports[`Storyshots components/WorkpadHeader/ShareMenu with Reporting 1`] = `
( -
- 'PDF URL String'} - onCopy={action('onCopy')} - onExport={action('onExport')} - /> -
- )); diff --git a/x-pack/plugins/canvas/public/components/workpad_header/share_menu/__stories__/share_menu.stories.tsx b/x-pack/plugins/canvas/public/components/workpad_header/share_menu/__stories__/share_menu.stories.tsx index bca96f3851e37..20e52b40bc702 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/share_menu/__stories__/share_menu.stories.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/share_menu/__stories__/share_menu.stories.tsx @@ -5,19 +5,34 @@ * 2.0. */ -import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; +import { storiesOf } from '@storybook/react'; import React from 'react'; +import { platformService } from '../../../../services/stubs/platform'; +import { reportingService } from '../../../../services/stubs/reporting'; import { ShareMenu } from '../share_menu.component'; -storiesOf('components/WorkpadHeader/ShareMenu', module).add('default', () => ( +storiesOf('components/WorkpadHeader/ShareMenu', module).add('minimal', () => ( { - action(`getExportUrl('${type}')`); - return type; + /> +)); + +storiesOf('components/WorkpadHeader/ShareMenu', module).add('with Reporting', () => ( + )); diff --git a/x-pack/plugins/canvas/public/components/workpad_header/share_menu/pdf_panel.tsx b/x-pack/plugins/canvas/public/components/workpad_header/share_menu/pdf_panel.tsx deleted file mode 100644 index b412bd5328a5b..0000000000000 --- a/x-pack/plugins/canvas/public/components/workpad_header/share_menu/pdf_panel.tsx +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useState } from 'react'; -import { - EuiAccordion, - EuiButton, - EuiFormRow, - EuiHorizontalRule, - EuiSpacer, - EuiSwitch, - EuiText, - EuiTitle, -} from '@elastic/eui'; -import { Clipboard } from '../../clipboard'; -import { LayoutType } from './utils'; - -import { ComponentStrings } from '../../../../i18n/components'; -const { WorkpadHeaderShareMenu: strings } = ComponentStrings; - -interface Props { - /** Retrieve URL that will invoke PDF Report generation. */ - getPdfURL: (layout: LayoutType) => string; - /** Handler to invoke when the PDF is exported */ - onExport: (layout: LayoutType) => void; - /** Handler to invoke when the URL is copied to the clipboard. */ - onCopy: () => void; -} - -/** - * A panel displayed in the Export Menu with options in which to generate PDF Reports. - */ -export const PDFPanel = ({ getPdfURL, onExport, onCopy }: Props) => { - const [reportLayout, setReportLayout] = useState('preserve_layout'); - - return ( -
- -

{strings.getPDFPanelGenerateDescription()}

-
- - -
{strings.getPDFPanelOptionsLabel()}
-
- - - - reportLayout === 'canvas' - ? setReportLayout('preserve_layout') - : setReportLayout('canvas') - } - data-test-subj="reportModeToggle" - /> - - onExport(reportLayout)} - size="s" - style={{ width: '100%' }} - data-test-subj="generateReportButton" - > - {strings.getPDFPanelGenerateButtonLabel()} - - - - - -

{strings.getPDFPanelCopyDescription()}

-
- - - - {strings.getPDFPanelCopyButtonLabel()} - - -
-
- ); -}; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/share_menu/share_menu.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/share_menu/share_menu.component.tsx index 0d2e877bebdfd..d4cb4d0736bb1 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/share_menu/share_menu.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/share_menu/share_menu.component.tsx @@ -5,47 +5,47 @@ * 2.0. */ -import React, { FunctionComponent, useState } from 'react'; -import PropTypes from 'prop-types'; import { EuiButtonEmpty, EuiContextMenu, EuiIcon } from '@elastic/eui'; +import { IBasePath } from 'kibana/public'; +import PropTypes from 'prop-types'; +import React, { FunctionComponent, useState } from 'react'; +import { ReportingStart } from '../../../../../reporting/public'; import { ComponentStrings } from '../../../../i18n/components'; import { flattenPanelTree } from '../../../lib/flatten_panel_tree'; -import { Popover, ClosePopoverFn } from '../../popover'; -import { PDFPanel } from './pdf_panel'; +import { ClosePopoverFn, Popover } from '../../popover'; import { ShareWebsiteFlyout } from './flyout'; -import { LayoutType } from './utils'; +import { CanvasWorkpadSharingData, getPdfJobParams } from './utils'; const { WorkpadHeaderShareMenu: strings } = ComponentStrings; type CopyTypes = 'pdf' | 'reportingConfig'; type ExportTypes = 'pdf' | 'json'; -type ExportUrlTypes = 'pdf'; type CloseTypes = 'share'; export type OnCopyFn = (type: CopyTypes) => void; -export type OnExportFn = (type: ExportTypes, layout?: LayoutType) => void; +export type OnExportFn = (type: ExportTypes) => void; export type OnCloseFn = (type: CloseTypes) => void; -export type GetExportUrlFn = (type: ExportUrlTypes, layout: LayoutType) => string; export interface Props { - /** Flag to include the Reporting option only if Reporting is enabled */ - includeReporting: boolean; - /** Handler to invoke when an export URL is copied to the clipboard. */ - onCopy: OnCopyFn; + /** Canvas workpad to export as PDF **/ + sharingData: CanvasWorkpadSharingData; + sharingServices: { + /** BasePath dependency **/ + basePath: IBasePath; + /** Reporting dependency **/ + reporting?: ReportingStart; + }; /** Handler to invoke when an end product is exported. */ onExport: OnExportFn; - /** Handler to retrive an export URL based on the type of export requested. */ - getExportUrl: GetExportUrlFn; } /** * The Menu for Exporting a Workpad from Canvas. */ export const ShareMenu: FunctionComponent = ({ - includeReporting, - onCopy, + sharingData, + sharingServices: services, onExport, - getExportUrl, }) => { const [showFlyout, setShowFlyout] = useState(false); @@ -53,22 +53,6 @@ export const ShareMenu: FunctionComponent = ({ setShowFlyout(false); }; - const getPDFPanel = (closePopover: ClosePopoverFn) => { - return ( - getExportUrl('pdf', layoutType)} - onExport={(layoutType) => { - onExport('pdf', layoutType); - closePopover(); - }} - onCopy={() => { - onCopy('pdf'); - closePopover(); - }} - /> - ); - }; - const getPanelTree = (closePopover: ClosePopoverFn) => ({ id: 0, items: [ @@ -80,14 +64,20 @@ export const ShareMenu: FunctionComponent = ({ closePopover(); }, }, - includeReporting + services.reporting != null ? { name: strings.getShareDownloadPDFTitle(), icon: 'document', panel: { id: 1, title: strings.getShareDownloadPDFTitle(), - content: getPDFPanel(closePopover), + content: ( + getPdfJobParams(sharingData, services.basePath)} + layoutOption="canvas" + onClose={closePopover} + /> + ), }, 'data-test-subj': 'sharePanel-PDFReports', } @@ -132,8 +122,5 @@ export const ShareMenu: FunctionComponent = ({ }; ShareMenu.propTypes = { - includeReporting: PropTypes.bool.isRequired, - onCopy: PropTypes.func.isRequired, onExport: PropTypes.func.isRequired, - getExportUrl: PropTypes.func.isRequired, }; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/share_menu/share_menu.ts b/x-pack/plugins/canvas/public/components/workpad_header/share_menu/share_menu.ts index 47b5e755d439c..fc4906817cf6f 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/share_menu/share_menu.ts +++ b/x-pack/plugins/canvas/public/components/workpad_header/share_menu/share_menu.ts @@ -7,16 +7,12 @@ import { connect } from 'react-redux'; import { compose, withProps } from 'recompose'; -import { jobCompletionNotifications } from '../../../../../../plugins/reporting/public'; -import { getWorkpad, getPages } from '../../../state/selectors/workpad'; -import { getWindow } from '../../../lib/get_window'; +import { ComponentStrings } from '../../../../i18n'; +import { CanvasWorkpad, State } from '../../../../types'; import { downloadWorkpad } from '../../../lib/download_workpad'; -import { ShareMenu as Component, Props as ComponentProps } from './share_menu.component'; -import { getPdfUrl, createPdf } from './utils'; -import { State, CanvasWorkpad } from '../../../../types'; import { withServices, WithServicesProps } from '../../../services'; - -import { ComponentStrings } from '../../../../i18n'; +import { getPages, getWorkpad } from '../../../state/selectors/workpad'; +import { Props as ComponentProps, ShareMenu as Component } from './share_menu.component'; const { WorkpadHeaderShareMenu: strings } = ComponentStrings; @@ -25,17 +21,6 @@ const mapStateToProps = (state: State) => ({ pageCount: getPages(state).length, }); -const getAbsoluteUrl = (path: string) => { - const { location } = getWindow(); - - if (!location) { - return path; - } // fallback for mocked window object - - const { protocol, hostname, port } = location; - return `${protocol}//${hostname}:${port}${path}`; -}; - interface Props { workpad: CanvasWorkpad; pageCount: number; @@ -45,63 +30,28 @@ export const ShareMenu = compose( connect(mapStateToProps), withServices, withProps( - ({ workpad, pageCount, services }: Props & WithServicesProps): ComponentProps => ({ - includeReporting: services.reporting.includeReporting(), - getExportUrl: (type, layout) => { - if (type === 'pdf') { - const pdfUrl = getPdfUrl( - workpad, - layout, - { pageCount }, - services.platform.getBasePathInterface() - ); - return getAbsoluteUrl(pdfUrl); - } - - throw new Error(strings.getUnknownExportErrorMessage(type)); - }, - onCopy: (type) => { - switch (type) { - case 'pdf': - services.notify.info(strings.getCopyPDFMessage()); - break; - case 'reportingConfig': - services.notify.info(strings.getCopyReportingConfigMessage()); - break; - default: - throw new Error(strings.getUnknownExportErrorMessage(type)); - } - }, - onExport: (type, layout) => { - switch (type) { - case 'pdf': - return createPdf( - workpad, - layout || 'preserve_layout', - { pageCount }, - services.platform.getBasePathInterface() - ) - .then(({ data }: { data: { job: { id: string } } }) => { - services.notify.info(strings.getExportPDFMessage(), { - title: strings.getExportPDFTitle(workpad.name), - }); - - // register the job so a completion notification shows up when it's ready - jobCompletionNotifications.add(data.job.id); - }) - .catch((err: Error) => { - services.notify.error(err, { - title: strings.getExportPDFErrorTitle(workpad.name), - 'data-test-subj': 'queueReportError', - }); - }); - case 'json': - downloadWorkpad(workpad.id); - return; - default: - throw new Error(strings.getUnknownExportErrorMessage(type)); - } - }, - }) + ({ workpad, pageCount, services }: Props & WithServicesProps): ComponentProps => { + const { + platform, + reporting: { start: reporting }, + } = services; + + return { + sharingServices: { basePath: platform.getBasePathInterface(), reporting }, + sharingData: { workpad, pageCount }, + onExport: (type) => { + switch (type) { + case 'pdf': + // notifications are automatically handled by the Reporting plugin + break; + case 'json': + downloadWorkpad(workpad.id); + return; + default: + throw new Error(strings.getUnknownExportErrorMessage(type)); + } + }, + }; + } ) )(Component); diff --git a/x-pack/plugins/canvas/public/components/workpad_header/share_menu/utils.test.ts b/x-pack/plugins/canvas/public/components/workpad_header/share_menu/utils.test.ts index df82feb088379..fd6f4bb894991 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/share_menu/utils.test.ts +++ b/x-pack/plugins/canvas/public/components/workpad_header/share_menu/utils.test.ts @@ -7,9 +7,8 @@ jest.mock('../../../../common/lib/fetch'); -import { getPdfUrl, createPdf, LayoutType } from './utils'; +import { getPdfJobParams } from './utils'; import { workpads } from '../../../../__fixtures__/workpads'; -import { fetch } from '../../../../common/lib/fetch'; import { IBasePath } from 'kibana/public'; const basePath = ({ @@ -17,33 +16,36 @@ const basePath = ({ get: () => 'basepath/s/spacey', serverBasePath: `basepath`, } as unknown) as IBasePath; -const workpad = workpads[0]; - -test('getPdfUrl returns the correct url for canvas layout', () => { - ['canvas', 'preserve_layout'].forEach((layout) => { - const url = getPdfUrl(workpad, layout as LayoutType, { pageCount: 2 }, basePath); - - expect(url).toMatchInlineSnapshot( - `"basepath/s/spacey//api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FNew_York,layout:(dimensions:(height:0,width:0),id:${layout}),objectType:'canvas%20workpad',relativeUrls:!(%2Fs%2Fspacey%2Fapp%2Fcanvas%23%2Fexport%2Fworkpad%2Fpdf%2Fbase-workpad%2Fpage%2F1,%2Fs%2Fspacey%2Fapp%2Fcanvas%23%2Fexport%2Fworkpad%2Fpdf%2Fbase-workpad%2Fpage%2F2),title:'base%20workpad')"` - ); - }); -}); - -test('createPdf posts to create the pdf with canvas layout', () => { - ['canvas', 'preserve_layout'].forEach((layout, index) => { - createPdf(workpad, layout as LayoutType, { pageCount: 2 }, basePath); - - expect(fetch.post).toBeCalled(); - - const args = (fetch.post as jest.MockedFunction).mock.calls[index]; - - expect(args[0]).toMatchInlineSnapshot( - `"basepath/s/spacey//api/reporting/generate/printablePdf"` - ); - expect(args[1]).toMatchInlineSnapshot(` - Object { - "jobParams": "(browserTimezone:America/New_York,layout:(dimensions:(height:0,width:0),id:${layout}),objectType:'canvas workpad',relativeUrls:!(/s/spacey/app/canvas#/export/workpad/pdf/base-workpad/page/1,/s/spacey/app/canvas#/export/workpad/pdf/base-workpad/page/2),title:'base workpad')", - } - `); - }); +const workpadSharingData = { workpad: workpads[0], pageCount: 12 }; + +test('getPdfJobParams returns the correct job params for canvas layout', () => { + const jobParams = getPdfJobParams(workpadSharingData, basePath); + expect(jobParams).toMatchInlineSnapshot(` + Object { + "browserTimezone": "America/New_York", + "layout": Object { + "dimensions": Object { + "height": 0, + "width": 0, + }, + "id": "canvas", + }, + "objectType": "canvas workpad", + "relativeUrls": Array [ + "/s/spacey/app/canvas#/export/workpad/pdf/base-workpad/page/1", + "/s/spacey/app/canvas#/export/workpad/pdf/base-workpad/page/2", + "/s/spacey/app/canvas#/export/workpad/pdf/base-workpad/page/3", + "/s/spacey/app/canvas#/export/workpad/pdf/base-workpad/page/4", + "/s/spacey/app/canvas#/export/workpad/pdf/base-workpad/page/5", + "/s/spacey/app/canvas#/export/workpad/pdf/base-workpad/page/6", + "/s/spacey/app/canvas#/export/workpad/pdf/base-workpad/page/7", + "/s/spacey/app/canvas#/export/workpad/pdf/base-workpad/page/8", + "/s/spacey/app/canvas#/export/workpad/pdf/base-workpad/page/9", + "/s/spacey/app/canvas#/export/workpad/pdf/base-workpad/page/10", + "/s/spacey/app/canvas#/export/workpad/pdf/base-workpad/page/11", + "/s/spacey/app/canvas#/export/workpad/pdf/base-workpad/page/12", + ], + "title": "base workpad", + } + `); }); diff --git a/x-pack/plugins/canvas/public/components/workpad_header/share_menu/utils.ts b/x-pack/plugins/canvas/public/components/workpad_header/share_menu/utils.ts index 40797ef097781..9586242bc7386 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/share_menu/utils.ts +++ b/x-pack/plugins/canvas/public/components/workpad_header/share_menu/utils.ts @@ -5,33 +5,24 @@ * 2.0. */ -import rison from 'rison-node'; import { IBasePath } from 'kibana/public'; import moment from 'moment-timezone'; -import { fetch } from '../../../../common/lib/fetch'; +import rison from 'rison-node'; +import { BaseParams } from '../../../../../reporting/common/types'; import { CanvasWorkpad } from '../../../../types'; -import { url } from '../../../../../../../src/plugins/kibana_utils/public'; -interface PageCount { +export interface CanvasWorkpadSharingData { + workpad: Pick; pageCount: number; } -export type LayoutType = 'canvas' | 'preserve_layout'; - -type Arguments = [CanvasWorkpad, LayoutType, PageCount, IBasePath]; - -interface PdfUrlData { - createPdfUri: string; - createPdfPayload: { jobParams: string }; -} +// TODO: get the correct type from Reporting plugin +type JobParamsPDF = BaseParams & { relativeUrls: string[] }; -function getPdfUrlParts( - { id, name: title, width, height }: CanvasWorkpad, - layoutType: LayoutType, - { pageCount }: PageCount, +export function getPdfJobParams( + { workpad: { id, name: title, width, height }, pageCount }: CanvasWorkpadSharingData, basePath: IBasePath -): PdfUrlData { - const reportingEntry = basePath.prepend('/api/reporting/generate'); +): JobParamsPDF { const urlPrefix = basePath.get().replace(basePath.serverBasePath, ''); // for Spaces prefix, which is included in basePath.get() const canvasEntry = `${urlPrefix}/app/canvas#`; @@ -51,34 +42,14 @@ function getPdfUrlParts( workpadUrls.push(rison.encode(`${canvasEntry}/export/workpad/pdf/${id}/page/${i}`)); } - const jobParams = { + return { browserTimezone: moment.tz.guess(), layout: { dimensions: { width, height }, - id: layoutType, + id: 'canvas', }, objectType: 'canvas workpad', relativeUrls: workpadUrls, title, }; - - return { - createPdfUri: `${reportingEntry}/printablePdf`, - createPdfPayload: { - jobParams: rison.encode(jobParams), - }, - }; -} - -export function getPdfUrl(...args: Arguments): string { - const urlParts = getPdfUrlParts(...args); - const param = (key: string, val: any) => - url.encodeUriQuery(key, true) + (val === true ? '' : '=' + url.encodeUriQuery(val, true)); - - return `${urlParts.createPdfUri}?${param('jobParams', urlParts.createPdfPayload.jobParams)}`; -} - -export function createPdf(...args: Arguments) { - const { createPdfUri, createPdfPayload } = getPdfUrlParts(...args); - return fetch.post(createPdfUri, createPdfPayload); } diff --git a/x-pack/plugins/canvas/public/services/reporting.ts b/x-pack/plugins/canvas/public/services/reporting.ts index 3299363cd5c7f..4fa40401472c6 100644 --- a/x-pack/plugins/canvas/public/services/reporting.ts +++ b/x-pack/plugins/canvas/public/services/reporting.ts @@ -5,10 +5,11 @@ * 2.0. */ +import { ReportingStart } from '../../../reporting/public'; import { CanvasServiceFactory } from './'; export interface ReportingService { - includeReporting: () => boolean; + start?: ReportingStart; } export const reportingServiceFactory: CanvasServiceFactory = ( @@ -18,18 +19,24 @@ export const reportingServiceFactory: CanvasServiceFactory = ( startPlugins ): ReportingService => { const { reporting } = startPlugins; + + const reportingEnabled = () => ({ start: reporting }); + const reportingDisabled = () => ({ start: undefined }); + if (!reporting) { // Reporting is not enabled - return { includeReporting: () => false }; + return reportingDisabled(); } if (reporting.usesUiCapabilities()) { - // Canvas has declared Reporting as a subfeature with the `generatePdf` UI Capability - return { - includeReporting: () => coreStart.application.capabilities.canvas?.generatePdf === true, - }; + if (coreStart.application.capabilities.canvas?.generatePdf === true) { + // Canvas has declared Reporting as a subfeature with the `generatePdf` UI Capability + return reportingEnabled(); + } else { + return reportingDisabled(); + } } - // Reporting is enabled as an Elasticsearch feature (Legacy/Deprecated) - return { includeReporting: () => true }; + // Legacy/Deprecated: Reporting is enabled as an Elasticsearch feature + return reportingEnabled(); }; diff --git a/x-pack/plugins/canvas/public/services/stubs/reporting.ts b/x-pack/plugins/canvas/public/services/stubs/reporting.ts index f257dd14543ec..b32dfc51cbfde 100644 --- a/x-pack/plugins/canvas/public/services/stubs/reporting.ts +++ b/x-pack/plugins/canvas/public/services/stubs/reporting.ts @@ -8,5 +8,16 @@ import { ReportingService } from '../reporting'; export const reportingService: ReportingService = { - includeReporting: () => true, + start: { + usesUiCapabilities: () => true, + components: { + ReportingPanelPDF: () => (null as unknown) as JSX.Element, + }, + getDefaultLayoutSelectors: () => ({ + screenshot: 'stub', + renderComplete: 'stub', + itemsCountAttribute: 'stub', + timefilterDurationAttribute: 'stub', + }), + }, }; diff --git a/x-pack/plugins/reporting/common/constants.ts b/x-pack/plugins/reporting/common/constants.ts index 2a95557473fc0..f20c6c2d52fdd 100644 --- a/x-pack/plugins/reporting/common/constants.ts +++ b/x-pack/plugins/reporting/common/constants.ts @@ -53,6 +53,7 @@ export const UI_SETTINGS_CSV_QUOTE_VALUES = 'csv:quoteValues'; export const UI_SETTINGS_DATEFORMAT_TZ = 'dateFormat:tz'; export const LAYOUT_TYPES = { + CANVAS: 'canvas', PRESERVE_LAYOUT: 'preserve_layout', PRINT: 'print', }; diff --git a/x-pack/plugins/reporting/public/components/__snapshots__/screen_capture_panel_content.test.tsx.snap b/x-pack/plugins/reporting/public/components/__snapshots__/screen_capture_panel_content.test.tsx.snap new file mode 100644 index 0000000000000..a6753211fba3b --- /dev/null +++ b/x-pack/plugins/reporting/public/components/__snapshots__/screen_capture_panel_content.test.tsx.snap @@ -0,0 +1,1191 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ScreenCapturePanelContent properly renders a view with "canvas" layout option 1`] = ` + +
+ +
+

+ + + Analytical Apps can take a minute or two to generate based upon the size of your test-object-type. + + +

+
+
+ +
+ + + } + labelType="label" + > +
+
+ + } + onBlur={[Function]} + onChange={[Function]} + onFocus={[Function]} + > +
+ + + + + Full page layout + + + +
+
+ +
+ + + Remove borders and footer logo + + +
+
+
+
+
+ + + + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+ + +
+

+ + + Alternatively, copy this POST URL to call generation from outside Kibana or from Watcher. + + +

+
+
+ +
+ + + + + + + + + + + + +
+
+ +
+
+ +
+ +`; + +exports[`ScreenCapturePanelContent properly renders a view with "print" layout option 1`] = ` + +
+ +
+

+ + + Analytical Apps can take a minute or two to generate based upon the size of your test-object-type. + + +

+
+
+ +
+ + + } + labelType="label" + > +
+
+ + } + onBlur={[Function]} + onChange={[Function]} + onFocus={[Function]} + > +
+ + + + + Optimize for printing + + + +
+
+ +
+ + + Uses multiple pages, showing at most 2 visualizations per page + + +
+
+
+
+
+ + + + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+ + +
+

+ + + Alternatively, copy this POST URL to call generation from outside Kibana or from Watcher. + + +

+
+
+ +
+ + + + + + + + + + + + +
+
+ +
+
+ +
+ +`; + +exports[`ScreenCapturePanelContent renders the default view properly 1`] = ` + +
+ +
+

+ + + Analytical Apps can take a minute or two to generate based upon the size of your test-object-type. + + +

+
+
+ +
+ + + + + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+ + +
+

+ + + Alternatively, copy this POST URL to call generation from outside Kibana or from Watcher. + + +

+
+
+ +
+ + + + + + + + + + + + +
+
+ +
+
+ +
+ +`; diff --git a/x-pack/plugins/reporting/public/components/index.ts b/x-pack/plugins/reporting/public/components/index.ts index 4adcc347dc704..b8cccda2a6613 100644 --- a/x-pack/plugins/reporting/public/components/index.ts +++ b/x-pack/plugins/reporting/public/components/index.ts @@ -11,3 +11,4 @@ export { getWarningFormulasToast } from './job_warning_formulas'; export { getWarningMaxSizeToast } from './job_warning_max_size'; export { getGeneralErrorToast } from './general_error'; export { ScreenCapturePanelContent } from './screen_capture_panel_content'; +export { getSharedComponents } from './shared'; diff --git a/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx index a38c37c808689..4d7828b789407 100644 --- a/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx +++ b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx @@ -5,7 +5,17 @@ * 2.0. */ -import { EuiButton, EuiCopy, EuiForm, EuiFormRow, EuiSpacer, EuiText } from '@elastic/eui'; +import { + EuiAccordion, + EuiButton, + EuiCopy, + EuiForm, + EuiFormRow, + EuiHorizontalRule, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React, { Component, ReactElement } from 'react'; import { ToastsSetup } from 'src/core/public'; @@ -20,14 +30,12 @@ export interface Props { toasts: ToastsSetup; reportType: string; - /** - * Whether the report to be generated requires saved state that is not captured in the URL submitted to the report generator. - */ + /** Whether the report to be generated requires saved state that is not captured in the URL submitted to the report generator. **/ requiresSavedState: boolean; layoutId: string | undefined; objectId?: string; getJobParams: () => BaseParams; - options?: ReactElement; + options?: ReactElement | null; isDirty?: boolean; onClose?: () => void; intl: InjectedIntl; @@ -110,50 +118,61 @@ class ReportingPanelContentUi extends Component { ); } - const reportMsg = ( - - ); - return ( -

{reportMsg}

+

+ +

{this.props.options} {this.renderGenerateReportButton(false)} - - -

- -

-
- + - - {(copy) => ( - + + + +

- - )} - +

+
+ + + + {(copy) => ( + + + + )} + +
); } @@ -247,44 +266,12 @@ class ReportingPanelContentUi extends Component { } }) .catch((error: any) => { - if (error.message === 'not exportable') { - return this.props.toasts.addWarning({ - title: intl.formatMessage( - { - id: 'xpack.reporting.panelContent.whatCanBeExportedWarningTitle', - defaultMessage: 'Only saved {objectType} can be exported', - }, - { objectType: this.state.objectType } - ), - text: toMountPoint( - - ), - }); - } - - const defaultMessage = - error?.res?.status === 403 ? ( - - ) : ( - - ); - - this.props.toasts.addDanger({ + this.props.toasts.addError(error, { title: intl.formatMessage({ id: 'xpack.reporting.panelContent.notification.reportingErrorTitle', - defaultMessage: 'Reporting error', + defaultMessage: 'Failed to create report', }), - text: toMountPoint(error.message || defaultMessage), - 'data-test-subj': 'queueReportError', + toastMessage: error.body.message, }); }); }; diff --git a/x-pack/plugins/reporting/public/components/screen_capture_panel_content.test.tsx b/x-pack/plugins/reporting/public/components/screen_capture_panel_content.test.tsx new file mode 100644 index 0000000000000..84a6dcc3c0ba3 --- /dev/null +++ b/x-pack/plugins/reporting/public/components/screen_capture_panel_content.test.tsx @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mount } from 'enzyme'; +import React from 'react'; +import { IntlProvider } from 'react-intl'; +import { coreMock } from '../../../../../src/core/public/mocks'; +import { BaseParams } from '../../common/types'; +import { ReportingAPIClient } from '../lib/reporting_api_client'; +import { ScreenCapturePanelContent } from './screen_capture_panel_content'; + +const getJobParamsDefault: () => BaseParams = () => ({ + objectType: 'test-object-type', + title: 'Test Report Title', + browserTimezone: 'America/New_York', +}); + +test('ScreenCapturePanelContent renders the default view properly', () => { + const coreSetup = coreMock.createSetup(); + const component = mount( + + + + ); + expect(component.find('EuiForm')).toMatchSnapshot(); + expect(component.text()).not.toMatch('Full page layout'); + expect(component.text()).not.toMatch('Optimize for printing'); +}); + +test('ScreenCapturePanelContent properly renders a view with "canvas" layout option', () => { + const coreSetup = coreMock.createSetup(); + const component = mount( + + + + ); + expect(component.find('EuiForm')).toMatchSnapshot(); + expect(component.text()).toMatch('Full page layout'); +}); + +test('ScreenCapturePanelContent properly renders a view with "print" layout option', () => { + const coreSetup = coreMock.createSetup(); + const component = mount( + + + + ); + expect(component.find('EuiForm')).toMatchSnapshot(); + expect(component.text()).toMatch('Optimize for printing'); +}); diff --git a/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx b/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx index 21497ed289156..fd6003f8656e8 100644 --- a/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx +++ b/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx @@ -5,11 +5,13 @@ * 2.0. */ -import { EuiSpacer, EuiSwitch, EuiSwitchEvent } from '@elastic/eui'; +import { EuiFormRow, EuiSwitch, EuiSwitchEvent } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import React, { Component, Fragment } from 'react'; +import moment from 'moment'; +import React, { Component } from 'react'; import { ToastsSetup } from 'src/core/public'; -import { BaseParams } from '../../common/types'; +import { getDefaultLayoutSelectors } from '../../common'; +import { BaseParams, LayoutParams } from '../../common/types'; import { ReportingAPIClient } from '../lib/reporting_api_client'; import { ReportingPanelContent } from './reporting_panel_content'; @@ -17,33 +19,33 @@ export interface Props { apiClient: ReportingAPIClient; toasts: ToastsSetup; reportType: string; + layoutOption?: 'canvas' | 'print'; objectId?: string; getJobParams: () => BaseParams; + requiresSavedState: boolean; isDirty?: boolean; onClose?: () => void; } interface State { - isPreserveLayoutSupported: boolean; usePrintLayout: boolean; + useCanvasLayout: boolean; } export class ScreenCapturePanelContent extends Component { constructor(props: Props) { super(props); - const { objectType } = props.getJobParams(); - const isPreserveLayoutSupported = props.reportType !== 'png' && objectType !== 'visualization'; this.state = { - isPreserveLayoutSupported, usePrintLayout: false, + useCanvasLayout: false, }; } public render() { return ( { } private renderOptions = () => { - if (this.state.isPreserveLayoutSupported) { + if (this.props.layoutOption === 'print') { return ( - + + } + > { onChange={this.handlePrintLayoutChange} data-test-subj="usePrintLayout" /> - - + ); } - return ( - - - - ); + if (this.props.layoutOption === 'canvas') { + return ( + + } + > + + } + checked={this.state.useCanvasLayout} + onChange={this.handleCanvasLayoutChange} + data-test-subj="reportModeToggle" + /> + + ); + } + + return null; }; private handlePrintLayoutChange = (evt: EuiSwitchEvent) => { - this.setState({ usePrintLayout: evt.target.checked }); + this.setState({ usePrintLayout: evt.target.checked, useCanvasLayout: false }); }; - private getLayout = () => { + private handleCanvasLayoutChange = (evt: EuiSwitchEvent) => { + this.setState({ useCanvasLayout: evt.target.checked, usePrintLayout: false }); + }; + + private getLayout = (): Required => { + const { layout: outerLayout } = this.props.getJobParams(); + + 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 }; + } + + let selectors = outerLayout?.selectors; + if (!selectors) { + selectors = getDefaultLayoutSelectors(); + } + if (this.state.usePrintLayout) { - return { id: 'print' }; + return { id: 'print', dimensions, selectors }; } - const el = document.querySelector('[data-shared-items-container]'); - const bounds = el ? el.getBoundingClientRect() : { height: 768, width: 1024 }; + if (this.state.useCanvasLayout) { + return { id: 'canvas', dimensions, selectors }; + } - return { - id: this.props.reportType === 'png' ? 'png' : 'preserve_layout', - dimensions: { - height: bounds.height, - width: bounds.width, - }, - }; + return { id: 'preserve_layout', dimensions, selectors }; }; - private getJobParams = () => { + private getJobParams = (): Required => { + const outerParams = this.props.getJobParams(); + let browserTimezone = outerParams.browserTimezone; + if (!browserTimezone) { + browserTimezone = moment.tz.guess(); + } + return { ...this.props.getJobParams(), layout: this.getLayout(), + browserTimezone, }; }; } diff --git a/x-pack/plugins/reporting/public/components/shared/get_shared_components.tsx b/x-pack/plugins/reporting/public/components/shared/get_shared_components.tsx new file mode 100644 index 0000000000000..12d70c8701975 --- /dev/null +++ b/x-pack/plugins/reporting/public/components/shared/get_shared_components.tsx @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CoreSetup } from 'kibana/public'; +import React from 'react'; +import { ReportingAPIClient } from '../..'; +import { PDF_REPORT_TYPE } from '../../../common/constants'; +import type { Props as PanelPropsScreenCapture } from '../screen_capture_panel_content'; +import { ScreenCapturePanelContent } from '../screen_capture_panel_content_lazy'; + +interface IncludeOnCloseFn { + onClose: () => void; +} + +type PropsPDF = Pick & IncludeOnCloseFn; + +/* + * As of 7.14, the only shared component is a PDF report that is suited for Canvas integration. + * This is not planned to expand, as work is to be done on moving the export-type implementations out of Reporting + * Related Discuss issue: https://github.com/elastic/kibana/issues/101422 + */ +export function getSharedComponents(core: CoreSetup) { + return { + ReportingPanelPDF(props: PropsPDF) { + return ( + + ); + }, + }; +} diff --git a/x-pack/plugins/reporting/public/components/shared/index.tsx b/x-pack/plugins/reporting/public/components/shared/index.tsx new file mode 100644 index 0000000000000..592538f1b84cc --- /dev/null +++ b/x-pack/plugins/reporting/public/components/shared/index.tsx @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { getSharedComponents } from './get_shared_components'; diff --git a/x-pack/plugins/reporting/public/index.ts b/x-pack/plugins/reporting/public/index.ts index 7c82fda7554d4..7179a81664b6f 100644 --- a/x-pack/plugins/reporting/public/index.ts +++ b/x-pack/plugins/reporting/public/index.ts @@ -7,24 +7,20 @@ import { PluginInitializerContext } from 'src/core/public'; import { getDefaultLayoutSelectors } from '../common'; -import { ScreenCapturePanelContent } from './components/screen_capture_panel_content'; -import * as jobCompletionNotifications from './lib/job_completion_notifications'; +import { getSharedComponents } from './components'; import { ReportingAPIClient } from './lib/reporting_api_client'; import { ReportingPublicPlugin } from './plugin'; export interface ReportingSetup { - components: { - ScreenCapturePanel: typeof ScreenCapturePanelContent; - }; getDefaultLayoutSelectors: typeof getDefaultLayoutSelectors; - ReportingAPIClient: typeof ReportingAPIClient; usesUiCapabilities: () => boolean; + components: ReturnType; } export type ReportingStart = ReportingSetup; export { constants, getDefaultLayoutSelectors } from '../common'; -export { ReportingAPIClient, ReportingPublicPlugin as Plugin, jobCompletionNotifications }; +export { ReportingAPIClient, ReportingPublicPlugin as Plugin }; export function plugin(initializerContext: PluginInitializerContext) { return new ReportingPublicPlugin(initializerContext); diff --git a/x-pack/plugins/reporting/public/mocks.ts b/x-pack/plugins/reporting/public/mocks.ts new file mode 100644 index 0000000000000..414d1b0ae70fe --- /dev/null +++ b/x-pack/plugins/reporting/public/mocks.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { coreMock } from 'src/core/public/mocks'; +import { ReportingSetup } from '.'; +import { getDefaultLayoutSelectors } from '../common'; +import { getSharedComponents } from './components/shared'; + +type Setup = jest.Mocked; + +const createSetupContract = (): Setup => { + const coreSetup = coreMock.createSetup(); + return { + getDefaultLayoutSelectors: jest.fn().mockImplementation(getDefaultLayoutSelectors), + usesUiCapabilities: jest.fn().mockImplementation(() => true), + components: getSharedComponents(coreSetup), + }; +}; + +export const reportingPluginMock = { + createSetupContract, + createStartContract: createSetupContract, +}; diff --git a/x-pack/plugins/reporting/public/plugin.ts b/x-pack/plugins/reporting/public/plugin.ts index ff0d425faf54a..577732fdb1392 100644 --- a/x-pack/plugins/reporting/public/plugin.ts +++ b/x-pack/plugins/reporting/public/plugin.ts @@ -29,10 +29,7 @@ import { constants, getDefaultLayoutSelectors } from '../common'; import { durationToNumber } from '../common/schema_utils'; import { JobId, JobSummarySet } from '../common/types'; import { ReportingSetup, ReportingStart } from './'; -import { - getGeneralErrorToast, - ScreenCapturePanelContent as ScreenCapturePanel, -} from './components'; +import { getGeneralErrorToast, getSharedComponents } from './components'; import { ReportingAPIClient } from './lib/reporting_api_client'; import { ReportingNotifierStreamHandler as StreamHandler } from './lib/stream_handler'; import { ReportingCsvPanelAction } from './panel_actions/get_csv_panel_action'; @@ -86,7 +83,6 @@ export class ReportingPublicPlugin ReportingPublicPluginSetupDendencies, ReportingPublicPluginStartDendencies > { - private readonly contract: ReportingStart; private readonly stop$ = new Rx.ReplaySubject(1); private readonly title = i18n.translate('xpack.reporting.management.reportingTitle', { defaultMessage: 'Reporting', @@ -95,21 +91,30 @@ export class ReportingPublicPlugin defaultMessage: 'Reporting', }); private config: ClientConfigType; + private contract?: ReportingSetup; constructor(initializerContext: PluginInitializerContext) { this.config = initializerContext.config.get(); + } + + private getContract(core?: CoreSetup) { + if (core) { + this.contract = { + getDefaultLayoutSelectors, + usesUiCapabilities: () => this.config.roles?.enabled === false, + components: getSharedComponents(core), + }; + } - this.contract = { - ReportingAPIClient, - components: { ScreenCapturePanel }, - getDefaultLayoutSelectors, - usesUiCapabilities: () => this.config.roles?.enabled === false, - }; + if (!this.contract) { + throw new Error(`Setup error in Reporting plugin!`); + } + + return this.contract; } public setup(core: CoreSetup, setupDeps: ReportingPublicPluginSetupDendencies) { - const { http, notifications, getStartServices, uiSettings } = core; - const { toasts } = notifications; + const { http, getStartServices, uiSettings } = core; const { home, management, @@ -163,6 +168,9 @@ export class ReportingPublicPlugin new ReportingCsvPanelAction({ core, startServices$, license$, usesUiCapabilities }) ); + const reportingStart = this.getContract(core); + const { toasts } = core.notifications; + share.register( ReportingCsvShareProvider({ apiClient, @@ -173,6 +181,7 @@ export class ReportingPublicPlugin usesUiCapabilities, }) ); + share.register( reportingScreenshotShareProvider({ apiClient, @@ -184,7 +193,7 @@ export class ReportingPublicPlugin }) ); - return this.contract; + return reportingStart; } public start(core: CoreStart) { @@ -203,7 +212,7 @@ export class ReportingPublicPlugin ) .subscribe(); - return this.contract; + return this.getContract(); } public stop() { diff --git a/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx index fc732b0ec7dfb..7fe5268fc9910 100644 --- a/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx @@ -65,13 +65,7 @@ export const ReportingCsvShareProvider = ({ ? moment.tz.guess() : uiSettings.get('dateFormat:tz'); - const getShareMenuItems = ({ - objectType, - objectId, - sharingData, - onClose, - isDirty, - }: ShareContext) => { + const getShareMenuItems = ({ objectType, objectId, sharingData, onClose }: ShareContext) => { if ('search' !== objectType) { return []; } @@ -114,7 +108,6 @@ export const ReportingCsvShareProvider = ({ layoutId={undefined} objectId={objectId} getJobParams={getJobParams} - isDirty={isDirty} onClose={onClose} /> ), diff --git a/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx b/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx index f4a952ef58298..42f6ee5fcb898 100644 --- a/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx @@ -11,7 +11,7 @@ import React from 'react'; import * as Rx from 'rxjs'; import type { IUiSettingsClient, ToastsSetup } from 'src/core/public'; import { CoreStart } from 'src/core/public'; -import type { ShareContext } from '../../../../../src/plugins/share/public'; +import { ShareContext } from 'src/plugins/share/public'; import type { LicensingPluginSetup } from '../../../licensing/public'; import type { LayoutParams } from '../../common/types'; import type { JobParamsPNG } from '../../server/export_types/png/types'; @@ -167,6 +167,7 @@ export const reportingScreenshotShareProvider = ({ toasts={toasts} reportType="png" objectId={objectId} + requiresSavedState={true} getJobParams={getPngJobParams({ shareableUrl, apiClient, @@ -203,6 +204,8 @@ export const reportingScreenshotShareProvider = ({ toasts={toasts} reportType="printablePdf" objectId={objectId} + requiresSavedState={true} + layoutOption={objectType === 'dashboard' ? 'print' : undefined} getJobParams={getPdfJobParams({ shareableUrl, apiClient, diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 4bc03b4ba6e2a..8066ca178d34f 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -7003,20 +7003,7 @@ "xpack.canvas.workpadHeaderRefreshControlSettings.refreshAriaLabel": "エレメントを更新", "xpack.canvas.workpadHeaderRefreshControlSettings.refreshTooltip": "データを更新", "xpack.canvas.workpadHeaderShareMenu.copyPDFMessage": "{PDF}生成{URL}がクリップボードにコピーされました。", - "xpack.canvas.workpadHeaderShareMenu.copyReportingConfigMessage": "レポート構成がクリップボードにコピーされました", "xpack.canvas.workpadHeaderShareMenu.copyShareConfigMessage": "共有マークアップがクリップボードにコピーされました", - "xpack.canvas.workpadHeaderShareMenu.exportPDFErrorMessage": "'{workpadName}'の{PDF}を作成できませんでした", - "xpack.canvas.workpadHeaderShareMenu.exportPDFMessage": "{PDF}をエクスポートしています。管理で進捗を確認できます。", - "xpack.canvas.workpadHeaderShareMenu.exportPDFTitle": "ワークパッド '{workpadName}' の {PDF} エクスポート", - "xpack.canvas.workpadHeaderShareMenu.FullPageLayoutHelpText": "枠線とフッターロゴを削除", - "xpack.canvas.workpadHeaderShareMenu.FullPageLayoutLabel": "全ページレイアウト", - "xpack.canvas.workpadHeaderShareMenu.pdfPanelAdvancedOptionsLabel": "高度なオプション", - "xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyAriaLabel": "この {URL} を使用してスクリプトから、または Watcher で {PDF} を生成することもできます。{URL}をクリップボードにコピーするにはEnterキーを押してください。", - "xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyButtonLabel": "{POST} {URL}をコピー", - "xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyDescription": "{POST} {URL}をコピーして{KIBANA}外またはWatcherから生成を実行することもできます。", - "xpack.canvas.workpadHeaderShareMenu.pdfPanelGenerateButtonLabel": "{PDF}を生成", - "xpack.canvas.workpadHeaderShareMenu.pdfPanelGenerateDescription": "ワークパッドのサイズによって、{PDF} の生成には数分かかる場合があります。", - "xpack.canvas.workpadHeaderShareMenu.pdfPanelOptionsLabel": "オプション", "xpack.canvas.workpadHeaderShareMenu.shareDownloadJSONTitle": "{JSON} をダウンロード", "xpack.canvas.workpadHeaderShareMenu.shareDownloadPDFTitle": "{PDF}レポート", "xpack.canvas.workpadHeaderShareMenu.shareMenuButtonLabel": "共有", @@ -17879,14 +17866,10 @@ "xpack.reporting.panelContent.generateButtonLabel": "{reportingType} を生成", "xpack.reporting.panelContent.generationTimeDescription": "{objectType} のサイズによって、{reportingType} の作成には数分かかる場合があります。", "xpack.reporting.panelContent.howToCallGenerationDescription": "POST URL をコピーして Kibana 外または ウォッチャー から生成を実行することもできます。", - "xpack.reporting.panelContent.noPermissionToGenerateReportDescription": "このレポートを生成するパーミッションがありません。", - "xpack.reporting.panelContent.notification.cantReachServerDescription": "サーバーと通信できません。再試行してください。", "xpack.reporting.panelContent.notification.reportingErrorTitle": "レポートエラー", "xpack.reporting.panelContent.saveWorkDescription": "レポートの生成前に変更内容を保存してください。", "xpack.reporting.panelContent.successfullyQueuedReportNotificationDescription": "{path}で進捗状況を追跡", "xpack.reporting.panelContent.successfullyQueuedReportNotificationTitle": "{objectType} のレポートキュー", - "xpack.reporting.panelContent.whatCanBeExportedWarningDescription": "初めに変更内容を保存してください", - "xpack.reporting.panelContent.whatCanBeExportedWarningTitle": "保存された {objectType} のみエクスポートできます", "xpack.reporting.pdfFooterImageDescription": "PDFのフッターに使用するカスタム画像です", "xpack.reporting.pdfFooterImageLabel": "PDFフッター画像", "xpack.reporting.publicNotifier.csvContainsFormulas.formulaReportMessage": "レポートには、スプレッドシートアプリケーションで式と解釈される可能性のある文字が含まれています。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 46bdb83c42528..33f5d4aceb51c 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -7051,20 +7051,7 @@ "xpack.canvas.workpadHeaderRefreshControlSettings.refreshAriaLabel": "刷新元素", "xpack.canvas.workpadHeaderRefreshControlSettings.refreshTooltip": "刷新数据", "xpack.canvas.workpadHeaderShareMenu.copyPDFMessage": "{PDF} 生成 {URL} 已复制到您的剪贴板。", - "xpack.canvas.workpadHeaderShareMenu.copyReportingConfigMessage": "已将报告配置复制到剪贴板", "xpack.canvas.workpadHeaderShareMenu.copyShareConfigMessage": "已将共享标记复制到剪贴板", - "xpack.canvas.workpadHeaderShareMenu.exportPDFErrorMessage": "无法为“{workpadName}”创建 {PDF}", - "xpack.canvas.workpadHeaderShareMenu.exportPDFMessage": "正在导出 {PDF}。可以在“管理”中跟踪进度。", - "xpack.canvas.workpadHeaderShareMenu.exportPDFTitle": "Workpad“{workpadName}”的 {PDF} 导出", - "xpack.canvas.workpadHeaderShareMenu.FullPageLayoutHelpText": "删除边框和页脚徽标", - "xpack.canvas.workpadHeaderShareMenu.FullPageLayoutLabel": "全页面布局", - "xpack.canvas.workpadHeaderShareMenu.pdfPanelAdvancedOptionsLabel": "高级选项", - "xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyAriaLabel": "或者,也可以从脚本或使用此 {URL} 通过 Watcher 生成 {PDF}。按 Enter 键可将 {URL} 复制到剪贴板。", - "xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyButtonLabel": "复制 {POST} {URL}", - "xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyDescription": "或者,复制此 {POST} {URL} 以从 {KIBANA} 外部或从 Watcher 调用生成。", - "xpack.canvas.workpadHeaderShareMenu.pdfPanelGenerateButtonLabel": "生成 {PDF}", - "xpack.canvas.workpadHeaderShareMenu.pdfPanelGenerateDescription": "{PDF} 可能会花费一两分钟生成,具体取决于 Workpad 的大小。", - "xpack.canvas.workpadHeaderShareMenu.pdfPanelOptionsLabel": "选项", "xpack.canvas.workpadHeaderShareMenu.shareDownloadJSONTitle": "下载为 {JSON}", "xpack.canvas.workpadHeaderShareMenu.shareDownloadPDFTitle": "{PDF} 报告", "xpack.canvas.workpadHeaderShareMenu.shareMenuButtonLabel": "共享", @@ -18120,14 +18107,10 @@ "xpack.reporting.panelContent.generateButtonLabel": "生成 {reportingType}", "xpack.reporting.panelContent.generationTimeDescription": "{reportingType} 可能会花费 1 或 2 分钟生成,取决于 {objectType} 的大小。", "xpack.reporting.panelContent.howToCallGenerationDescription": "或者,复制此 POST URL 以从 Kibana 外部或从 Watcher 调用生成。", - "xpack.reporting.panelContent.noPermissionToGenerateReportDescription": "您无权生成此报告。", - "xpack.reporting.panelContent.notification.cantReachServerDescription": "无法访问服务器。请重试。", "xpack.reporting.panelContent.notification.reportingErrorTitle": "报告错误", "xpack.reporting.panelContent.saveWorkDescription": "请在生成报告之前保存您的工作。", "xpack.reporting.panelContent.successfullyQueuedReportNotificationDescription": "在 {path} 中跟踪其进度", "xpack.reporting.panelContent.successfullyQueuedReportNotificationTitle": "已为 {objectType} 排队报告", - "xpack.reporting.panelContent.whatCanBeExportedWarningDescription": "请先保存您的工作", - "xpack.reporting.panelContent.whatCanBeExportedWarningTitle": "只会导出保存的 {objectType}", "xpack.reporting.pdfFooterImageDescription": "要在 PDF 的页脚中使用的定制图像", "xpack.reporting.pdfFooterImageLabel": "PDF 页脚图像", "xpack.reporting.publicNotifier.csvContainsFormulas.formulaReportMessage": "报告包含电子表格应用程序可解释为公式的字符。", diff --git a/x-pack/test/functional/page_objects/reporting_page.ts b/x-pack/test/functional/page_objects/reporting_page.ts index e8999999ce50b..742d41031004b 100644 --- a/x-pack/test/functional/page_objects/reporting_page.ts +++ b/x-pack/test/functional/page_objects/reporting_page.ts @@ -91,7 +91,7 @@ export class ReportingPageObject extends FtrService { } async getQueueReportError() { - return await this.testSubjects.exists('queueReportError'); + return await this.testSubjects.exists('errorToastMessage'); } async getGenerateReportButton() {