Compare commits

..

4 Commits

Author SHA1 Message Date
Purwa Shrivastava
8aa86275f0 Changed the rendering of each data cell to check for the headers of each dimesnsion and measure where it shall be written. 2019-12-04 11:15:14 +01:00
Purwa Shrivastava
3ebc2b9e29 Adding Dim 2 to the rendering logic. 2019-11-29 16:17:49 +01:00
Purwa Shrivastava
ce81549011 Merge pull request #78 from qlik-oss/QB-498/disableConversion
Conversion led to different sort orders of data in different objects …
2019-11-28 13:40:45 +01:00
Purwa Shrivastava
e64af66dab Conversion led to different sort orders of data in different objects so disabling it. 2019-11-27 14:54:42 +01:00
7 changed files with 232 additions and 225 deletions

View File

@@ -21,6 +21,7 @@ class DataTable extends React.PureComponent {
const {
headers: {
dimension1,
dimension2,
measurements
},
matrix
@@ -31,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>
@@ -66,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
@@ -81,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 (
@@ -113,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
@@ -120,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

View File

@@ -6,15 +6,12 @@ function createCube (definition, app) {
});
}
async function buildDataCube (originCubeDefinition, originCube, app, requestPage) {
async function buildDataCube (originCubeDefinition, originCube, app) {
const cubeDefinition = {
...originCubeDefinition,
qInitialDataFetch: [
{
// eslint-disable-next-line no-undefined
qTop: requestPage === undefined ? 0 : requestPage[0].qTop,
qLeft: 0,
qHeight: 1000,
qHeight: originCube.qSize.qcy,
qWidth: originCube.qSize.qcx
}
],
@@ -31,15 +28,12 @@ async function buildDataCube (originCubeDefinition, originCube, app, requestPage
}
export async function initializeDataCube (component, layout) {
if (component.backendApi.isSnapshot) {
return layout.snapshotData.dataCube;
}
const app = qlik.currApp(component);
const properties = (await component.backendApi.getProperties());
const rowCount = component.backendApi.getRowCount();
const cellCount = rowCount * layout.qHyperCube.qSize.qcx;
const maxLoops = layout.maxloops;
// If this is a master object, fetch the hyperCubeDef of the original object
let hyperCubeDef = properties.qExtendsId
@@ -47,25 +41,8 @@ export async function initializeDataCube (component, layout) {
: properties.qHyperCubeDef;
hyperCubeDef = JSON.parse(JSON.stringify(hyperCubeDef));
hyperCubeDef.qStateName = layout.qStateName;
const pagedCube = {};
let lastRow = 0;
if (cellCount < (maxLoops * 10000)) {
for (let index = 0; cellCount > lastRow; index += 1) {
const requestPage = [
{
qHeight: 1000,
qLeft: 0,
qTop: lastRow,
qWidth: 10 // should be # of columns
}
];
// eslint-disable-next-line no-await-in-loop
pagedCube[index] = await buildDataCube(hyperCubeDef, layout.qHyperCube, app, requestPage);
lastRow = lastRow + 1000;
}
return pagedCube;
}
return null;
return buildDataCube(hyperCubeDef, layout.qHyperCube, app);
}
export function initializeDesignList (component, layout) {

View File

@@ -23,6 +23,30 @@ const pagination = {
{
value: 4,
label: '40k cells'
},
{
value: 5,
label: '50k cells'
},
{
value: 6,
label: '60k cells'
},
{
value: 7,
label: '70k cells'
},
{
value: 8,
label: '80k cells'
},
{
value: 9,
label: '90k cells'
},
{
value: 10,
label: '100k cells'
}
],
defaultValue: 2
@@ -31,8 +55,7 @@ const pagination = {
ref: 'errormessage',
label: 'Default error message',
type: 'string',
defaultValue: `Unable to display all the data.
Change the pagination size supported or apply more filters to limit the amount of displayed data.`
defaultValue: 'Unable to display all the data. Apply more filters to limit the amount of displayed data.'
}
}
};

View File

@@ -1,16 +1,15 @@
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";
import "./main.less";
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';
import './main.less';
if (!window._babelPolyfill) {
// eslint-disable-line no-underscore-dangle
require("@babel/polyfill"); // eslint-disable-line global-require
if (!window._babelPolyfill) { // eslint-disable-line no-underscore-dangle
require('@babel/polyfill'); // eslint-disable-line global-require
}
export default {
@@ -22,20 +21,23 @@ export default {
},
data: {
dimensions: {
max (nMeasures) {
max: function (nMeasures) {
return nMeasures < 9 ? 2 : 1;
},
min: 1,
uses: 'dimensions'
},
measures: {
max (nDims) {
max: function (nDims) {
return nDims < 2 ? 9 : 8;
},
min: 1,
uses: 'measures'
}
},
// Prevent conversion from and to this object
exportProperties: null,
importProperties: null,
definition,
initialProperties: {
version: 1.0,
@@ -43,14 +45,11 @@ export default {
qDimensions: [],
qInitialDataFetch: [
{
qTop: 0,
qLeft: 0,
qWidth: 50,
qHeight: 50
qHeight: 1,
qWidth: 10
}
],
qMeasures: [],
qSuppressMissing: true,
qSuppressZero: false
}
},
@@ -59,32 +58,23 @@ export default {
exportData: true,
snapshot: true
},
async paint ($element, layout, requestPage) {
const dataCube = await initializeDataCube(this, layout, requestPage);
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';
let state, designList;
if (dataCube === null) {
state = {
$element,
component: this,
dataCube,
designList,
layout,
error: true
};
} else {
designList = await initializeDesignList(this, layout);
state = await initializeStore({
$element,
component: this,
dataCube,
designList,
layout,
error: false
});
}
const jsx = (
<Root editmodeClass={editmodeClass} component={this} state={state} />
<Root
editmodeClass={editmodeClass}
component={this}
state={state}
/>
);
ReactDOM.render(jsx, $element[0]);
@@ -92,38 +82,28 @@ export default {
snapshot: {
canTakeSnapshot: true
},
async setSnapshotData (snapshotLayout) {
snapshotLayout.snapshotData.dataCube = await initializeDataCube(
this,
snapshotLayout
);
snapshotLayout.snapshotData.designList = await initializeDesignList(
this,
snapshotLayout
);
setSnapshotData: async function (snapshotLayout) {
snapshotLayout.snapshotData.dataCube = await initializeDataCube(this, snapshotLayout);
snapshotLayout.snapshotData.designList = await initializeDesignList(this, snapshotLayout);
return snapshotLayout;
},
async getContextMenu (obj, menu) {
getContextMenu: async function (obj, menu) {
const app = qlik.currApp(this);
const isPersonalResult = await app.global.isPersonalMode();
if (
!this.$scope.layout.allowexportxls ||
(isPersonalResult && isPersonalResult.qReturn)
) {
if (!this.$scope.layout.allowexportxls || (isPersonalResult && isPersonalResult.qReturn)) {
return menu;
}
menu.addItem({
translation: 'Export as XLS',
tid: 'export-excel',
icon: 'export',
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
);
this.$scope.layout.footnote);
}
});
return menu;

View File

@@ -64,63 +64,64 @@ function generateMatrixCell ({ cell, dimension1Information, dimension2Informatio
if (dimension2Information) {
matrixCell.parents.dimension2 = {
elementNumber: dimension2Information.qElemNumber
elementNumber: dimension2Information.qElemNumber,
header: dimension2Information.qText
};
}
return matrixCell;
}
let lastRow = 0;
function generateDataSet (
component, dimensionsInformation, measurementsInformation, dataCube) {
function generateDataSet (component, dimensionsInformation, measurementsInformation, dataCube) {
const measurements = generateMeasurements(measurementsInformation);
let dimension1 = [];
let dimension2 = [];
let matrix = [];
const hasSecondDimension = dimensionsInformation.length > 1;
// eslint-disable-next-line no-undefined
for (let index = 0; dataCube[index] !== undefined; index++) {
// eslint-disable-next-line no-loop-func
dataCube[index].forEach(row => {
const dimension1Entry = generateDimensionEntry(dimensionsInformation[0], row[0]);
dimension1.push(dimension1Entry);
let dimension2Entry;
let firstDataCell = 1;
if (hasSecondDimension) {
dimension2Entry = generateDimensionEntry(dimensionsInformation[1], row[1]);
dimension2.push(dimension2Entry);
firstDataCell = 2;
}
let matrixRow = row
.slice(firstDataCell, row.length)
.map((cell, cellIndex) => {
const measurementInformation = measurements[cellIndex];
measurementInformation.index = cellIndex;
const dimension1Information = row[0]; // eslint-disable-line prefer-destructuring
const dimension2Information = hasSecondDimension ? row[1] : null;
const generatedCell = generateMatrixCell({
cell,
dimension1Information,
dimension2Information,
measurementInformation
});
return generatedCell;
dataCube.forEach(row => {
lastRow += 1;
const dimension1Entry = generateDimensionEntry(dimensionsInformation[0], row[0]);
dimension1.push(dimension1Entry);
let dimension2Entry;
let firstDataCell = 1;
if (hasSecondDimension) {
dimension2Entry = generateDimensionEntry(dimensionsInformation[1], row[1]);
dimension2.push(dimension2Entry);
firstDataCell = 2;
}
let matrixRow = row
.slice(firstDataCell, row.length)
.map((cell, cellIndex) => {
const measurementInformation = measurements[cellIndex];
measurementInformation.index = cellIndex;
const dimension1Information = row[0]; // eslint-disable-line prefer-destructuring
const dimension2Information = hasSecondDimension ? row[1] : null;
const generatedCell = generateMatrixCell({
cell,
dimension1Information,
dimension2Information,
measurementInformation
});
let appendToRowIndex = matrix.length;
if (hasSecondDimension) {
return generatedCell;
});
let appendToRowIndex = matrix.length;
if (hasSecondDimension) {
// See if there already is a row for the current dim1
for (let i = 0; i < matrix.length; i++) {
if (matrix[i][0].parents.dimension1.header === matrixRow[0].parents.dimension1.header) {
appendToRowIndex = i;
matrixRow = matrix[i].concat(matrixRow);
}
for (let i = 0; i < matrix.length; i++) {
if (matrix[i][0].parents.dimension1.header === matrixRow[0].parents.dimension1.header) {
appendToRowIndex = i;
matrixRow = matrix[i].concat(matrixRow);
}
}
matrix[appendToRowIndex] = matrixRow;
});
}
}
matrix[appendToRowIndex] = matrixRow;
});
// filter header dimensions to only have distinct values
dimension1 = distinctArray(dimension1);
@@ -141,7 +142,7 @@ function generateDataSet (component, dimensionsInformation, measurementsInformat
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 (component, dimensionsInformation, measurementsInformat
* 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
@@ -194,10 +198,12 @@ function appendMissingCells (
return index;
}
function initializeTransformed ({ component, dataCube, designList, layout }) {
function initializeTransformed ({ $element, component, dataCube, designList, layout }) {
const dimensionsInformation = component.backendApi.getDimensionInfos();
const measurementsInformation = component.backendApi.getMeasureInfos();
const dimensionCount = layout.qHyperCube.qDimensionInfo.length;
const rowCount = component.backendApi.getRowCount();
const maxLoops = layout.maxloops;
const {
dimension1,
dimension2,
@@ -257,6 +263,7 @@ function initializeTransformed ({ component, dataCube, designList, layout }) {
cellWidth: cellWidth,
errorMessage: layout.errormessage,
footnote: layout.footnote,
maxLoops,
subtitle: layout.subtitle,
title: layout.title,
useColumnSeparator: layout.separatorcols && dimensionCount > 1
@@ -317,6 +324,20 @@ function initializeTransformed ({ component, dataCube, designList, layout }) {
}
};
if (rowCount > lastRow && rowCount <= (maxLoops * 1000)) {
const requestPage = [
{
qHeight: Math.min(1000, rowCount - lastRow),
qLeft: 0,
qTop: matrix.length,
qWidth: 10 // should be # of columns
}
];
component.backendApi.getData(requestPage).then(() => {
component.paint($element, layout);
});
}
return transformedProperties;
}

View File

@@ -105,20 +105,6 @@
width: 100%;
}
.error {
position: absolute; /*Define position */
width: 100%; /* Full width (cover the whole page) */
height: 100%; /* Full height (cover the whole page) */
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000; /* Specify a stack order in case you're using a different order for other elements */
display: flex;
justify-content: center;
align-items: center;
}
.kpi-table .fdim-cells,
.data-table td {
line-height: 1em !important;

View File

@@ -1,8 +1,8 @@
import React from "react";
import PropTypes from "prop-types";
import HeadersTable from "./headers-table/index.jsx";
import DataTable from "./data-table/index.jsx";
import { LinkedScrollWrapper, LinkedScrollSection } from "./linked-scroll";
import React from 'react';
import PropTypes from 'prop-types';
import HeadersTable from './headers-table/index.jsx';
import DataTable from './data-table/index.jsx';
import { LinkedScrollWrapper, LinkedScrollSection } from './linked-scroll';
class Root extends React.PureComponent {
constructor (props) {
@@ -25,7 +25,7 @@ class Root extends React.PureComponent {
render () {
const { editmodeClass, component, state } = this.props;
const { data, general, styling, error } = state;
const { data, general, styling } = state;
// Determine cell- and column separator width
let cellWidth = '0px';
@@ -48,80 +48,72 @@ class Root extends React.PureComponent {
let separatorWidth = 0;
if (general.useColumnSeparator) {
separatorCount = data.headers.dimension2.length - 1;
separatorWidth = Math.min(
Math.floor((tableWidth * 0.2) / separatorCount),
8
);
separatorWidth = Math.min(Math.floor(tableWidth * 0.2 / separatorCount), 8);
columnSeparatorWidth = `${separatorWidth}px`;
}
const separatorWidthSum =
(separatorWidth + borderWidth) * separatorCount;
cellWidth = `${Math.floor(
(tableWidth - separatorWidthSum - headerMarginRight - borderWidth) /
rowCellCount) - borderWidth}px`;
const separatorWidthSum = (separatorWidth + borderWidth) * separatorCount;
cellWidth = `${Math.floor((tableWidth - separatorWidthSum - headerMarginRight - borderWidth)
/ rowCellCount) - borderWidth}px`;
}
}
return (
<div className="root">
{error ? (
<div className="error">
{state.layout.errormessage}
<LinkedScrollWrapper>
<div className={`kpi-table ${editmodeClass}`}>
<HeadersTable
cellWidth={cellWidth}
columnSeparatorWidth={columnSeparatorWidth}
component={component}
data={data}
general={general}
isKpi
styling={styling}
/>
<LinkedScrollSection linkVertical>
<DataTable
cellWidth={cellWidth}
columnSeparatorWidth={columnSeparatorWidth}
component={component}
data={data}
general={general}
renderData={false}
styling={styling}
/>
</LinkedScrollSection>
</div>
) : (
<LinkedScrollWrapper>
<div className={`kpi-table ${editmodeClass}`}>
<div
className={`data-table ${editmodeClass}`}
style={{ width: general.cellWidth ? 'auto' : '100%' }}
ref={this.onDataTableRefSet}
>
<LinkedScrollSection linkHorizontal>
<HeadersTable
cellWidth={cellWidth}
columnSeparatorWidth={columnSeparatorWidth}
component={component}
data={data}
general={general}
isKpi
isKpi={false}
styling={styling}
/>
<LinkedScrollSection linkVertical>
<DataTable
cellWidth={cellWidth}
columnSeparatorWidth={columnSeparatorWidth}
component={component}
data={data}
general={general}
renderData={false}
styling={styling}
/>
</LinkedScrollSection>
</div>
<div
className={`data-table ${editmodeClass}`}
style={{ width: general.cellWidth ? 'auto' : '100%' }}
ref={this.onDataTableRefSet}
</LinkedScrollSection>
<LinkedScrollSection
linkHorizontal
linkVertical
>
<LinkedScrollSection linkHorizontal>
<HeadersTable
cellWidth={cellWidth}
columnSeparatorWidth={columnSeparatorWidth}
component={component}
data={data}
general={general}
isKpi={false}
styling={styling}
/>
</LinkedScrollSection>
<LinkedScrollSection linkHorizontal linkVertical>
<DataTable
cellWidth={cellWidth}
columnSeparatorWidth={columnSeparatorWidth}
component={component}
data={data}
general={general}
styling={styling}
/>
</LinkedScrollSection>
</div>
</LinkedScrollWrapper>
)}
<DataTable
cellWidth={cellWidth}
columnSeparatorWidth={columnSeparatorWidth}
component={component}
data={data}
general={general}
styling={styling}
/>
</LinkedScrollSection>
</div>
</LinkedScrollWrapper>
</div>
);
}
@@ -131,9 +123,9 @@ Root.propTypes = {
component: PropTypes.shape({}).isRequired,
editmodeClass: PropTypes.string.isRequired,
state: PropTypes.shape({
data: PropTypes.object,
general: PropTypes.object,
styling: PropTypes.object
data: PropTypes.object.isRequired,
general: PropTypes.object.isRequired,
styling: PropTypes.object.isRequired
}).isRequired
};