@gooddata/react-components
Version:
GoodData.UI - A powerful JavaScript library for building analytical applications
1,314 lines (1,165 loc) • 156 kB
text/typescript
// (C) 2007-2020 GoodData Corporation
import range = require("lodash/range");
import get = require("lodash/get");
import set = require("lodash/set");
import isNil = require("lodash/isNil");
import cloneDeep = require("lodash/cloneDeep");
import { Execution } from "@gooddata/typings";
import Highcharts from "../highcharts/highchartsEntryPoint";
import { findMeasureGroupInDimensions } from "../../../../helpers/executionResultHelper";
import { immutableSet } from "../../utils/common";
import {
isNegativeValueIncluded,
validateData,
getSeriesItemData,
getSeries,
getDrillableSeries,
customEscape,
buildTooltipFactory,
buildTooltipForTwoAttributesFactory,
generateTooltipHeatmapFn,
generateTooltipXYFn,
buildTooltipTreemapFactory,
getBubbleChartSeries,
getHeatmapDataClasses,
getTreemapAttributes,
isDerivedMeasure,
IValidationResult,
getHeatmapSeries,
} from "../chartOptionsBuilder";
import { DEFAULT_CATEGORIES_LIMIT } from "../highcharts/commonConfiguration";
import { generateChartOptions, getMVS, getMVSForViewByTwoAttributes } from "./helper";
import * as headerPredicateFactory from "../../../../factory/HeaderPredicateFactory";
import * as fixtures from "../../../../../stories/test_data/fixtures";
import { PIE_CHART_LIMIT, STACK_BY_DIMENSION_INDEX } from "../constants";
import { DEFAULT_COLOR_PALETTE, getLighterColor, getRgbString, GRAY, TRANSPARENT } from "../../utils/color";
import { IColorStrategy } from "../colorFactory";
import {
IChartConfig,
IColorPaletteItem,
IPointData,
IChartOptions,
IMeasuresStackConfig,
} from "../../../../interfaces/Config";
import { VisualizationTypes } from "../../../../constants/visualizationTypes";
import { NORMAL_STACK, PERCENT_STACK } from "../highcharts/getOptionalStackingConfiguration";
import HeatmapColorStrategy from "../colorStrategies/heatmap";
import TreemapColorStrategy from "../colorStrategies/treemap";
import BubbleChartColorStrategy from "../colorStrategies/bubbleChart";
import MeasureColorStrategy from "../colorStrategies/measure";
import AttributeColorStrategy from "../colorStrategies/attribute";
const FIRST_DEFAULT_COLOR_ITEM_AS_STRING = getRgbString(DEFAULT_COLOR_PALETTE[0]);
const SECOND_DEFAULT_COLOR_ITEM_AS_STRING = getRgbString(DEFAULT_COLOR_PALETTE[1]);
function getMVSTreemap(dataSet: any) {
const {
executionResponse: { dimensions },
executionResult: { headerItems },
mdObject,
} = dataSet;
const measureGroup = findMeasureGroupInDimensions(dimensions);
const { viewByAttribute, stackByAttribute } = getTreemapAttributes(dimensions, headerItems, mdObject);
return {
measureGroup,
viewByAttribute,
stackByAttribute,
};
}
function getSeriesItemDataParameters(dataSet: any, seriesIndex: any) {
const seriesItem = dataSet.executionResult.data[seriesIndex];
const { measureGroup, viewByAttribute, stackByAttribute } = getMVS(dataSet);
return [seriesItem, seriesIndex, measureGroup, viewByAttribute, stackByAttribute];
}
describe("chartOptionsBuilder", () => {
const DEFAULT_TOOLTIP_CONTENT_WIDTH = 320;
const SMALL_TOOLTIP_CONTENT_WIDTH = 200;
const { COLUMN, LINE, COMBO } = VisualizationTypes;
const barChartWithStackByAndViewByAttributesOptions = generateChartOptions();
const barChartWith3MetricsAndViewByAttributeOptions = generateChartOptions(
fixtures.barChartWith3MetricsAndViewByAttribute,
);
function getValues(str: string): string[] {
const strWithoutHiddenSpan = str.replace(/<span[^><]+max-content[^<>]+>[^<]+<\/span>/g, "");
const test = />([^<]+)<\/span>/g;
const result = strWithoutHiddenSpan.match(test).map((match: string) => match.slice(1, -7));
return (result && result.length) >= 2 ? result : null;
}
function getStyleMaxWidth(str: string): string[] {
const strWithoutHiddenSpan = str.replace(/<span[^><]+max-content[^<>]+>[^<]+<\/span>/g, "");
const testRegex = /max-width: ([^;:]+)px;/g;
return strWithoutHiddenSpan.match(testRegex).map((match: string): string => match.slice(11, -3));
}
const pieAndTreemapDataSet = {
...fixtures.pieChartWithMetricsOnly,
executionResult: {
...fixtures.pieChartWithMetricsOnly.executionResult,
data: [["-1", "38310753.45", "9011389.956"]],
},
};
const pieChartOptionsWithNegativeValue = generateChartOptions(pieAndTreemapDataSet, { type: "pie" });
const treemapOptionsWithNegativeValue = generateChartOptions(pieAndTreemapDataSet, { type: "treemap" });
const pieChartWithMetricsOnlyOptions: any = generateChartOptions(
{
...fixtures.pieChartWithMetricsOnly,
},
{
type: "pie",
},
);
const pointForSmallCharts = {
node: {
isLeaf: true,
},
value: 300,
x: 0,
y: 0,
series: {
chart: {
plotWidth: 200,
},
name: "name",
userOptions: {
dataLabels: {
formatGD: "abcd",
},
},
},
};
describe("isNegativeValueIncluded", () => {
it("should return true if there is at least one negative value in series", () => {
expect(isNegativeValueIncluded(pieChartOptionsWithNegativeValue.data.series)).toBe(true);
});
it("should return false if there are no negative values in series", () => {
expect(isNegativeValueIncluded(pieChartWithMetricsOnlyOptions.data.series)).toBe(false);
});
});
describe("validateData", () => {
describe("user supplied limits", () => {
it('should validate with "dataTooLarge: true" against series limit', () => {
const validationResult = validateData(
{
series: 1,
},
barChartWith3MetricsAndViewByAttributeOptions,
);
expect(validationResult).toEqual({
dataTooLarge: true,
hasNegativeValue: false,
});
});
it('should validate with "dataTooLarge: true" against categories limit', () => {
const validationResult = validateData(
{
categories: 1,
},
barChartWith3MetricsAndViewByAttributeOptions,
);
expect(validationResult).toEqual({
dataTooLarge: true,
hasNegativeValue: false,
});
});
});
describe("default limits", () => {
it("should be able to validate successfully", () => {
const chartOptions = barChartWithStackByAndViewByAttributesOptions;
const validationResult = validateData(undefined, chartOptions);
expect(validationResult).toEqual({
dataTooLarge: false,
hasNegativeValue: false,
});
});
it(
'should validate with "dataTooLarge: true" against default chart categories limit ' +
`of ${DEFAULT_CATEGORIES_LIMIT}`,
() => {
const chartOptions = generateChartOptions(
fixtures.barChartWith3MetricsAndViewByAttribute,
);
chartOptions.data.categories = range(DEFAULT_CATEGORIES_LIMIT + 1);
const validationResult = validateData(undefined, chartOptions);
expect(validationResult).toEqual({
dataTooLarge: true,
hasNegativeValue: false,
});
},
);
it('should validate with "dataTooLarge: true" against default pie chart series limit of 1', () => {
const chartOptions = generateChartOptions(fixtures.barChartWith3MetricsAndViewByAttribute, {
type: "pie",
});
const validationResult = validateData(undefined, chartOptions);
expect(validationResult).toEqual({
dataTooLarge: true,
hasNegativeValue: false,
});
});
it(
'should validate with "dataTooLarge: true" against default' +
`pie chart categories limit of ${PIE_CHART_LIMIT}`,
() => {
const chartOptions = generateChartOptions(fixtures.pieChartWithMetricsOnly, {
type: "pie",
});
chartOptions.data.categories = range(PIE_CHART_LIMIT + 1);
const validationResult = validateData(undefined, chartOptions);
expect(validationResult).toEqual({
dataTooLarge: true,
hasNegativeValue: false,
});
},
);
it('should validate with "hasNegativeValue: true" for pie chart if its series contains a negative value', () => {
const chartOptions = pieChartOptionsWithNegativeValue;
const validationResult = validateData(undefined, chartOptions);
expect(validationResult).toEqual({
dataTooLarge: false,
hasNegativeValue: true,
});
});
it('should validate with "hasNegativeValue: true" for treemap if its series contains a negative value', () => {
const validationResult = validateData(undefined, treemapOptionsWithNegativeValue);
expect(validationResult).toEqual({
dataTooLarge: false,
hasNegativeValue: true,
});
});
});
describe("Treemap filters out root nodes for dataPoints limit", () => {
it('should validate with "dataTooLarge: false" against data points limit', () => {
// 2 roots + 4 leafs
const treemapOptions = generateChartOptions(
fixtures.treemapWithMetricViewByAndStackByAttribute,
{
type: "treemap",
mdObject: fixtures.treemapWithMetricViewByAndStackByAttribute.mdObject,
},
);
const validationResult = validateData(
{
dataPoints: 4,
},
treemapOptions,
);
expect(validationResult).toEqual({
dataTooLarge: false,
hasNegativeValue: false,
});
});
it('should validate with "dataTooLarge: true" against data points limit', () => {
// 2 roots + 4 leafs
const treemapOptions = generateChartOptions(
fixtures.treemapWithMetricViewByAndStackByAttribute,
{
type: "treemap",
mdObject: fixtures.treemapWithMetricViewByAndStackByAttribute.mdObject,
},
);
const validationResult = validateData(
{
dataPoints: 3,
},
treemapOptions,
);
expect(validationResult).toEqual({
dataTooLarge: true,
hasNegativeValue: false,
});
});
});
describe("optional stacking with viewBy 2 attributes", () => {
const chartOptions: IChartOptions = {
isViewByTwoAttributes: true,
type: "column",
};
const testData = [
["false", "less", 3, { dataTooLarge: false, hasNegativeValue: false }],
["true", "greater", 31, { dataTooLarge: true, hasNegativeValue: false }],
];
it.each(testData)(
'should validate with "dataTooLarge: %s" when category number is %s than default limit',
(_result: string, _operator: string, categoriesNum: number, expected: IValidationResult) => {
const data = {
series: Array(1),
categories: Array(categoriesNum).fill({
name: "Month",
categories: Array(31),
}),
};
const validationResult = validateData(undefined, { ...chartOptions, data });
expect(validationResult).toEqual(expected);
},
);
});
});
describe("isDerivedMeasure", () => {
it("should return true if measureItem was defined as a popMeasure", () => {
const measureItem =
fixtures.barChartWithPopMeasureAndViewByAttribute.executionResponse.dimensions[
STACK_BY_DIMENSION_INDEX
].headers[0].measureGroupHeader.items[0];
const { afm } = fixtures.barChartWithPopMeasureAndViewByAttribute.executionRequest;
expect(isDerivedMeasure(measureItem, afm)).toEqual(true);
});
it("should return true if measureItem was defined as a previousPeriodMeasure", () => {
const measureItem =
fixtures.barChartWithPreviousPeriodMeasure.executionResponse.dimensions[
STACK_BY_DIMENSION_INDEX
].headers[0].measureGroupHeader.items[0];
const { afm } = fixtures.barChartWithPreviousPeriodMeasure.executionRequest;
expect(isDerivedMeasure(measureItem, afm)).toEqual(true);
});
it("should return false if measureItem was defined as a simple measure", () => {
const measureItem =
fixtures.barChartWithPopMeasureAndViewByAttribute.executionResponse.dimensions[
STACK_BY_DIMENSION_INDEX
].headers[0].measureGroupHeader.items[1];
const { afm } = fixtures.barChartWithPopMeasureAndViewByAttribute.executionRequest;
expect(isDerivedMeasure(measureItem, afm)).toEqual(false);
});
});
describe("getSeriesItemData", () => {
describe("in usecase of bar chart with pop measure and view by attribute", () => {
const parameters = getSeriesItemDataParameters(
fixtures.barChartWithPopMeasureAndViewByAttribute,
0,
);
const seriesItem = parameters[0];
const seriesIndex = parameters[1];
const measureGroup = parameters[2];
const viewByAttribute = parameters[3];
const stackByAttribute = parameters[4];
const attributeColorStrategy = new AttributeColorStrategy(
DEFAULT_COLOR_PALETTE,
undefined,
measureGroup,
viewByAttribute,
stackByAttribute,
fixtures.pieChartWithMetricsOnly.executionRequest.afm,
);
const seriesItemData = getSeriesItemData(
seriesItem,
seriesIndex,
measureGroup,
viewByAttribute,
stackByAttribute,
"column",
attributeColorStrategy,
);
it("should fill correct pointData name", () => {
expect(seriesItemData.map((pointData: any) => pointData.name)).toEqual([
"Amount previous year",
"Amount previous year",
"Amount previous year",
"Amount previous year",
"Amount previous year",
"Amount previous year",
]);
});
it("should parse all pointData values", () => {
expect(seriesItemData.map((pointData: any) => pointData.y)).toEqual([
null,
2773426.95,
8656468.2,
29140409.09,
60270072.2,
15785080.1,
]);
});
it("should enable markers for all non-null pointData values", () => {
expect(seriesItemData.map((pointData: any) => pointData.marker.enabled)).toEqual([
false,
true,
true,
true,
true,
true,
]);
});
});
describe("in usecase of bar chart with previous period measure and view by attribute", () => {
const parameters = getSeriesItemDataParameters(fixtures.barChartWithPreviousPeriodMeasure, 0);
const seriesItem = parameters[0];
const seriesIndex = parameters[1];
const measureGroup = parameters[2];
const viewByAttribute = parameters[3];
const stackByAttribute = parameters[4];
const attributeColorStrategy = new AttributeColorStrategy(
DEFAULT_COLOR_PALETTE,
undefined,
measureGroup,
viewByAttribute,
stackByAttribute,
fixtures.pieChartWithMetricsOnly.executionRequest.afm,
);
const seriesItemData = getSeriesItemData(
seriesItem,
seriesIndex,
measureGroup,
viewByAttribute,
stackByAttribute,
"column",
attributeColorStrategy,
);
it("should fill correct pointData name", () => {
expect(seriesItemData.map((pointData: any) => pointData.name)).toEqual([
"Primary measure - period ago",
"Primary measure - period ago",
]);
});
it("should parse all pointData values", () => {
expect(seriesItemData.map((pointData: any) => pointData.y)).toEqual([24000, null]);
});
it("should enable markers for all non-null pointData values", () => {
expect(seriesItemData.map((pointData: any) => pointData.marker.enabled)).toEqual([
true,
false,
]);
});
});
describe("in usecase of pie chart and treemap with metrics only", () => {
const parameters = getSeriesItemDataParameters(fixtures.pieChartWithMetricsOnly, 0);
const seriesItem = parameters[0];
const seriesIndex = parameters[1];
const measureGroup = parameters[2];
const viewByAttribute = parameters[3];
const stackByAttribute = parameters[4];
const metricColorStrategy = new MeasureColorStrategy(
DEFAULT_COLOR_PALETTE,
undefined,
viewByAttribute,
stackByAttribute,
fixtures.pieChartWithMetricsOnly.executionResponse,
fixtures.pieChartWithMetricsOnly.executionRequest.afm,
);
const pieSeriesItemData = getSeriesItemData(
seriesItem,
seriesIndex,
measureGroup,
viewByAttribute,
stackByAttribute,
"pie",
metricColorStrategy,
);
const treeMapColorStrategy = new TreemapColorStrategy(
DEFAULT_COLOR_PALETTE,
undefined,
viewByAttribute,
stackByAttribute,
fixtures.pieChartWithMetricsOnly.executionResponse,
fixtures.pieChartWithMetricsOnly.executionRequest.afm,
);
const treemapSeriesItemData = getSeriesItemData(
seriesItem,
seriesIndex,
measureGroup,
viewByAttribute,
stackByAttribute,
"treemap",
treeMapColorStrategy,
);
it("should fill correct pointData name", () => {
expect(pieSeriesItemData.map(pointData => pointData.name)).toEqual([
"Lost",
"Won",
"Expected",
]);
expect(treemapSeriesItemData.map(pointData => pointData.name)).toEqual([
"Lost",
"Won",
"Expected",
]);
});
it("should fill correct pointData color", () => {
expect(pieSeriesItemData.map(pointData => pointData.color)).toEqual([
FIRST_DEFAULT_COLOR_ITEM_AS_STRING,
SECOND_DEFAULT_COLOR_ITEM_AS_STRING,
getRgbString(DEFAULT_COLOR_PALETTE[2]),
]);
expect(treemapSeriesItemData.map(pointData => pointData.color)).toEqual([
FIRST_DEFAULT_COLOR_ITEM_AS_STRING,
SECOND_DEFAULT_COLOR_ITEM_AS_STRING,
getRgbString(DEFAULT_COLOR_PALETTE[2]),
]);
});
it("should fill correct pointData legendIndex", () => {
expect(pieSeriesItemData.map(pointData => pointData.legendIndex)).toEqual([0, 1, 2]);
expect(treemapSeriesItemData.map(pointData => pointData.legendIndex)).toEqual([0, 1, 2]);
});
it("should fill correct pointData format", () => {
expect(pieSeriesItemData.map(pointData => pointData.format)).toEqual([
"#,##0.00",
"#,##0.00",
"#,##0.00",
]);
expect(treemapSeriesItemData.map(pointData => pointData.format)).toEqual([
"#,##0.00",
"#,##0.00",
"#,##0.00",
]);
});
});
describe("in usecase of pie chart with an attribute", () => {
const parameters = getSeriesItemDataParameters(fixtures.barChartWithViewByAttribute, 0);
const seriesItem = parameters[0];
const seriesIndex = parameters[1];
const measureGroup = parameters[2];
const viewByAttribute = parameters[3];
const stackByAttribute = parameters[4];
const attributeColorStrategy = new AttributeColorStrategy(
DEFAULT_COLOR_PALETTE,
undefined,
viewByAttribute,
stackByAttribute,
fixtures.pieChartWithMetricsOnly.executionResponse,
fixtures.pieChartWithMetricsOnly.executionRequest.afm,
);
const pieSeriesItemData = getSeriesItemData(
seriesItem,
seriesIndex,
measureGroup,
viewByAttribute,
stackByAttribute,
"pie",
attributeColorStrategy,
);
const treeMapColorStrategy = new TreemapColorStrategy(
DEFAULT_COLOR_PALETTE,
undefined,
viewByAttribute,
stackByAttribute,
fixtures.barChartWithViewByAttribute.executionResponse,
fixtures.barChartWithViewByAttribute.executionRequest.afm,
);
const treemapSeriesItemData = getSeriesItemData(
seriesItem,
seriesIndex,
measureGroup,
viewByAttribute,
stackByAttribute,
"treemap",
treeMapColorStrategy,
);
it("should fill correct pointData name", () => {
expect(pieSeriesItemData.map(pointData => pointData.name)).toEqual([
"Direct Sales",
"Inside Sales",
]);
expect(treemapSeriesItemData.map(pointData => pointData.name)).toEqual([
"Direct Sales",
"Inside Sales",
]);
});
it("should fill correct pointData color", () => {
expect(pieSeriesItemData.map(pointData => pointData.color)).toEqual([
FIRST_DEFAULT_COLOR_ITEM_AS_STRING,
SECOND_DEFAULT_COLOR_ITEM_AS_STRING,
]);
expect(treemapSeriesItemData.map(pointData => pointData.color)).toEqual([
FIRST_DEFAULT_COLOR_ITEM_AS_STRING,
SECOND_DEFAULT_COLOR_ITEM_AS_STRING,
]);
});
it("should fill correct pointData legendIndex", () => {
expect(pieSeriesItemData.map(pointData => pointData.legendIndex)).toEqual([0, 1]);
expect(treemapSeriesItemData.map(pointData => pointData.legendIndex)).toEqual([0, 1]);
});
it("should fill correct pointData format", () => {
expect(pieSeriesItemData.map(pointData => pointData.format)).toEqual([
"#,##0.00",
"#,##0.00",
]);
expect(treemapSeriesItemData.map(pointData => pointData.format)).toEqual([
"#,##0.00",
"#,##0.00",
]);
});
});
});
describe("getSeries", () => {
describe("in usecase of bar chart with 3 measures and view by attribute", () => {
const dataSet = fixtures.barChartWith3MetricsAndViewByAttribute;
const { measureGroup, viewByAttribute, stackByAttribute } = getMVS(dataSet);
const attributeColorStrategy = new AttributeColorStrategy(
DEFAULT_COLOR_PALETTE,
undefined,
viewByAttribute,
stackByAttribute,
fixtures.barChartWith3MetricsAndViewByAttribute.executionResponse,
fixtures.barChartWith3MetricsAndViewByAttribute.executionRequest.afm,
);
const type = "column";
const seriesData = getSeries(
dataSet.executionResult.data,
measureGroup,
viewByAttribute,
stackByAttribute,
type,
{} as any,
attributeColorStrategy,
);
it("should return number of series equal to the count of measures", () => {
expect(seriesData.length).toBe(3);
});
it("should fill correct series name", () => {
expect(seriesData.map((seriesItem: any) => seriesItem.name)).toEqual([
"<button>Lost</button> ...",
"Won",
"Expected",
]);
});
it("should fill correct series color", () => {
expect(seriesData.map((seriesItem: any) => seriesItem.color)).toEqual([
FIRST_DEFAULT_COLOR_ITEM_AS_STRING,
SECOND_DEFAULT_COLOR_ITEM_AS_STRING,
getRgbString(DEFAULT_COLOR_PALETTE[2]),
]);
});
it("should fill correct series legendIndex", () => {
expect(seriesData.map((seriesItem: any) => seriesItem.legendIndex)).toEqual([0, 1, 2]);
});
it("should fill correct series data", () => {
const expectedData = [0, 1, 2].map((seriesIndex: any) => {
const parameters = getSeriesItemDataParameters(dataSet, seriesIndex);
const seriesItem = parameters[0];
const si = parameters[1];
const measureGroup = parameters[2];
const viewByAttribute = parameters[3];
const stackByAttribute = parameters[4];
return getSeriesItemData(
seriesItem,
si,
measureGroup,
viewByAttribute,
stackByAttribute,
type,
attributeColorStrategy,
);
});
expect(seriesData.map((seriesItem: any) => seriesItem.data)).toEqual(expectedData);
});
});
describe("in usecase of bar chart with stack by and view by attributes", () => {
const dataSet = fixtures.barChartWithStackByAndViewByAttributes;
const { measureGroup, viewByAttribute, stackByAttribute } = getMVS(dataSet);
const type = "column";
const attributeColorStrategy = new AttributeColorStrategy(
DEFAULT_COLOR_PALETTE,
undefined,
viewByAttribute,
stackByAttribute,
fixtures.barChartWithStackByAndViewByAttributes.executionResponse,
fixtures.barChartWithStackByAndViewByAttributes.executionRequest.afm,
);
const seriesData = getSeries(
dataSet.executionResult.data,
measureGroup,
viewByAttribute,
stackByAttribute,
type,
{} as any,
attributeColorStrategy,
);
it("should return number of series equal to the count of stack by attribute values", () => {
expect(seriesData.length).toBe(2);
});
it("should fill correct series name equal to stack by attribute values", () => {
expect(seriesData.map((seriesItem: any) => seriesItem.name)).toEqual([
"East Coast",
"West Coast",
]);
});
it("should fill correct series color", () => {
expect(seriesData.map((seriesItem: any) => seriesItem.color)).toEqual([
FIRST_DEFAULT_COLOR_ITEM_AS_STRING,
SECOND_DEFAULT_COLOR_ITEM_AS_STRING,
]);
});
it("should fill correct series legendIndex", () => {
expect(seriesData.map((seriesItem: any) => seriesItem.legendIndex)).toEqual([0, 1]);
});
it("should fill correct series data", () => {
const expectedData = [0, 1].map(seriesIndex => {
const parameters = getSeriesItemDataParameters(dataSet, seriesIndex);
const seriesItem = parameters[0];
const si = parameters[1];
const measureGroup = parameters[2];
const viewByAttribute = parameters[3];
const stackByAttribute = parameters[4];
const attributeColorStrategy = new AttributeColorStrategy(
DEFAULT_COLOR_PALETTE,
undefined,
measureGroup,
viewByAttribute,
stackByAttribute,
fixtures.barChartWith3MetricsAndViewByAttribute.executionRequest.afm,
);
return getSeriesItemData(
seriesItem,
si,
measureGroup,
viewByAttribute,
stackByAttribute,
type,
attributeColorStrategy,
);
});
expect(seriesData.map((seriesItem: any) => seriesItem.data)).toEqual(expectedData);
});
});
describe("in use case of bubble", () => {
const dummyBucketItem = {
visualizationAttribute: {
localIdentifier: "abc",
displayForm: { uri: "abc" },
},
};
const dummyMeasureGroup = {
items: [
{
measureHeaderItem: {
localIdentifier: "m1",
name: "dummyName",
format: "#.##x",
},
},
],
};
const dummyExecutionResponse: Execution.IExecutionResponse = {
dimensions: [
{
headers: [
{
measureGroupHeader: dummyMeasureGroup,
},
],
},
],
links: { executionResult: "foo" },
};
const stackByAttribute = {
items: [
{
attributeHeaderItem: {
name: "abc",
},
},
{
attributeHeaderItem: {
name: "def",
},
},
],
};
const colorPalette = [
{
guid: "3",
fill: {
r: 255,
g: 0,
b: 0,
},
},
{
guid: "2",
fill: {
r: 0,
g: 255,
b: 0,
},
},
];
it("should fill X, Y and Z with valid values when measure buckets are not empty", () => {
const executionResultData = [[1, 2, 3], [4, 5, 6]];
const mdObject = {
visualizationClass: { uri: "abc" },
buckets: [
{
localIdentifier: "measures",
items: [dummyBucketItem],
},
{
localIdentifier: "secondary_measures",
items: [dummyBucketItem],
},
{
localIdentifier: "tertiary_measures",
items: [dummyBucketItem],
},
],
};
const expectedSeries = [
{
name: "abc",
color: "rgb(255,0,0)",
legendIndex: 0,
data: [{ x: 1, y: 2, z: 3, format: "#.##x" }],
},
{
name: "def",
color: undefined,
legendIndex: 1,
data: [{ x: 4, y: 5, z: 6, format: "#.##x" }],
},
];
const colorStrategy = new BubbleChartColorStrategy(
colorPalette,
undefined,
null,
stackByAttribute,
dummyExecutionResponse,
{},
);
const series = getBubbleChartSeries(
executionResultData,
dummyMeasureGroup,
stackByAttribute,
mdObject,
colorStrategy,
);
expect(series).toEqual(expectedSeries);
});
it("should fill X and Y with zeroes when X and Y measure buckets are empty", () => {
const executionResultData = [[3], [6]];
const mdObject = {
visualizationClass: { uri: "abc" },
buckets: [
{
localIdentifier: "tertiary_measures",
items: [dummyBucketItem],
},
],
};
const expectedSeries = [
{
name: "",
color: "rgb(255,0,0)",
legendIndex: 0,
data: [{ x: 0, y: 0, z: 3, format: "#.##x" }],
},
{
name: "",
color: undefined,
legendIndex: 1,
data: [{ x: 0, y: 0, z: 6, format: "#.##x" }],
},
];
const colorStrategy = new BubbleChartColorStrategy(
colorPalette,
undefined,
null,
stackByAttribute,
dummyExecutionResponse,
{},
);
const series = getBubbleChartSeries(
executionResultData,
dummyMeasureGroup,
null,
mdObject,
colorStrategy,
);
expect(series).toEqual(expectedSeries);
});
it("should fill Y with x values when primary bucket is empty but secondary is not", () => {
const executionResultData = [[1, 3], [4, 6]];
const mdObject = {
visualizationClass: { uri: "abc" },
buckets: [
{
localIdentifier: "secondary_measures",
items: [dummyBucketItem],
},
{
localIdentifier: "tertiary_measures",
items: [dummyBucketItem],
},
],
};
const expectedSeries = [
{
name: "abc",
color: "rgb(255,0,0)",
legendIndex: 0,
data: [{ x: 0, y: 1, z: 3, format: "#.##x" }],
},
{
name: "def",
color: undefined,
legendIndex: 1,
data: [{ x: 0, y: 4, z: 6, format: "#.##x" }],
},
];
const colorStrategy = new BubbleChartColorStrategy(
colorPalette,
undefined,
stackByAttribute,
stackByAttribute,
dummyExecutionResponse,
{},
);
const series = getBubbleChartSeries(
executionResultData,
dummyMeasureGroup,
stackByAttribute,
mdObject,
colorStrategy,
);
expect(series).toEqual(expectedSeries);
});
it("should fill X with x and Z with z values when secondary bucket is empty", () => {
const executionResultData = [[1, 3], [4, 6]];
const mdObject = {
visualizationClass: { uri: "abc" },
buckets: [
{
localIdentifier: "measures",
items: [dummyBucketItem],
},
{
localIdentifier: "tertiary_measures",
items: [dummyBucketItem],
},
],
};
const expectedSeries = [
{
name: "abc",
color: "rgb(255,0,0)",
legendIndex: 0,
data: [{ x: 1, y: 0, z: 3, format: "#.##x" }],
},
{
name: "def",
color: undefined,
legendIndex: 1,
data: [{ x: 4, y: 0, z: 6, format: "#.##x" }],
},
];
const colorStrategy = new BubbleChartColorStrategy(
colorPalette,
undefined,
stackByAttribute,
stackByAttribute,
dummyExecutionResponse,
null,
);
const series = getBubbleChartSeries(
executionResultData,
dummyMeasureGroup,
stackByAttribute,
mdObject,
colorStrategy,
);
expect(series).toEqual(expectedSeries);
});
it("should fill Z with NaNs when tertiary bucket is empty", () => {
const executionResultData = [[1, 3], [4, 6]];
const mdObject = {
visualizationClass: { uri: "abc" },
buckets: [
{
localIdentifier: "measures",
items: [dummyBucketItem],
},
{
localIdentifier: "secondary_measures",
items: [dummyBucketItem],
},
],
};
const expectedSeries = [
{
name: "abc",
color: "rgb(255,0,0)",
legendIndex: 0,
data: [{ x: 1, y: 3, z: NaN, format: "#.##x" }],
},
{
name: "def",
color: undefined,
legendIndex: 1,
data: [{ x: 4, y: 6, z: NaN, format: "#.##x" }],
},
];
const colorStrategy = new BubbleChartColorStrategy(
colorPalette,
undefined,
null,
stackByAttribute,
dummyExecutionResponse,
null,
);
const series = getBubbleChartSeries(
executionResultData,
dummyMeasureGroup,
stackByAttribute,
mdObject,
colorStrategy,
);
expect(series).toEqual(expectedSeries);
});
it("should handle null in result", () => {
const executionResultData = [[null, 2, 3], [4, null, 6], [7, 8, null]];
const stackByAttributeWithThreeElements = {
items: [
{
attributeHeaderItem: {
name: "abc",
},
},
{
attributeHeaderItem: {
name: "def",
},
},
{
attributeHeaderItem: {
name: "ghi",
},
},
],
};
const mdObject = {
visualizationClass: { uri: "abc" },
buckets: [
{
localIdentifier: "measures",
items: [dummyBucketItem],
},
{
localIdentifier: "secondary_measures",
items: [dummyBucketItem],
},
{
localIdentifier: "tertiary_measures",
items: [dummyBucketItem],
},
],
};
const colorPaletteWithBlue = [
...colorPalette,
{
guid: "1",
fill: {
r: 0,
g: 0,
b: 255,
},
},
];
const expectedSeries = [
{
name: "abc",
color: "rgb(255,0,0)",
legendIndex: 0,
data: [] as any,
},
{
name: "def",
color: undefined,
legendIndex: 1,
data: [],
},
{
name: "ghi",
color: undefined,
legendIndex: 2,
data: [],
},
];
const colorStrategy = new BubbleChartColorStrategy(
colorPaletteWithBlue,
undefined,
null,
stackByAttributeWithThreeElements,
dummyExecutionResponse,
null,
);
const series = getBubbleChartSeries(
executionResultData,
dummyMeasureGroup,
stackByAttributeWithThreeElements,
mdObject,
colorStrategy,
);
expect(series).toEqual(expectedSeries);
});
});
describe("in use case of treemap", () => {
describe("with only one measure", () => {
const dataSet = fixtures.barChartWithSingleMeasureAndNoAttributes;
const { measureGroup, viewByAttribute, stackByAttribute } = getMVSTreemap(dataSet);
const type = "treemap";
const treeMapColorStrategy = new TreemapColorStrategy(
DEFAULT_COLOR_PALETTE,
undefined,
measureGroup,
viewByAttribute,
stackByAttribute,
fixtures.barChartWithSingleMeasureAndNoAttributes.executionRequest.afm,
);
const seriesData = getSeries(
dataSet.executionResult.data,
measureGroup,
viewByAttribute,
stackByAttribute,
type,
{} as any,
treeMapColorStrategy,
);
it("should return only one serie", () => {
expect(seriesData.length).toBe(1);
});
it("should fill correct series name equal to measure name", () => {
expect(seriesData[0].name).toEqual("Amount");
});
it("should fill correct series color", () => {
expect(seriesData[0].color).toEqual(FIRST_DEFAULT_COLOR_ITEM_AS_STRING);
});
it("should fill correct series legendIndex", () => {
expect(seriesData[0].legendIndex).toEqual(0);
});
it("should fill correct series data", () => {
expect(seriesData[0].data.length).toBe(1);
expect(seriesData[0].data[0]).toMatchObject({
value: 116625456.54,
color: FIRST_DEFAULT_COLOR_ITEM_AS_STRING,
format: "#,##0.00",
legendIndex: 0,
name: "Amount",
marker: expect.any(Object),
});
});
});
describe("with one measure and view by attribute", () => {
const dataSet = fixtures.treemapWithMetricAndViewByAttribute;
const { measureGroup, viewByAttribute, stackByAttribute } = getMVSTreemap(dataSet);
const type = "treemap";
const treeMapColorStrategy = new TreemapColorStrategy(
DEFAULT_COLOR_PALETTE,
undefined,
viewByAttribute,
stackByAttribute,
fixtures.treemapWithMetricAndViewByAttribute.executionResponse,
fixtures.treemapWithMetricAndViewByAttribute.executionRequest.afm,
);
const seriesData = getSeries(
dataSet.executionResult.data,
measureGroup,
viewByAttribute,
stackByAttribute,
type,
dataSet.mdObject,
treeMapColorStrategy,
);
it("should return only one serie", () => {
expect(seriesData.length).toBe(1);
});
it("should fill correct series name equal to measure name", () => {
expect(seriesData[0].name).toEqual("Amount");
});
it("should fill correct series legendIndex", () => {
expect(seriesData[0].legendIndex).toEqual(0);
});
it("should fill correct series data", () => {
expect(seriesData[