Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f17a7b7714 | ||
|
|
8aa86275f0 | ||
|
|
3ebc2b9e29 | ||
|
|
ce81549011 | ||
|
|
e64af66dab | ||
|
|
ca1b1a9b53 | ||
|
|
a65f843008 | ||
|
|
be9570e0aa | ||
|
|
41d3a7c9af | ||
|
|
1e0a7c1204 | ||
|
|
17b5df296c |
@@ -20,7 +20,7 @@ jobs:
|
||||
command: npm install
|
||||
- run:
|
||||
name: BlackDuck scan
|
||||
command: curl -s https://blackducksoftware.github.io/hub-detect/hub-detect.sh | bash -s -- \
|
||||
command: curl -s https://detect.synopsys.com/detect.sh | bash -s -- \
|
||||
--blackduck.url="https://qliktech.blackducksoftware.com" \
|
||||
--blackduck.trust.cert=true \
|
||||
--blackduck.username="svc-blackduck" \
|
||||
|
||||
BIN
assets/Excel.png
BIN
assets/Excel.png
Binary file not shown.
|
Before Width: | Height: | Size: 2.2 KiB |
@@ -5,6 +5,7 @@ import DataCell from './data-cell.jsx';
|
||||
import RowHeader from './row-header.jsx';
|
||||
import { injectSeparators } from '../utilities';
|
||||
|
||||
// eslint-disable-next-line react/prefer-stateless-function
|
||||
class DataTable extends React.PureComponent {
|
||||
render () {
|
||||
const {
|
||||
@@ -20,6 +21,7 @@ class DataTable extends React.PureComponent {
|
||||
const {
|
||||
headers: {
|
||||
dimension1,
|
||||
dimension2,
|
||||
measurements
|
||||
},
|
||||
matrix
|
||||
@@ -30,6 +32,36 @@ class DataTable extends React.PureComponent {
|
||||
maxWidth: columnSeparatorWidth
|
||||
};
|
||||
|
||||
const renderMeasurementData = (dimIndex, atEvery) => {
|
||||
const injectSeparatorsArray = injectSeparators(
|
||||
matrix[dimIndex],
|
||||
columnSeparatorWidth,
|
||||
atEvery
|
||||
);
|
||||
|
||||
if (dimension2.length <= 0) {
|
||||
return injectSeparatorsArray;
|
||||
}
|
||||
|
||||
let measurementDataRow = [],
|
||||
index = 0;
|
||||
dimension2.forEach((dim2) => {
|
||||
measurements.forEach((measure) => {
|
||||
for (index = 0; index < injectSeparatorsArray.length; index++) {
|
||||
if (dimension1[dimIndex].displayValue === injectSeparatorsArray[index].parents.dimension1.header) {
|
||||
if (dim2.displayValue === injectSeparatorsArray[index].parents.dimension2.header) {
|
||||
if (measure.name === injectSeparatorsArray[index].parents.measurement.header) {
|
||||
measurementDataRow.push(injectSeparatorsArray[index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return measurementDataRow;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="row-wrapper">
|
||||
<table>
|
||||
@@ -65,11 +97,7 @@ class DataTable extends React.PureComponent {
|
||||
styling={styling}
|
||||
/> : null
|
||||
}
|
||||
{renderData && injectSeparators(
|
||||
matrix[dimensionIndex],
|
||||
columnSeparatorWidth,
|
||||
{ atEvery: measurements.length }
|
||||
).map((measurementData, index) => {
|
||||
{renderData && renderMeasurementData(dimensionIndex, { atEvery: measurements.length }).map((measurementData, index) => {
|
||||
if (measurementData.isSeparator) {
|
||||
return (
|
||||
<td
|
||||
@@ -80,6 +108,7 @@ class DataTable extends React.PureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-shadow
|
||||
const { dimension1: dimension1Info, dimension2, measurement } = measurementData.parents;
|
||||
const id = `${dimension1Info.elementNumber}-${dimension2 && dimension2.elementNumber}-${measurement.header}-${measurement.index}`;
|
||||
return (
|
||||
@@ -112,6 +141,7 @@ DataTable.defaultProps = {
|
||||
DataTable.propTypes = {
|
||||
cellWidth: PropTypes.string.isRequired,
|
||||
columnSeparatorWidth: PropTypes.string.isRequired,
|
||||
component: PropTypes.shape({}).isRequired,
|
||||
data: PropTypes.shape({
|
||||
headers: PropTypes.shape({
|
||||
dimension1: PropTypes.array.isRequired
|
||||
@@ -119,7 +149,6 @@ DataTable.propTypes = {
|
||||
matrix: PropTypes.arrayOf(PropTypes.array.isRequired).isRequired
|
||||
}).isRequired,
|
||||
general: PropTypes.shape({}).isRequired,
|
||||
component: PropTypes.shape({}).isRequired,
|
||||
renderData: PropTypes.bool,
|
||||
styling: PropTypes.shape({
|
||||
hasCustomFileStyle: PropTypes.bool.isRequired
|
||||
|
||||
@@ -39,7 +39,7 @@ const definition = {
|
||||
component: 'text'
|
||||
},
|
||||
paragraph1: {
|
||||
label: `P&L pivot is a Qlik Sense extension which allows you to display Profit & Loss
|
||||
label: `P&L pivot is a Qlik Sense chart which allows you to display Profit & Loss
|
||||
reporting with color and font customizations.`,
|
||||
component: 'text'
|
||||
},
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { exportXLS } from './excel-export';
|
||||
|
||||
class ExportButton extends React.PureComponent {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.handleExport = this.handleExport.bind(this);
|
||||
}
|
||||
|
||||
handleExport () {
|
||||
const { component, excelExport, general } = this.props;
|
||||
const { title, subtitle, footnote } = general;
|
||||
if (excelExport) {
|
||||
exportXLS(component.$element, title, subtitle, footnote);
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { excelExport } = this.props;
|
||||
return excelExport === true && (
|
||||
<input
|
||||
className="icon-xls"
|
||||
onClick={this.handleExport}
|
||||
src="/Extensions/qlik-smart-pivot/Excel.png"
|
||||
type="image"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ExportButton.defaultProps = {
|
||||
excelExport: false
|
||||
};
|
||||
|
||||
ExportButton.propTypes = {
|
||||
component: PropTypes.shape({}).isRequired,
|
||||
excelExport: PropTypes.bool,
|
||||
general: PropTypes.shape({}).isRequired
|
||||
};
|
||||
|
||||
export default ExportButton;
|
||||
@@ -1,9 +1,10 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ExportButton from '../export-button.jsx';
|
||||
import { HEADER_FONT_SIZE } from '../initialize-transformed';
|
||||
import Tooltip from '../tooltip/index.jsx';
|
||||
|
||||
const ExportColumnHeader = ({ component, baseCSS, general, title, allowExcelExport, hasSecondDimension, styling }) => {
|
||||
const Dim1Header = ({ component, baseCSS, title, hasSecondDimension, styling }) => {
|
||||
const inEditState = component.inEditState();
|
||||
const rowSpan = hasSecondDimension ? 2 : 1;
|
||||
const isMediumFontSize = styling.headerOptions.fontSizeAdjustment === HEADER_FONT_SIZE.MEDIUM;
|
||||
const style = {
|
||||
@@ -21,21 +22,20 @@ const ExportColumnHeader = ({ component, baseCSS, general, title, allowExcelExpo
|
||||
rowSpan={rowSpan}
|
||||
style={style}
|
||||
>
|
||||
<ExportButton
|
||||
component={component}
|
||||
excelExport={allowExcelExport}
|
||||
general={general}
|
||||
/>
|
||||
{title}
|
||||
<Tooltip
|
||||
isTooltipActive={!inEditState}
|
||||
styling={styling}
|
||||
tooltipText={title}
|
||||
>
|
||||
{title}
|
||||
</Tooltip>
|
||||
</th>
|
||||
);
|
||||
};
|
||||
|
||||
ExportColumnHeader.propTypes = {
|
||||
component: PropTypes.shape({}).isRequired,
|
||||
allowExcelExport: PropTypes.bool.isRequired,
|
||||
Dim1Header.propTypes = {
|
||||
baseCSS: PropTypes.shape({}).isRequired,
|
||||
general: PropTypes.shape({}).isRequired,
|
||||
component: PropTypes.shape({}).isRequired,
|
||||
hasSecondDimension: PropTypes.bool.isRequired,
|
||||
styling: PropTypes.shape({
|
||||
headerOptions: PropTypes.shape({
|
||||
@@ -45,4 +45,4 @@ ExportColumnHeader.propTypes = {
|
||||
title: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default ExportColumnHeader;
|
||||
export default Dim1Header;
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ExportColumnHeader from './export-column-header.jsx';
|
||||
import Dim1Header from './dim1-header.jsx';
|
||||
import ColumnHeader from './column-header.jsx';
|
||||
import MeasurementColumnHeader from './measurement-column-header.jsx';
|
||||
import { injectSeparators } from '../utilities';
|
||||
@@ -12,7 +12,6 @@ class HeadersTable extends React.PureComponent {
|
||||
columnSeparatorWidth,
|
||||
component,
|
||||
data,
|
||||
general,
|
||||
isKpi,
|
||||
styling
|
||||
} = this.props;
|
||||
@@ -43,11 +42,9 @@ class HeadersTable extends React.PureComponent {
|
||||
<tbody>
|
||||
<tr>
|
||||
{isKpi ?
|
||||
<ExportColumnHeader
|
||||
allowExcelExport={general.allowExcelExport}
|
||||
<Dim1Header
|
||||
baseCSS={baseCSS}
|
||||
component={component}
|
||||
general={general}
|
||||
hasSecondDimension={hasSecondDimension}
|
||||
styling={styling}
|
||||
title={dimension1[0].name}
|
||||
@@ -129,7 +126,6 @@ HeadersTable.propTypes = {
|
||||
measurements: PropTypes.array
|
||||
})
|
||||
}).isRequired,
|
||||
general: PropTypes.shape({}).isRequired,
|
||||
component: PropTypes.shape({}).isRequired,
|
||||
styling: PropTypes.shape({
|
||||
headerOptions: PropTypes.shape({}),
|
||||
|
||||
32
src/index.js
32
src/index.js
@@ -1,6 +1,8 @@
|
||||
import definition from './definition';
|
||||
import { exportXLS } from './excel-export';
|
||||
import { initializeDataCube, initializeDesignList } from './dataset';
|
||||
import initializeStore from './store';
|
||||
import qlik from 'qlik';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Root from './root.jsx';
|
||||
@@ -11,11 +13,6 @@ if (!window._babelPolyfill) { // eslint-disable-line no-underscore-dangle
|
||||
}
|
||||
|
||||
export default {
|
||||
controller: [
|
||||
'$scope',
|
||||
'$timeout',
|
||||
function controller () {}
|
||||
],
|
||||
design: {
|
||||
dimensions: {
|
||||
max: 1,
|
||||
@@ -38,6 +35,9 @@ export default {
|
||||
uses: 'measures'
|
||||
}
|
||||
},
|
||||
// Prevent conversion from and to this object
|
||||
exportProperties: null,
|
||||
importProperties: null,
|
||||
definition,
|
||||
initialProperties: {
|
||||
version: 1.0,
|
||||
@@ -50,7 +50,6 @@ export default {
|
||||
}
|
||||
],
|
||||
qMeasures: [],
|
||||
qSuppressMissing: true,
|
||||
qSuppressZero: false
|
||||
}
|
||||
},
|
||||
@@ -88,5 +87,26 @@ export default {
|
||||
snapshotLayout.snapshotData.designList = await initializeDesignList(this, snapshotLayout);
|
||||
return snapshotLayout;
|
||||
},
|
||||
getContextMenu: async function (obj, menu) {
|
||||
const app = qlik.currApp(this);
|
||||
const isPersonalResult = await app.global.isPersonalMode();
|
||||
if (!this.$scope.layout.allowexportxls || (isPersonalResult && isPersonalResult.qReturn)) {
|
||||
return menu;
|
||||
}
|
||||
|
||||
menu.addItem({
|
||||
translation: "Export as XLS",
|
||||
tid: "export-excel",
|
||||
icon: "export",
|
||||
select: () => {
|
||||
exportXLS(
|
||||
this.$element,
|
||||
this.$scope.layout.title,
|
||||
this.$scope.layout.subtitle,
|
||||
this.$scope.layout.footnote);
|
||||
}
|
||||
});
|
||||
return menu;
|
||||
},
|
||||
version: 1.0
|
||||
};
|
||||
|
||||
@@ -64,7 +64,8 @@ function generateMatrixCell ({ cell, dimension1Information, dimension2Informatio
|
||||
|
||||
if (dimension2Information) {
|
||||
matrixCell.parents.dimension2 = {
|
||||
elementNumber: dimension2Information.qElemNumber
|
||||
elementNumber: dimension2Information.qElemNumber,
|
||||
header: dimension2Information.qText
|
||||
};
|
||||
}
|
||||
|
||||
@@ -141,7 +142,7 @@ function generateDataSet (
|
||||
let rowDataIndex = 0;
|
||||
dimension2.forEach(dim => {
|
||||
rowDataIndex = appendMissingCells(
|
||||
row, newRow, rowDataIndex, measurements, rowIndex, dim.elementNumber);
|
||||
row, newRow, rowDataIndex, measurements, rowIndex, dim, dimension1);
|
||||
});
|
||||
} else {
|
||||
appendMissingCells(row, newRow, 0, measurements, rowIndex);
|
||||
@@ -165,14 +166,11 @@ function generateDataSet (
|
||||
* index of the dimension2 value being processed.
|
||||
*/
|
||||
function appendMissingCells (
|
||||
sourceRow, destRow, sourceIndex, measurements, dim1ElementNumber, dim2ElementNumber = -1) {
|
||||
sourceRow, destRow, sourceIndex, measurements, matrixIndex, dim2, dim1) {
|
||||
|
||||
let index = sourceIndex;
|
||||
measurements.forEach((measurement, measureIndex) => {
|
||||
if (index < sourceRow.length
|
||||
&& (dim2ElementNumber === -1
|
||||
|| sourceRow[index].parents.dimension2.elementNumber === dim2ElementNumber)
|
||||
&& sourceRow[index].parents.measurement.header === measurement.name) {
|
||||
if (index < sourceRow.length) {
|
||||
// Source contains the expected cell
|
||||
destRow.push(sourceRow[index]);
|
||||
index++;
|
||||
@@ -181,8 +179,14 @@ function appendMissingCells (
|
||||
destRow.push({
|
||||
displayValue: '',
|
||||
parents: {
|
||||
dimension1: { elementNumber: dim1ElementNumber },
|
||||
dimension2: { elementNumber: dim2ElementNumber },
|
||||
dimension1: {
|
||||
elementNumber: dim1[matrixIndex].elementNumber,
|
||||
header: dim1[matrixIndex].displayValue
|
||||
},
|
||||
dimension2: {
|
||||
elementNumber: dim2.elementNumber,
|
||||
header: dim2.displayValue
|
||||
},
|
||||
measurement: {
|
||||
header: measurement.name,
|
||||
index: measureIndex
|
||||
|
||||
Reference in New Issue
Block a user