From af307fd24baafd5cd2c709e70b5fc54d3b49800f Mon Sep 17 00:00:00 2001 From: Albert Backenhof Date: Thu, 25 Apr 2019 14:17:38 +0200 Subject: [PATCH] Improved excel and snapshot export -KPI is now included in excel. -Snapshots now work. Issue: QLIK-95019 --- src/dataset.js | 17 +++--- src/excel-export.js | 65 ++++++++++++++-------- src/export-button.jsx | 5 +- src/headers-table/export-column-header.jsx | 4 +- src/headers-table/index.jsx | 1 + src/index.js | 38 ++++++++++--- src/paint.jsx | 27 --------- 7 files changed, 88 insertions(+), 69 deletions(-) delete mode 100644 src/paint.jsx diff --git a/src/dataset.js b/src/dataset.js index 6bddb44..ea145cf 100644 --- a/src/dataset.js +++ b/src/dataset.js @@ -26,21 +26,20 @@ async function buildDataCube (originCubeDefinition, hasTwoDimensions, app) { } export async function initializeDataCube (component, layout) { - const app = qlik.currApp(component); - - let properties; if (component.backendApi.isSnapshot) { - // Fetch properties of source - properties = (await app.getObjectProperties(layout.sourceObjectId)).properties; - } else { - properties = await component.backendApi.getProperties(); + return layout.snapshotData.dataCube; } - return buildDataCube( - properties.qHyperCubeDef, layout.qHyperCube.qDimensionInfo.length === 2, app); + const app = qlik.currApp(component); + const hyperCubeDef = (await component.backendApi.getProperties()).qHyperCubeDef; + return buildDataCube(hyperCubeDef, layout.qHyperCube.qDimensionInfo.length === 2, app); } export function initializeDesignList (component, layout) { + if (component.backendApi.isSnapshot) { + return layout.snapshotData.designList; + } + if (!layout.stylingfield) { return null; } diff --git a/src/excel-export.js b/src/excel-export.js index 413f391..7b01e18 100644 --- a/src/excel-export.js +++ b/src/excel-export.js @@ -1,19 +1,32 @@ -function removeAllTooltips (node) { - const tooltips = node.querySelectorAll('.tooltip'); - [].forEach.call(tooltips, tooltip => { - if (tooltip.parentNode) { - tooltip.parentNode.removeChild(tooltip); +function cleanupNodes (node) { + const removables = node.querySelectorAll('.tooltip,input'); + [].forEach.call(removables, removeable => { + if (removeable.parentNode) { + removeable.parentNode.removeChild(removeable); } }); } -function buildTableHTML (title, subtitle, footnote) { +function buildTableHTML (id, title, subtitle, footnote) { const titleHTML = `

${title}

`; const subtitleHTML = `

${subtitle}

`; - const footnoteHTML = `

Note:${footnote}

`; - const dataTableClone = document.querySelector('.data-table').cloneNode(true); + const footnoteHTML = `

${footnote}

`; + const container = document.querySelector(`[tid="${id}"]`); + const kpiTableClone = container.querySelector('.kpi-table').cloneNode(true); + const dataTableClone = container.querySelector('.data-table').cloneNode(true); + cleanupNodes(kpiTableClone); + cleanupNodes(kpiTableClone); - removeAllTooltips(dataTableClone); + const kpiTableBodies = kpiTableClone.querySelectorAll('tbody'); + const dataTableBodies = dataTableClone.querySelectorAll('tbody'); + const kpiHeader = kpiTableBodies[0].querySelector('tr'); + const dataTableHeaders = dataTableBodies[0].querySelectorAll('tr'); + const kpiRows = kpiTableBodies[1].querySelectorAll('tr'); + const dataRows = dataTableBodies[1].querySelectorAll('tr'); + let combinedRows = ''; + for (let i = 0; i < kpiRows.length; i++) { + combinedRows += `${kpiRows[i].innerHTML}${dataRows[i].innerHTML}`; + } const tableHTML = ` ${titleHTML.length > 0 ? titleHTML : ''} ${subtitleHTML.length > 0 ? subtitleHTML : ''} +
+ + + + ${kpiHeader.innerHTML} + ${dataTableHeaders[0].innerHTML} + + ${dataTableHeaders.length > 1 ? dataTableHeaders[1].outerHTML : ''} + +
+ + + ${combinedRows} + +
+
${footnoteHTML.length > 0 ? footnoteHTML : ''} - ${dataTableClone.outerHTML} `.split('>.<') @@ -55,15 +83,15 @@ function buildTableHTML (title, subtitle, footnote) { function downloadXLS (html) { const filename = 'analysis.xls'; + const blobObject = new Blob([html]); + // IE/Edge if (window.navigator.msSaveOrOpenBlob) { - const blobObject = new Blob([html]); return window.navigator.msSaveOrOpenBlob(blobObject, filename); } - const dataURI = generateDataURI(html); const link = window.document.createElement('a'); - link.href = dataURI; + link.href = URL.createObjectURL(blobObject); link.download = filename; document.body.appendChild(link); link.click(); @@ -72,15 +100,8 @@ function downloadXLS (html) { return true; } -function generateDataURI (html) { - const dataType = 'data:application/vnd.ms-excel;base64,'; - const data = window.btoa(unescape(encodeURIComponent(html))); - - return `${dataType}${data}`; -} - -export function exportXLS (title, subtitle, footnote) { +export function exportXLS (id, title, subtitle, footnote) { // original was removing icon when starting export, disable and some spinner instead, shouldn't take enough time to warrant either..? - const table = buildTableHTML(title, subtitle, footnote); + const table = buildTableHTML(id, title, subtitle, footnote); downloadXLS(table); } diff --git a/src/export-button.jsx b/src/export-button.jsx index 76436ed..1f1093e 100644 --- a/src/export-button.jsx +++ b/src/export-button.jsx @@ -9,10 +9,10 @@ class ExportButton extends React.PureComponent { } handleExport () { - const { excelExport, general } = this.props; + const { id, excelExport, general } = this.props; const { title, subtitle, footnote } = general; if (excelExport) { - exportXLS(title, subtitle, footnote); + exportXLS(id, title, subtitle, footnote); } } @@ -34,6 +34,7 @@ ExportButton.defaultProps = { }; ExportButton.propTypes = { + id: PropTypes.string.isRequired, excelExport: PropTypes.bool, general: PropTypes.shape({}).isRequired }; diff --git a/src/headers-table/export-column-header.jsx b/src/headers-table/export-column-header.jsx index a0fb9e1..8e09724 100644 --- a/src/headers-table/export-column-header.jsx +++ b/src/headers-table/export-column-header.jsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import ExportButton from '../export-button.jsx'; import { HEADER_FONT_SIZE } from '../initialize-transformed'; -const ExportColumnHeader = ({ baseCSS, general, title, allowExcelExport, hasSecondDimension, styling }) => { +const ExportColumnHeader = ({ id, baseCSS, general, title, allowExcelExport, hasSecondDimension, styling }) => { const rowSpan = hasSecondDimension ? 2 : 1; const isMediumFontSize = styling.headerOptions.fontSizeAdjustment === HEADER_FONT_SIZE.MEDIUM; const style = { @@ -22,6 +22,7 @@ const ExportColumnHeader = ({ baseCSS, general, title, allowExcelExport, hasSeco style={style} > @@ -31,6 +32,7 @@ const ExportColumnHeader = ({ baseCSS, general, title, allowExcelExport, hasSeco }; ExportColumnHeader.propTypes = { + id: PropTypes.string.isRequired, allowExcelExport: PropTypes.bool.isRequired, baseCSS: PropTypes.shape({}).isRequired, general: PropTypes.shape({}).isRequired, diff --git a/src/headers-table/index.jsx b/src/headers-table/index.jsx index 0229c27..31fb82c 100644 --- a/src/headers-table/index.jsx +++ b/src/headers-table/index.jsx @@ -28,6 +28,7 @@ const HeadersTable = ({ data, general, qlik, styling, isKpi }) => { {isKpi ? + ); + + ReactDOM.render(jsx, $element[0]); }, snapshot: { canTakeSnapshot: true }, + setSnapshotData: async function (snapshotLayout) { + snapshotLayout.snapshotData.dataCube = await initializeDataCube(this, snapshotLayout); + snapshotLayout.snapshotData.designList = await initializeDesignList(this, snapshotLayout); + return snapshotLayout; + }, version: 1.0 }; diff --git a/src/paint.jsx b/src/paint.jsx deleted file mode 100644 index 5791a3a..0000000 --- a/src/paint.jsx +++ /dev/null @@ -1,27 +0,0 @@ -import initializeStore from './store'; -import React from 'react'; -import ReactDOM from 'react-dom'; -import Root from './root.jsx'; -import { initializeDataCube, initializeDesignList } from './dataset'; - -export default async function paint ($element, layout, component) { - const dataCube = await initializeDataCube(component, layout); - const designList = await initializeDesignList(component, layout); - const state = await initializeStore({ - $element, - component, - dataCube, - designList, - layout - }); - const editmodeClass = component.inAnalysisState() ? '' : 'edit-mode'; - const jsx = ( - - ); - - ReactDOM.render(jsx, $element[0]); -}