Compare commits

...

7 Commits

Author SHA1 Message Date
Albert Backenhof
2f59d97cf3 Merge pull request #55 from qlik-oss/DEB-204/NewString
Updated string according to Ralf Narfeldt
2019-05-03 08:26:36 +02:00
Albert Backenhof
b8d6b0a53e Merge pull request #53 from qlik-oss/QLIK-95019/Excel
Improved excel and snapshot export
2019-05-03 08:26:25 +02:00
Purwa Shrivastava
6cc82e7b38 Merge pull request #57 from qlik-oss/DEB-213/misalignedTable
DEB-213: Mis calculated the default, changed from 23 to 25 px.
2019-05-02 14:25:28 +02:00
Purwa Shrivastava
cc7e3e62ed DEB-213: Mis calculated the default, changed from 23 to 25 px. 2019-05-02 14:19:36 +02:00
Purwa Shrivastava
26de3b63ed Merge pull request #56 from qlik-oss/DEB-213/misalignedTable
DEB-213: Introdued a minimum height for the table rows, so even if th…
2019-05-02 13:04:17 +02:00
Purwa Shrivastava
8eafeffcec DEB-213: Introdued a minimum height for the table rows, so even if the content is null, the rows are displayed even. 2019-05-02 11:31:15 +02:00
Albert Backenhof
af307fd24b Improved excel and snapshot export
-KPI is now included in excel.
-Snapshots now work.

Issue: QLIK-95019
2019-04-30 13:04:42 +02:00
8 changed files with 92 additions and 69 deletions

View File

@@ -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;
}

View File

@@ -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 = `<p style="font-size:15pt"><b>${title}</b></p>`;
const subtitleHTML = `<p style="font-size:11pt">${subtitle}</p>`;
const footnoteHTML = `<p style="font-size:11pt"><i>Note:</i>${footnote}</p>`;
const dataTableClone = document.querySelector('.data-table').cloneNode(true);
const footnoteHTML = `<p style="font-size:11pt">${footnote}</p>`;
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 += `<tr>${kpiRows[i].innerHTML}${dataRows[i].innerHTML}</tr>`;
}
const tableHTML = `
<html
@@ -41,8 +54,23 @@ function buildTableHTML (title, subtitle, footnote) {
<body>
${titleHTML.length > 0 ? titleHTML : ''}
${subtitleHTML.length > 0 ? subtitleHTML : ''}
<div>
<table>
<tbody>
<tr>
${kpiHeader.innerHTML}
${dataTableHeaders[0].innerHTML}
</tr>
${dataTableHeaders.length > 1 ? dataTableHeaders[1].outerHTML : ''}
</tbody>
</table>
<table>
<tbody>
${combinedRows}
</tbody>
</table>
</div>
${footnoteHTML.length > 0 ? footnoteHTML : ''}
${dataTableClone.outerHTML}
</body>
</html>
`.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);
}

View File

@@ -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
};

View File

@@ -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}
>
<ExportButton
id={id}
excelExport={allowExcelExport}
general={general}
/>
@@ -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,

View File

@@ -28,6 +28,7 @@ const HeadersTable = ({ data, general, qlik, styling, isKpi }) => {
<tr>
{isKpi ?
<ExportColumnHeader
id={qlik.options.id}
allowExcelExport={general.allowExcelExport}
baseCSS={baseCSS}
general={general}

View File

@@ -1,5 +1,9 @@
import paint from './paint.jsx';
import definition from './definition';
import { initializeDataCube, initializeDesignList } from './dataset';
import initializeStore from './store';
import React from 'react';
import ReactDOM from 'react-dom';
import Root from './root.jsx';
import './main.less';
if (!window._babelPolyfill) { // eslint-disable-line no-underscore-dangle
@@ -53,16 +57,34 @@ export default {
exportData: true,
snapshot: true
},
paint ($element, layout) {
try {
paint($element, layout, this);
} catch (exception) {
console.error(exception); // eslint-disable-line no-console
throw exception;
}
paint: async function ($element, layout) {
const dataCube = await initializeDataCube(this, layout);
const designList = await initializeDesignList(this, layout);
const state = await initializeStore({
$element,
component: this,
dataCube,
designList,
layout
});
const editmodeClass = this.inAnalysisState() ? '' : 'edit-mode';
const jsx = (
<Root
editmodeClass={editmodeClass}
qlik={this}
state={state}
/>
);
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
};

View File

@@ -37,6 +37,10 @@
width: auto;
}
tr {
height: 25px;
}
td,
th {
padding: 5px !important; // prevent overwriting from single object

View File

@@ -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 = (
<Root
editmodeClass={editmodeClass}
qlik={component}
state={state}
/>
);
ReactDOM.render(jsx, $element[0]);
}