Compare commits

..

1 Commits

Author SHA1 Message Date
Albert Backenhof
dfb30285ab Updated default error message
-Previous isn't grammatically correct.

Issue: DEB-160
2019-04-10 12:15:28 +02:00
11 changed files with 115 additions and 179 deletions

View File

@@ -33,26 +33,24 @@ class DataCell extends React.PureComponent {
styling
} = this.props;
let textAlignment = styling.options.textAlignment || 'Right';
const isColumnPercentageBased = (/%/).test(measurement.format);
let formattedMeasurementValue = formatMeasurementValue(measurement, styling);
if (styleBuilder.hasComments()) {
formattedMeasurementValue = '.';
}
let textAlignment = 'Right';
const textAlignmentProp = styling.options.textAlignment;
if (textAlignmentProp) {
textAlignment = textAlignmentProp;
}
let cellStyle = {
fontFamily: styling.options.fontFamily,
...styleBuilder.getStyle(),
paddingLeft: '5px',
textAlign: textAlignment
};
const isEmptyCell = measurement.displayValue === '';
const isColumnPercentageBased = (/%/).test(measurement.format);
let formattedMeasurementValue;
if (isEmptyCell) {
formattedMeasurementValue = '';
cellStyle.cursor = 'default';
} else if (styleBuilder.hasComments()) {
formattedMeasurementValue = '.';
} else {
formattedMeasurementValue = formatMeasurementValue(measurement, styling);
}
};
const { semaphoreColors, semaphoreColors: { fieldsToApplyTo } } = styling;
const isValidSemaphoreValue = !styleBuilder.hasComments() && !isNaN(measurement.value);
@@ -81,7 +79,7 @@ class DataCell extends React.PureComponent {
return (
<td
className={`${cellClass}${general.cellSuffix}`}
onClick={isEmptyCell ? null : this.handleSelect}
onClick={this.handleSelect}
style={cellStyle}
>
<Tooltip

View File

@@ -69,7 +69,6 @@ const DataTable = ({ data, general, qlik, renderData, styling }) => {
</td>
);
}
const { dimension1: dimension1Info, dimension2, measurement } = measurementData.parents;
const id = `${dimension1Info.elementNumber}-${dimension2 && dimension2.elementNumber}-${measurement.header}`;
return (

View File

@@ -6,7 +6,7 @@ function createCube (definition, app) {
});
}
async function buildDataCube (originCubeDefinition, hasTwoDimensions, app) {
async function buildDataCube (originCubeDefinition, dimensionIndexes, app) {
const cubeDefinition = {
...originCubeDefinition,
qInitialDataFetch: [
@@ -15,18 +15,62 @@ async function buildDataCube (originCubeDefinition, hasTwoDimensions, app) {
qWidth: 10
}
],
qDimensions: [originCubeDefinition.qDimensions[0]],
qDimensions: [originCubeDefinition.qDimensions[dimensionIndexes.dimension1]],
qMeasures: originCubeDefinition.qMeasures
};
if (hasTwoDimensions) {
cubeDefinition.qDimensions.push(originCubeDefinition.qDimensions[1]);
if (dimensionIndexes.dimension2) {
cubeDefinition.qDimensions.push(originCubeDefinition.qDimensions[dimensionIndexes.dimension2]);
}
const cube = await createCube(cubeDefinition, app);
return cube.qHyperCube.qDataPages[0].qMatrix;
}
export async function initializeDataCube (component, layout) {
async function buildDesignCube (originCubeDefinition, dimensionIndexes, app) {
if (!dimensionIndexes.design) {
return null;
}
const cube = await createCube({
qInitialDataFetch: [
{
qHeight: 1000,
qWidth: 1
}
],
qDimensions: [originCubeDefinition.qDimensions[dimensionIndexes.design]]
}, app);
return cube.qHyperCube.qDataPages[0].qMatrix;
}
const STYLE_SEPARATOR_COUNT = 7;
function findDesignDimension (qMatrix) {
return qMatrix[0].map(entry => (entry.qText.match(/;/g) || []).length).indexOf(STYLE_SEPARATOR_COUNT);
}
function getDimensionIndexes (dimensionsInformation, designDimensionIndex) {
const hasDesign = designDimensionIndex !== -1;
const nonDesignDimensionCount = hasDesign ? dimensionsInformation.length - 1 : dimensionsInformation.length;
const dimension1 = designDimensionIndex === 0 ? 1 : 0;
let dimension2 = false;
if (nonDesignDimensionCount === 2) {
dimension2 = hasDesign && designDimensionIndex < 2 ? 2 : 1;
}
const design = hasDesign && designDimensionIndex;
const firstMeasurementIndex = dimensionsInformation.length;
return {
design,
dimension1,
dimension2,
firstMeasurementIndex
};
}
export async function initializeCubes ({ component, layout }) {
const app = qlik.currApp(component);
const designDimensionIndex = findDesignDimension(layout.qHyperCube.qDataPages[0].qMatrix);
const dimensionsInformation = layout.qHyperCube.qDimensionInfo;
const dimensionIndexes = getDimensionIndexes(dimensionsInformation, designDimensionIndex);
let properties;
if (component.backendApi.isSnapshot) {
@@ -36,24 +80,12 @@ export async function initializeDataCube (component, layout) {
properties = await component.backendApi.getProperties();
}
return buildDataCube(
properties.qHyperCubeDef, layout.qHyperCube.qDimensionInfo.length === 2, app);
}
const originCubeDefinition = properties.qHyperCubeDef;
const designCube = await buildDesignCube(originCubeDefinition, dimensionIndexes, app);
const dataCube = await buildDataCube(originCubeDefinition, dimensionIndexes, app);
export function initializeDesignList (component, layout) {
if (!layout.stylingfield) {
return null;
}
return new Promise(resolve => {
const app = qlik.currApp(component);
const stylingField = app.field(layout.stylingfield);
const listener = function () {
const data = stylingField.rows.map(row => row.qText);
stylingField.OnData.unbind(listener);
resolve(data);
};
stylingField.OnData.bind(listener);
stylingField.getData();
});
return {
design: designCube,
data: dataCube
};
}

View File

@@ -18,9 +18,6 @@ const definition = {
},
uses: 'data'
},
sorting: {
uses: 'sorting'
},
settings: {
items: {
ConceptSemaphores: conceptSemaphores,
@@ -31,25 +28,8 @@ const definition = {
},
uses: 'settings'
},
about: {
component: 'items',
label: 'About',
items: {
header: {
label: 'P&L pivot',
style: 'header',
component: 'text'
},
paragraph1: {
label: `P&L pivot is a Qlik Sense extension which allows you to display Profit & Loss
reporting with color and font customizations.`,
component: 'text'
},
paragraph2: {
label: 'P&L pivot is based upon an extension created by Ivan Felipe Asensio.',
component: 'text'
}
}
sorting: {
uses: 'sorting'
}
},
type: 'items'

View File

@@ -55,7 +55,7 @@ const pagination = {
ref: 'errormessage',
label: 'Default error message',
type: 'string',
defaultValue: 'Ups! It seems you asked for too many data. Please filter more to see the whole picture.'
defaultValue: 'Unable to display all the data. Apply more filters to limit the amount of displayed data.'
}
}
};

View File

@@ -1,53 +1,12 @@
const qlik = window.require('qlik');
// fixes case for when there are 3 dimensions, missies the case with 1 design dimension and 1 data dimension
function hasDesignDimension (data) {
return data.qHyperCubeDef.qDimensions.length > 2;
}
function getFieldList () {
return new Promise(function (resolve) {
const app = qlik.currApp();
app.getList('FieldList').then(function (model) {
// Close the model to prevent any updates.
app.destroySessionObject(model.layout.qInfo.qId);
// This is a bit iffy, might be smarter to reject and handle empty lists on the props instead.
if (!model.layout.qFieldList.qItems) {
return resolve([]);
}
// Resolve an array with master objects.
return resolve(model.layout.qFieldList.qItems.map(function (item) {
return {
value: item.qName,
label: item.qName
};
}));
});
});
}
const tableFormat = {
type: 'items',
label: 'Table Format',
items: {
StylingField: {
ref: 'stylingfield',
disabledRef: '',
type: 'string',
component: 'dropdown',
label: 'Style with field',
options: function () {
return getFieldList().then(function (items) {
items.unshift(
{
value: '',
label: 'None'
});
return items;
});
}
},
IndentBool: {
ref: 'indentbool',
type: 'boolean',

View File

@@ -50,7 +50,7 @@ const MeasurementColumnHeader = ({ baseCSS, general, hasSecondDimension, measure
};
return (
<th
className={`grid-cells2${general.cellSuffix}`}
className={`grid-cells2 ${general.cellSuffix}`}
style={style}
>
<span

View File

@@ -20,16 +20,12 @@ export default {
},
data: {
dimensions: {
max: function (nMeasures) {
return nMeasures < 9 ? 2 : 1;
},
max: 3,
min: 1,
uses: 'dimensions'
},
measures: {
max: function (nDims) {
return nDims < 2 ? 9 : 8;
},
max: 8,
min: 1,
uses: 'measures'
}

View File

@@ -107,16 +107,16 @@ function generateMatrixCell ({ cell, dimension1Information, dimension2Informatio
}
let lastRow = 0;
function generateDataSet (
component, dimensionsInformation, measurementsInformation, dataCube) {
function generateDataSet (component, dimensionsInformation, measurementsInformation, cubes) {
const dimension1 = [];
const dimension2 = [];
const measurements = generateMeasurements(measurementsInformation);
let dimension1 = [];
let dimension2 = [];
let matrix = [];
const hasSecondDimension = dimensionsInformation.length > 1;
dataCube.forEach(row => {
let previousDim1Entry;
const hasDesignDimension = cubes.design;
const hasSecondDimension = hasDesignDimension ? dimensionsInformation.length > 2 : dimensionsInformation.length > 1;
cubes.data.forEach(row => {
lastRow += 1;
const dimension1Entry = generateDimensionEntry(dimensionsInformation[0], row[0]);
dimension1.push(dimension1Entry);
@@ -127,7 +127,7 @@ function generateDataSet (
dimension2.push(dimension2Entry);
firstDataCell = 2;
}
let matrixRow = row
const matrixRow = row
.slice(firstDataCell, row.length)
.map((cell, cellIndex) => {
const measurementInformation = measurements[cellIndex];
@@ -143,65 +143,36 @@ function generateDataSet (
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);
}
const currentDim1Entry = row[0].qText;
const isSameDimension1AsPrevious = currentDim1Entry === previousDim1Entry;
if (isSameDimension1AsPrevious) {
const updatedRow = matrix[matrix.length - 1].concat(matrixRow);
matrix = [
...matrix.slice(0, matrix.length - 1),
updatedRow
];
} else {
matrix[matrix.length] = matrixRow;
}
previousDim1Entry = currentDim1Entry;
} else {
matrix[matrix.length] = matrixRow;
}
matrix[appendToRowIndex] = matrixRow;
});
// filter header dimensions to only have distinct values
dimension1 = distinctArray(dimension1);
dimension2 = distinctArray(dimension2);
// Make sure all rows are saturated, otherwise data risks being displayed in the wrong column
matrix = matrix.map((row, rowIndex) => {
if (row.length == dimension2.length) {
// Row is saturated
return row;
}
// Row is not saturated, so must add empty cells to fill the gaps
let newRow = [];
let cellIndex = 0;
dimension2.forEach(dim => {
measurements.forEach(measurement => {
if (cellIndex < row.length
&& row[cellIndex].parents.dimension2.elementNumber === dim.elementNumber
&& row[cellIndex].parents.measurement.header === measurement.name) {
newRow.push(row[cellIndex]);
cellIndex++;
} else {
newRow.push({
displayValue: '',
parents: {
dimension1: { elementNumber: rowIndex },
dimension2: { elementNumber: dim.elementNumber },
measurement: { header: measurement.name }
}
});
}
});
});
return newRow;
});
return {
dimension1: dimension1,
dimension2: dimension2,
dimension1: distinctArray(dimension1),
dimension2: distinctArray(dimension2),
matrix,
measurements
};
}
function initializeTransformed ({ $element, component, dataCube, designList, layout }) {
function initializeTransformed ({ $element, component, cubes, layout }) {
const dimensionsInformation = component.backendApi.getDimensionInfos();
const measurementsInformation = component.backendApi.getMeasureInfos();
const dimensionCount = layout.qHyperCube.qDimensionInfo.length;
@@ -212,18 +183,19 @@ function initializeTransformed ({ $element, component, dataCube, designList, lay
dimension2,
measurements,
matrix
} = generateDataSet(component, dimensionsInformation, measurementsInformation, dataCube);
} = generateDataSet(component, dimensionsInformation, measurementsInformation, cubes);
const customSchemaBasic = [];
const customSchemaFull = [];
let customHeadersCount = 0;
if (designList && designList.length > 0) {
const headers = designList[0].split(';');
if (cubes.design) {
const allTextLines = cubes.design.map(entry => entry[0].qText);
const headers = allTextLines[0].split(';');
customHeadersCount = headers.length;
for (let lineNumber = 0; lineNumber < designList.length; lineNumber += 1) {
for (let lineNumber = 0; lineNumber < allTextLines.length; lineNumber += 1) {
customSchemaFull[lineNumber] = new Array(headers.length);
const data = designList[lineNumber].split(';');
const data = allTextLines[lineNumber].split(';');
if (data.length === headers.length) {
for (let headerIndex = 0; headerIndex < headers.length; headerIndex += 1) {
@@ -267,7 +239,7 @@ function initializeTransformed ({ $element, component, dataCube, designList, lay
count: customHeadersCount,
full: customSchemaFull
},
hasCustomFileStyle: Boolean(designList),
hasCustomFileStyle: Boolean(cubes.design),
headerOptions: {
alignment: getAlignment(layout.HeaderAlign),
colorSchema: layout.HeaderColorSchema.color,

View File

@@ -2,24 +2,25 @@ import initializeStore from './store';
import React from 'react';
import ReactDOM from 'react-dom';
import Root from './root.jsx';
import { initializeDataCube, initializeDesignList } from './dataset';
import { initializeCubes } from './dataset';
export default async function paint ($element, layout, component) {
const dataCube = await initializeDataCube(component, layout);
const designList = await initializeDesignList(component, layout);
const cubes = await initializeCubes({
component,
layout
});
const state = await initializeStore({
$element,
component,
dataCube,
designList,
cubes,
layout
});
const editmodeClass = component.inAnalysisState() ? '' : 'edit-mode';
const jsx = (
<Root
editmodeClass={editmodeClass}
qlik={component}
state={state}
editmodeClass={editmodeClass}
/>
);

View File

@@ -1,11 +1,10 @@
import initializeTransformed from './initialize-transformed';
async function initialize ({ $element, layout, component, dataCube, designList }) {
async function initialize ({ $element, layout, component, cubes }) {
const transformedProperties = await initializeTransformed({
$element,
component,
dataCube,
designList,
cubes,
layout
});