Adds options to download workpad from an active workpad (#28131) (#28423)

* Change download icon from sortDown to exportAction in workpad loader

* Added context menu and download menu item to workpad export popover

* Updated icon on download button in workpad loader

* Added TODO in workpad export

* Added copy to clipboard on click to disabled reporting panel view
This commit is contained in:
Catherine Liu 2019-01-09 17:11:41 -07:00 committed by GitHub
parent bea81ad0c6
commit 1ba322c4a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 190 additions and 85 deletions

View file

@ -28,7 +28,7 @@ export class Clipboard extends React.PureComponent {
render() {
return (
<div className="canvas_clipboard" onClick={this.onClick}>
<div className="canvasClipboard" onClick={this.onClick}>
{this.props.children}
</div>
);

View file

@ -0,0 +1,3 @@
.canvasClipboard {
cursor: pointer;
}

View file

@ -13,6 +13,7 @@ import { getWorkpad, getPages } from '../../state/selectors/workpad';
import { getReportingBrowserType } from '../../state/selectors/app';
import { notify } from '../../lib/notify';
import { getWindow } from '../../lib/get_window';
import { downloadWorkpad } from '../../lib/download_workpad';
import { WorkpadExport as Component } from './workpad_export';
import { getPdfUrl, createPdf } from './utils';
@ -43,29 +44,34 @@ export const WorkpadExport = compose(
throw new Error(`Unknown export type: ${type}`);
},
onCopy: type => {
if (type === 'pdf') {
return notify.info('The PDF generation URL was copied to your clipboard.');
switch (type) {
case 'pdf':
return notify.info('The PDF generation URL was copied to your clipboard.');
case 'reportingConfig':
return notify.info(`Copied reporting configuration to clipboard`);
}
throw new Error(`Unknown export type: ${type}`);
},
onExport: type => {
if (type === 'pdf') {
return createPdf(workpad, { pageCount })
.then(({ data }) => {
notify.info('Exporting PDF. You can track the progress in Management.', {
title: `PDF export of workpad '${workpad.name}'`,
switch (type) {
case 'pdf':
return createPdf(workpad, { pageCount })
.then(({ data }) => {
notify.info('Exporting PDF. You can track the progress in Management.', {
title: `PDF export of workpad '${workpad.name}'`,
});
// register the job so a completion notification shows up when it's ready
jobCompletionNotifications.add(data.job.id);
})
.catch(err => {
notify.error(err, { title: `Failed to create PDF for '${workpad.name}'` });
});
// register the job so a completion notification shows up when it's ready
jobCompletionNotifications.add(data.job.id);
})
.catch(err => {
notify.error(err, { title: `Failed to create PDF for '${workpad.name}'` });
});
case 'json':
return downloadWorkpad(workpad.id);
default:
throw new Error(`Unknown export type: ${type}`);
}
throw new Error(`Unknown export type: ${type}`);
},
}))
)(Component);

View file

@ -9,12 +9,12 @@ import PropTypes from 'prop-types';
import {
EuiButton,
EuiButtonIcon,
EuiFlexGroup,
EuiFlexItem,
EuiSpacer,
EuiCodeBlock,
EuiHorizontalRule,
EuiFormRow,
EuiCode,
EuiContextMenu,
EuiIcon,
EuiText,
} from '@elastic/eui';
import { Popover } from '../popover';
import { Clipboard } from '../clipboard';
@ -27,81 +27,155 @@ export class WorkpadExport extends React.PureComponent {
getExportUrl: PropTypes.func.isRequired,
};
anchorElement = React.createRef();
flattenPanelTree(tree, array = []) {
array.push(tree);
if (tree.items) {
tree.items.forEach(item => {
if (item.panel) {
this.flattenPanelTree(item.panel, array);
item.panel = item.panel.id;
}
});
}
return array;
}
exportPdf = () => {
this.props.onExport('pdf');
};
renderControls = closePopover => {
downloadWorkpad = () => {
this.props.onExport('json');
};
renderPDFControls = closePopover => {
const pdfUrl = this.props.getExportUrl('pdf');
return (
<div>
<EuiFlexGroup justifyContent="spaceAround">
<EuiFlexItem grow>
<EuiFormRow label="Click below to create a PDF. You'll be notified when the export is complete">
<EuiButton
onClick={() => {
this.exportPdf();
closePopover();
}}
>
Export as PDF
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule size="half" />
<EuiFormRow label="To generate a PDF from a script or with Watcher, use this URL.">
<EuiFlexGroup alignItems="center">
<EuiFlexItem style={{ overflow: 'auto' }}>
<EuiCodeBlock style={{ whiteSpace: 'nowrap' }} paddingSize="s">
{pdfUrl}
</EuiCodeBlock>
</EuiFlexItem>
<div className="canvasWorkpadExport__panelContent">
<EuiText size="s">
<p>PDFs can take a minute or two to generate based upon the size of your workpad</p>
</EuiText>
<EuiSpacer size="s" />
<EuiFlexItem grow={false}>
<Clipboard
content={pdfUrl}
onCopy={() => {
this.props.onCopy('pdf');
closePopover();
}}
>
<EuiButtonIcon aria-label="Copy to clipboard" iconType="copy" />
</Clipboard>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFormRow>
{this.props.options}
<EuiButton
fill
onClick={() => {
closePopover();
this.exportPdf();
}}
size="s"
style={{ width: '100%' }}
>
Generate PDF
</EuiButton>
<EuiSpacer size="s" />
<EuiText size="s">
<p>
Alternatively, copy this POST URL to call generation from outside Kibana or from
Watcher.
</p>
</EuiText>
<EuiSpacer size="s" />
<Clipboard
content={pdfUrl}
onCopy={() => {
this.props.onCopy('pdf');
closePopover();
}}
>
<EuiButton
aria-label="Copy to clipboard"
iconType="copy"
size="s"
style={{ width: '100%' }}
>
Copy POST URL
</EuiButton>
</Clipboard>
</div>
);
};
renderPanelTree = closePopover => ({
id: 0,
title: 'Share this workpad',
items: [
{
name: 'Download as JSON',
icon: <EuiIcon type="exportAction" size="m" />,
onClick: () => {
closePopover();
this.downloadWorkpad();
},
},
{
name: 'PDF Reports',
icon: 'document',
panel: {
id: 1,
title: 'PDF Reports',
content: this.props.enabled
? this.renderPDFControls(closePopover)
: this.renderDisabled(),
},
},
],
});
renderDisabled = () => {
const reportingConfig = `xpack.reporting:
enabled: true
capture.browser.type: chromium`;
return (
<div>
Export to PDF is disabled. You must configure reporting to use the Chromium browser. Add
this to your kibana.yml file.
<div className="canvasWorkpadExport__panelContent">
<EuiText size="s">
<p>
Export to PDF is disabled. You must configure reporting to use the Chromium browser. Add
this to your <EuiCode>kibana.yml</EuiCode> file.
</p>
</EuiText>
<EuiSpacer />
<EuiCodeBlock paddingSize="s" language="yml">
xpack.reporting.capture.browser.type: chromium
</EuiCodeBlock>
<Clipboard content={reportingConfig} onCopy={() => this.props.onCopy('reportingConfig')}>
<EuiCodeBlock
className="canvasWorkpadExport__reportingConfig"
paddingSize="s"
fontSize="s"
language="yml"
>
{reportingConfig}
</EuiCodeBlock>
</Clipboard>
</div>
);
};
render() {
const exportControl = togglePopover => (
<EuiButtonIcon iconType="exportAction" aria-label="Create PDF" onClick={togglePopover} />
<EuiButtonIcon iconType="share" aria-label="Share this workpad" onClick={togglePopover} />
);
// TODO: replace this with `showShareContextMenu` in `ui/share` once it's been converted to React
return (
<Popover button={exportControl} tooltip="Export workpad" tooltipPosition="bottom">
<Popover
button={exportControl}
panelPaddingSize="none"
tooltip="Share workpad"
tooltipPosition="bottom"
>
{({ closePopover }) => (
<EuiFlexGroup justifyContent="flexEnd">
<EuiFlexItem grow={false} style={{ maxWidth: '300px' }}>
{this.props.enabled && this.renderControls(closePopover)}
{!this.props.enabled && this.renderDisabled()}
</EuiFlexItem>
</EuiFlexGroup>
<EuiContextMenu
initialPanelId={0}
panels={this.flattenPanelTree(this.renderPanelTree(closePopover))}
/>
)}
</Popover>
);

View file

@ -0,0 +1,10 @@
.canvasWorkpadExport__panelContent {
padding: $euiSize;
}
.canvasWorkpadExport__reportingConfig {
.euiCodeBlock__pre {
@include euiScrollBar;
overflow-x: auto;
white-space: pre;
}
}

View file

@ -7,13 +7,13 @@
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { compose, withState, getContext, withHandlers } from 'recompose';
import fileSaver from 'file-saver';
import * as workpadService from '../../lib/workpad_service';
import { notify } from '../../lib/notify';
import { canUserWrite } from '../../state/selectors/app';
import { getWorkpad } from '../../state/selectors/workpad';
import { getId } from '../../lib/get_id';
import { setCanUserWrite } from '../../state/actions/transient';
import { downloadWorkpad } from '../../lib/download_workpad';
import { WorkpadLoader as Component } from './workpad_loader';
const mapStateToProps = state => ({
@ -67,15 +67,7 @@ export const WorkpadLoader = compose(
},
// Workpad import/export methods
downloadWorkpad: () => async workpadId => {
try {
const workpad = await workpadService.get(workpadId);
const jsonBlob = new Blob([JSON.stringify(workpad)], { type: 'application/json' });
fileSaver.saveAs(jsonBlob, `canvas-workpad-${workpad.name}-${workpad.id}.json`);
} catch (err) {
notify.error(err, { title: `Couldn't download workpad` });
}
},
downloadWorkpad: () => workpadId => downloadWorkpad(workpadId),
// Clone workpad given an id
cloneWorkpad: props => async workpadId => {

View file

@ -142,7 +142,7 @@ export class WorkpadLoader extends React.PureComponent {
<EuiFlexItem grow={false}>
<EuiToolTip content="Download">
<EuiButtonIcon
iconType="sortDown"
iconType="exportAction"
onClick={() => this.props.downloadWorkpad(workpad.id)}
aria-label="Download Workpad"
/>
@ -288,7 +288,7 @@ export class WorkpadLoader extends React.PureComponent {
);
const downloadButton = (
<EuiButton color="secondary" onClick={this.downloadWorkpads} iconType="sortDown">
<EuiButton color="secondary" onClick={this.downloadWorkpads} iconType="exportAction">
{`Download (${selectedWorkpads.length})`}
</EuiButton>
);

View file

@ -0,0 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import fileSaver from 'file-saver';
import { notify } from './notify';
import * as workpadService from './workpad_service';
export const downloadWorkpad = async workpadId => {
try {
const workpad = await workpadService.get(workpadId);
const jsonBlob = new Blob([JSON.stringify(workpad)], { type: 'application/json' });
fileSaver.saveAs(jsonBlob, `canvas-workpad-${workpad.name}-${workpad.id}.json`);
} catch (err) {
notify.error(err, { title: `Couldn't download workpad` });
}
};

View file

@ -24,6 +24,7 @@
@import '../components/autocomplete/autocomplete';
@import '../components/border_connection/border_connection';
@import '../components/border_resize_handle/border_resize_handle';
@import '../components/clipboard/clipboard';
@import '../components/color_dot/color_dot';
@import '../components/color_palette/color_palette';
@import '../components/color_picker_mini/color_picker_mini';
@ -52,6 +53,7 @@
@import '../components/toolbar/toolbar';
@import '../components/toolbar/tray/tray';
@import '../components/workpad/workpad';
@import '../components/workpad_export/workpad_export';
@import '../components/workpad_loader/workpad_loader';
@import '../components/workpad_loader/workpad_dropzone/workpad_dropzone';
@import '../components/workpad_page/workpad_page';