Skip to content

Commit

Permalink
feat: support ListObject (#526)
Browse files Browse the repository at this point in the history
* feat: support `ListObject`

* chore: add listbox fixture

* refactor: refactor

* refactor: refactor

* refactor: update error message

Co-authored-by: Christoffer Åström <stoffeastrom@gmail.com>
Co-authored-by: Tobias Åström <tsm@qlik.com>
  • Loading branch information
3 people committed Nov 2, 2020
1 parent c7529f5 commit c0daa04
Show file tree
Hide file tree
Showing 13 changed files with 283 additions and 32 deletions.
24 changes: 12 additions & 12 deletions apis/nucleus/src/components/Cell.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ const validateInfo = (min, info, getDescription, translatedError, translatedCalc
}`;
const customDescription = getDescription(i);
const description = customDescription ? `${customDescription}${label.length ? delimiter : ''}` : null;

return {
description,
label,
Expand All @@ -139,21 +138,22 @@ const validateInfo = (min, info, getDescription, translatedError, translatedCalc
});
};

const getInfo = (info) => (info && (Array.isArray(info) ? info : [info])) || [];

const validateTarget = (translator, layout, properties, def) => {
const minD = def.dimensions.min();
const minM = def.measures.min();
const hc = def.resolveLayout(layout);

const c = def.resolveLayout(layout);
const reqDimErrors = validateInfo(
minD,
hc.qDimensionInfo,
getInfo(c.qDimensionInfo),
(i) => def.dimensions.description(properties, i),
translator.get('Visualization.Invalid.Dimension'),
translator.get('Visualization.UnfulfilledCalculationCondition')
);
const reqMeasErrors = validateInfo(
minM,
hc.qMeasureInfo,
getInfo(c.qMeasureInfo),
(i) => def.measures.description(properties, i),
translator.get('Visualization.Invalid.Measure'),
translator.get('Visualization.UnfulfilledCalculationCondition')
Expand All @@ -175,21 +175,21 @@ const validateCubes = (translator, targets, layout) => {
const def = targets[i];
const minD = def.dimensions.min();
const minM = def.measures.min();
const hc = def.resolveLayout(layout);
const d = (hc.qDimensionInfo || []).filter(filterData); // Filter out optional calc conditions
const m = (hc.qMeasureInfo || []).filter(filterData); // Filter out optional calc conditions
const c = def.resolveLayout(layout);
const d = getInfo(c.qDimensionInfo).filter(filterData); // Filter out optional calc conditions
const m = getInfo(c.qMeasureInfo).filter(filterData); // Filter out optional calc conditions
aggMinD += minD;
aggMinM += minM;
if (d.length < minD || m.length < minM) {
hasUnfulfilledErrors = true;
}
if (hc.qError) {
if (c.qError) {
hasLayoutErrors = true;
hasLayoutUnfulfilledCalculcationCondition = hc.qError.qErrorCode === 7005;
hasLayoutUnfulfilledCalculcationCondition = c.qError.qErrorCode === 7005;
const title =
// eslint-disable-next-line no-nested-ternary
hasLayoutUnfulfilledCalculcationCondition && hc.qCalcCondMsg
? hc.qCalcCondMsg
hasLayoutUnfulfilledCalculcationCondition && c.qCalcCondMsg
? c.qCalcCondMsg
: hasLayoutUnfulfilledCalculcationCondition
? translator.get('Visualization.UnfulfilledCalculationCondition')
: translator.get('Visualization.LayoutError');
Expand Down
2 changes: 1 addition & 1 deletion apis/nucleus/src/object/__tests__/hc-handler.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('hc-handler', () => {
};

h = handler({
hc,
dc: hc,
def,
properties: 'props',
});
Expand Down
2 changes: 1 addition & 1 deletion apis/nucleus/src/object/__tests__/populator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ describe('populator', () => {
const resolved = { qDimensions: [] };
populate({ sn, properties: { a: { b: { c: resolved } } }, fields: [1] });
expect(handler).to.have.been.calledWithExactly({
hc: resolved,
dc: resolved,
def: target,
properties: { a: { b: { c: resolved } } },
});
Expand Down
14 changes: 7 additions & 7 deletions apis/nucleus/src/object/hc-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const nxMeasure = (f) => ({
},
});

export default function hcHandler({ hc, def, properties }) {
export default function hcHandler({ dc: hc, def, properties }) {
hc.qDimensions = hc.qDimensions || [];
hc.qMeasures = hc.qMeasures || [];
hc.qInterColumnSortOrder = hc.qInterColumnSortOrder || [];
Expand All @@ -45,7 +45,7 @@ export default function hcHandler({ hc, def, properties }) {

const objectProperties = properties;

const h = {
const handler = {
dimensions() {
return hc.qDimensions;
},
Expand Down Expand Up @@ -78,7 +78,7 @@ export default function hcHandler({ hc, def, properties }) {
dimension.qAttributeDimensions = dimension.qAttributeDimensions || [];
// ========= end defaults =============

if (hc.qDimensions.length < h.maxDimensions()) {
if (hc.qDimensions.length < handler.maxDimensions()) {
hc.qDimensions.push(dimension);
addIndex(hc.qInterColumnSortOrder, hc.qDimensions.length - 1);
def.dimensions.added(dimension, objectProperties);
Expand Down Expand Up @@ -115,7 +115,7 @@ export default function hcHandler({ hc, def, properties }) {
measure.qAttributeDimensions = measure.qAttributeDimensions || [];
measure.qAttributeExpressions = measure.qAttributeExpressions || [];

if (hc.qMeasures.length < h.maxMeasures()) {
if (hc.qMeasures.length < handler.maxMeasures()) {
hc.qMeasures.push(measure);
addIndex(hc.qInterColumnSortOrder, hc.qDimensions.length + hc.qMeasures.length - 1);
def.measures.added(measure, objectProperties);
Expand All @@ -141,12 +141,12 @@ export default function hcHandler({ hc, def, properties }) {
return def.measures.max(hc.qDimensions.length);
},
canAddDimension() {
return hc.qDimensions.length < h.maxDimensions();
return hc.qDimensions.length < handler.maxDimensions();
},
canAddMeasure() {
return hc.qMeasures.length < h.maxMeasures();
return hc.qMeasures.length < handler.maxMeasures();
},
};

return h;
return handler;
}
72 changes: 72 additions & 0 deletions apis/nucleus/src/object/lo-handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* eslint no-param-reassign:0 */

import uid from './uid';

const nxDimension = (f) => ({
qDef: {
qFieldDefs: [f],
},
});

export default function loHandler({ dc: lo, def, properties }) {
lo.qInitialDataFetch = lo.qInitialDataFetch || [];

const objectProperties = properties;

const handler = {
dimensions() {
if (!lo.qDef || !lo.qDef.qFieldDefs || lo.qDef.qFieldDefs.length === 0) return [];
return [lo];
},
measures() {
return [];
},
addDimension(d) {
const dimension =
typeof d === 'string'
? nxDimension(d)
: {
...d,
qDef: d.qDef || {},
};
dimension.qDef.cId = dimension.qDef.cId || uid();

dimension.qDef.qSortCriterias = dimension.qDef.qSortCriterias || [
{
qSortByState: 1,
qSortByLoadOrder: 1,
qSortByNumeric: 1,
qSortByAscii: 1,
},
];
Object.keys(dimension).forEach((k) => {
lo[k] = dimension[k];
});
def.dimensions.added(dimension, objectProperties);
},
removeDimension(idx) {
const dimension = lo;
Object.keys(dimension).forEach((k) => {
delete lo[k];
});
def.dimensions.removed(dimension, objectProperties, idx);
},
addMeasure() {},
removeMeasure() {},

maxDimensions() {
return 1;
},
maxMeasures() {
return 0;
},
canAddDimension() {
return lo.qDef && lo.qDef.qFieldDefs ? lo.qDef.qFieldDefs.length === 0 : !lo.qDef;
},
canAddMeasure() {
return false;
},
};

return handler;
}
2 changes: 1 addition & 1 deletion apis/nucleus/src/object/populator.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default function populateData({ sn, properties, fields }) {
}

const hc = hcHandler({
hc: p,
dc: p,
def: target,
properties,
});
Expand Down
32 changes: 30 additions & 2 deletions apis/supernova/__tests__/unit/qae.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ describe('qae', () => {
data: {
targets: [
{
path: 'qhc',
path: '/qHyperCubeDefFoo',
dimensions: {
min: () => 3,
max: () => 7,
Expand All @@ -105,7 +105,35 @@ describe('qae', () => {
],
},
})
).to.throw('Incorrect definition for qHyperCubeDef at qhc');
).to.throw('Incorrect definition for qHyperCubeDef at /qHyperCubeDefFoo');
});
it('should throw with incorrect listobject def', () => {
expect(() =>
qae({
data: {
targets: [
{
path: '/qListObjectDefFoo',
dimensions: {
min: () => 3,
max: () => 7,
added: () => 'a',
description: () => 'Slice',
moved: () => 'c',
replaced: () => 'd',
},
measures: {
min: 2,
max: 4,
added: () => 'b',
description: () => 'Angle',
removed: () => 'e',
},
},
],
},
})
).to.throw('Incorrect definition for qListObjectDef at /qListObjectDefFoo');
});
it('should resolve layout', () => {
const t = qae({
Expand Down
7 changes: 5 additions & 2 deletions apis/supernova/src/qae.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,11 @@ const resolveValue = (data, reference, defaultValue) => {
function target(def) {
const propertyPath = def.path || '/qHyperCubeDef';
const layoutPath = propertyPath.slice(0, -3);
if (/\/qHyperCube$/.test(layoutPath) === false) {
throw new Error(`Incorrect definition for qHyperCubeDef at ${propertyPath}`);
if (/\/(qHyperCube|qListObject)$/.test(layoutPath) === false) {
const d = layoutPath.includes('/qHyperCube') ? 'qHyperCubeDef' : 'qListObjectDef';
throw new Error(
`Incorrect definition for ${d} at ${propertyPath}. Valid paths include /qHyperCubeDef or /qListObjectDef, e.g. data/qHyperCubeDef`
);
}
return {
propertyPath,
Expand Down
4 changes: 2 additions & 2 deletions commands/serve/web/components/property-panel/Data.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';

import { List, ListItem, Typography } from '@material-ui/core';

import HyperCube from './HyperCube';
import DataCube from './DataCube';

export default function Data({ setProperties, sn, properties }) {
if (!sn) {
Expand All @@ -19,7 +19,7 @@ export default function Data({ setProperties, sn, properties }) {
<List>
{targets.map((t) => (
<ListItem key={t.propertyPath} divider disableGutters>
<HyperCube target={t} properties={properties} setProperties={setProperties} />
<DataCube target={t} properties={properties} setProperties={setProperties} />
</ListItem>
))}
</List>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useMemo } from 'react';

import hcHandler from '@nebula.js/nucleus/src/object/hc-handler';
import loHandler from '@nebula.js/nucleus/src/object/lo-handler';

import { Typography } from '@material-ui/core';

Expand All @@ -25,12 +26,13 @@ const getValue = (data, reference, defaultValue) => {
return dataContainer;
};

export default function HyperCube({ setProperties, target, properties }) {
export default function DataCube({ setProperties, target, properties }) {
const createHandler = target.propertyPath.match('/qHyperCube') ? hcHandler : loHandler;
const handler = useMemo(
() =>
hcHandler({
createHandler({
def: target,
hc: getValue(properties, target.propertyPath),
dc: getValue(properties, target.propertyPath),
properties,
}),
[properties]
Expand Down
2 changes: 1 addition & 1 deletion commands/serve/web/components/property-panel/Fields.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export default function Fields({
<Typography variant="overline">{label}</Typography>
<List dense>
{items.map((d, i) => (
<ListItem disableGutters key={d.qDef.cId || i}>
<ListItem disableGutters key={(d.qDef && d.qDef.cId) || i}>
<ListItemText>
<FieldTitle field={d} libraryItems={libraryItems} type={type} />
</ListItemText>
Expand Down
7 changes: 7 additions & 0 deletions test/fixtures/viz/listbox/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "listbox",
"main": "dist/listbox.js",
"peerDependencies": {
"@nebula.js/stardust": "*"
}
}

0 comments on commit c0daa04

Please sign in to comment.