diff --git a/.eslintrc.js b/.eslintrc.js
index 1a61524f2d..5838f5f875 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -81,7 +81,8 @@ module.exports = {
'global-require': 1,
'import/no-dynamic-require': 1,
'no-shadow': 1,
- 'no-param-reassign': 1,
+ 'no-param-reassign': [1, { props: false }],
+ '@typescript-eslint/comma-spacing': 0,
'react/no-array-index-key': 1,
'react/prefer-stateless-function': 1,
'react/require-default-props': 'off',
@@ -342,7 +343,7 @@ module.exports = {
'prefer-destructuring': [
'warn',
{
- array: true,
+ array: false,
object: true,
},
{
diff --git a/.playground/playground.tsx b/.playground/playground.tsx
index 13036f2204..3c9f2ee240 100644
--- a/.playground/playground.tsx
+++ b/.playground/playground.tsx
@@ -38,39 +38,10 @@
import React from 'react';
-import { Chart, Settings, Partition, PartitionLayout } from '../src';
+import { Example } from '../stories/icicle/01_unix_icicle';
export class Playground extends React.Component {
render() {
- return (
-
-
-
- d.val as number}
- layers={[
- {
- groupByRollup: (d: any) => d.cat1,
- },
- {
- groupByRollup: (d: any) => d.cat2,
- },
- ]}
- config={{
- partitionLayout: PartitionLayout.sunburst,
- }}
- />
-
-
- );
+ return ;
}
}
diff --git a/api/charts.api.md b/api/charts.api.md
index 5c1859c6e8..dea40bc007 100644
--- a/api/charts.api.md
+++ b/api/charts.api.md
@@ -1258,6 +1258,8 @@ export interface PartitionLayer {
export const PartitionLayout: Readonly<{
sunburst: "sunburst";
treemap: "treemap";
+ icicle: "icicle";
+ flame: "flame";
}>;
// @public (undocumented)
@@ -1958,8 +1960,8 @@ export type YDomainRange = YDomainBase & DomainRange;
// src/chart_types/heatmap/layout/types/config_types.ts:28:13 - (ae-forgotten-export) The symbol "SizeRatio" needs to be exported by the entry point index.d.ts
// src/chart_types/heatmap/layout/types/config_types.ts:60:5 - (ae-forgotten-export) The symbol "TextAlign" needs to be exported by the entry point index.d.ts
// src/chart_types/heatmap/layout/types/config_types.ts:61:5 - (ae-forgotten-export) The symbol "TextBaseline" needs to be exported by the entry point index.d.ts
-// src/chart_types/partition_chart/layout/types/config_types.ts:126:5 - (ae-forgotten-export) The symbol "TimeMs" needs to be exported by the entry point index.d.ts
-// src/chart_types/partition_chart/layout/types/config_types.ts:127:5 - (ae-forgotten-export) The symbol "AnimKeyframe" needs to be exported by the entry point index.d.ts
+// src/chart_types/partition_chart/layout/types/config_types.ts:128:5 - (ae-forgotten-export) The symbol "TimeMs" needs to be exported by the entry point index.d.ts
+// src/chart_types/partition_chart/layout/types/config_types.ts:129:5 - (ae-forgotten-export) The symbol "AnimKeyframe" needs to be exported by the entry point index.d.ts
// src/chart_types/partition_chart/specs/index.ts:48:13 - (ae-forgotten-export) The symbol "NodeColorAccessor" needs to be exported by the entry point index.d.ts
// src/commons/series_id.ts:39:3 - (ae-forgotten-export) The symbol "SeriesKey" needs to be exported by the entry point index.d.ts
diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-flame-chart-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-flame-chart-visually-looks-correct-1-snap.png
new file mode 100644
index 0000000000..b4a00fc2e4
Binary files /dev/null and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-flame-chart-visually-looks-correct-1-snap.png differ
diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-icicle-chart-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-icicle-chart-visually-looks-correct-1-snap.png
new file mode 100644
index 0000000000..f23daebae8
Binary files /dev/null and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-icicle-chart-visually-looks-correct-1-snap.png differ
diff --git a/src/chart_types/partition_chart/layout/types/config_types.ts b/src/chart_types/partition_chart/layout/types/config_types.ts
index 1307bbb76d..8bcec0dabd 100644
--- a/src/chart_types/partition_chart/layout/types/config_types.ts
+++ b/src/chart_types/partition_chart/layout/types/config_types.ts
@@ -27,6 +27,8 @@ import { Font, FontFamily, PartialFont } from './types';
export const PartitionLayout = Object.freeze({
sunburst: 'sunburst' as const,
treemap: 'treemap' as const,
+ icicle: 'icicle' as const,
+ flame: 'flame' as const,
});
/** @public */
diff --git a/src/chart_types/partition_chart/layout/utils/group_by_rollup.ts b/src/chart_types/partition_chart/layout/utils/group_by_rollup.ts
index 76cbb0de28..f39943ac4c 100644
--- a/src/chart_types/partition_chart/layout/utils/group_by_rollup.ts
+++ b/src/chart_types/partition_chart/layout/utils/group_by_rollup.ts
@@ -57,7 +57,9 @@ interface MapNode extends NodeDescriptor {
export type PrimitiveValue = string | number | null; // there could be more but sufficient for now
type Key = PrimitiveValue;
-type Sorter = (a: number, b: number) => number;
+
+export type Sorter = (a: number, b: number) => number;
+
type NodeSorter = (a: ArrayEntry, b: ArrayEntry) => number;
export const entryKey = ([key]: ArrayEntry) => key;
@@ -99,7 +101,7 @@ export function groupByRollup(
const statistics: Statistics = {
globalAggregate: NaN,
};
- const reductionMap = factTable.reduce((p: HierarchyOfMaps, n, index) => {
+ const reductionMap: HierarchyOfMaps = factTable.reduce((p: HierarchyOfMaps, n, index) => {
const keyCount = keyAccessors.length;
let pointer: HierarchyOfMaps = p;
keyAccessors.forEach((keyAccessor, i) => {
@@ -132,22 +134,22 @@ export function groupByRollup(
function getRootArrayNode(): ArrayNode {
const children: HierarchyOfArrays = [];
- const bootstrap = {
+ const bootstrap: Omit = {
[AGGREGATE_KEY]: NaN,
[DEPTH_KEY]: NaN,
[CHILDREN_KEY]: children,
[INPUT_KEY]: [] as number[],
[PATH_KEY]: [] as number[],
+ [SORT_INDEX_KEY]: 0,
+ [STATISTICS_KEY]: { globalAggregate: 0 },
};
- Object.assign(bootstrap, { [PARENT_KEY]: bootstrap });
- const result: ArrayNode = bootstrap as ArrayNode;
- return result;
+ return { ...bootstrap, [PARENT_KEY]: bootstrap } as ArrayNode; // TS doesn't yet handle bootstrapping but the `Omit` above retains guarantee for all props except `[PARENT_KEY`
}
/** @internal */
-export function mapsToArrays(root: HierarchyOfMaps, sorter: NodeSorter): HierarchyOfArrays {
- const groupByMap = (node: HierarchyOfMaps, parent: ArrayNode) =>
- Array.from(
+export function mapsToArrays(root: HierarchyOfMaps, sorter: NodeSorter | null): HierarchyOfArrays {
+ const groupByMap = (node: HierarchyOfMaps, parent: ArrayNode) => {
+ const items = Array.from(
node,
([key, value]: [Key, MapNode]): ArrayEntry => {
const valueElement = value[CHILDREN_KEY];
@@ -168,12 +170,15 @@ export function mapsToArrays(root: HierarchyOfMaps, sorter: NodeSorter): Hierarc
);
return [key, newValue];
},
- )
- .sort(sorter)
- .map((n: ArrayEntry, i) => {
- entryValue(n).sortIndex = i;
- return n;
- }); // with the current algo, decreasing order is important
+ );
+ if (sorter !== null) {
+ items.sort(sorter);
+ }
+ return items.map((n: ArrayEntry, i) => {
+ entryValue(n).sortIndex = i;
+ return n;
+ });
+ }; // with the current algo, decreasing order is important
const tree = groupByMap(root, getRootArrayNode());
const buildPaths = ([, mapNode]: ArrayEntry, currentPath: number[]) => {
const newPath = [...currentPath, mapNode[SORT_INDEX_KEY]];
diff --git a/src/chart_types/partition_chart/layout/utils/sunburst.ts b/src/chart_types/partition_chart/layout/utils/sunburst.ts
index fc4415410c..911f8c4e0c 100644
--- a/src/chart_types/partition_chart/layout/utils/sunburst.ts
+++ b/src/chart_types/partition_chart/layout/utils/sunburst.ts
@@ -22,11 +22,12 @@ import { ArrayEntry, childrenAccessor, HierarchyOfArrays } from './group_by_roll
/** @internal */
export function sunburst(
- nodes: HierarchyOfArrays,
+ outerNodes: HierarchyOfArrays,
areaAccessor: (e: ArrayEntry) => number,
- { x0, y0 }: Origin,
+ { x0: outerX0, y0: outerY0 }: Origin,
clockwiseSectors: boolean,
specialFirstInnermostSector: boolean,
+ heightStep: number = 1,
): Array {
const result: Array = [];
const laySubtree = (nodes: HierarchyOfArrays, { x0, y0 }: Origin, depth: number) => {
@@ -36,14 +37,14 @@ export function sunburst(
const index = clockwiseSectors ? i : nodeCount - i - 1;
const node = nodes[depth === 1 && specialFirstInnermostSector ? (index + 1) % nodeCount : index];
const area = areaAccessor(node);
- result.push({ node, x0: currentOffsetX, y0, x1: currentOffsetX + area, y1: y0 + 1 });
+ result.push({ node, x0: currentOffsetX, y0, x1: currentOffsetX + area, y1: y0 + heightStep });
const children = childrenAccessor(node);
- if (children && children.length) {
- laySubtree(children, { x0: currentOffsetX, y0: y0 + 1 }, depth + 1);
+ if (children.length > 0) {
+ laySubtree(children, { x0: currentOffsetX, y0: y0 + heightStep }, depth + 1);
}
currentOffsetX += area;
}
};
- laySubtree(nodes, { x0, y0 }, 0);
+ laySubtree(outerNodes, { x0: outerX0, y0: outerY0 }, 0);
return result;
}
diff --git a/src/chart_types/partition_chart/layout/utils/treemap.ts b/src/chart_types/partition_chart/layout/utils/treemap.ts
index 2b5dbb2969..7bfc456c3b 100644
--- a/src/chart_types/partition_chart/layout/utils/treemap.ts
+++ b/src/chart_types/partition_chart/layout/utils/treemap.ts
@@ -101,20 +101,25 @@ export function treemap(
areaAccessor: (e: ArrayEntry) => number,
topPaddingAccessor: (e: ArrayEntry) => number,
paddingAccessor: (e: ArrayEntry) => number,
- { x0, y0, width, height }: { x0: number; y0: number; width: number; height: number },
+ {
+ x0: outerX0,
+ y0: outerY0,
+ width: outerWidth,
+ height: outerHeight,
+ }: { x0: number; y0: number; width: number; height: number },
): Array {
if (nodes.length === 0) return [];
// some bias toward horizontal rectangles with a golden ratio of width to height
- const vertical = width / GOLDEN_RATIO <= height;
- const independentSize = vertical ? width : height;
+ const vertical = outerWidth / GOLDEN_RATIO <= outerHeight;
+ const independentSize = vertical ? outerWidth : outerHeight;
const vectorElements = bestVector(nodes, independentSize, areaAccessor);
- const vector = vectorNodeCoordinates(vectorElements, x0, y0, vertical);
+ const vector = vectorNodeCoordinates(vectorElements, outerX0, outerY0, vertical);
const { dependentSize } = vectorElements;
return vector
.concat(
...vector.map(({ node, x0, y0, x1, y1 }) => {
const childrenNodes = entryValue(node)[CHILDREN_KEY];
- if (!childrenNodes || !childrenNodes.length) {
+ if (childrenNodes.length === 0) {
return [];
}
const fullWidth = x1 - x0;
@@ -148,8 +153,8 @@ export function treemap(
topPaddingAccessor,
paddingAccessor,
vertical
- ? { x0, y0: y0 + dependentSize, width, height: height - dependentSize }
- : { x0: x0 + dependentSize, y0, width: width - dependentSize, height },
+ ? { x0: outerX0, y0: outerY0 + dependentSize, width: outerWidth, height: outerHeight - dependentSize }
+ : { x0: outerX0 + dependentSize, y0: outerY0, width: outerWidth - dependentSize, height: outerHeight },
),
);
}
diff --git a/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.ts b/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.ts
index 919dbaa61b..5514e931c0 100644
--- a/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.ts
+++ b/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.ts
@@ -28,12 +28,14 @@ import {
groupByRollup,
mapEntryValue,
mapsToArrays,
+ Sorter,
} from '../utils/group_by_rollup';
export function getHierarchyOfArrays(
rawFacts: Relation,
valueAccessor: ValueAccessor,
groupByRollupAccessors: IndexedAccessorFn[],
+ sorter: Sorter | null = childOrders.descending,
): HierarchyOfArrays {
const aggregator = aggregators.sum;
@@ -52,6 +54,6 @@ export function getHierarchyOfArrays(
// size as data value vs size as number of pixels in the rectangle
return mapsToArrays(
groupByRollup(groupByRollupAccessors, valueAccessor, aggregator, facts),
- aggregateComparator(mapEntryValue, childOrders.descending),
+ sorter && aggregateComparator(mapEntryValue, sorter),
);
}
diff --git a/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts b/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts
index d4a33c8f82..714f67c804 100644
--- a/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts
+++ b/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts
@@ -103,7 +103,7 @@ export function makeQuadViewModel(
const opacityMultiplier = 1; // could alter in the future, eg. in response to interactions
const layer = layers[node.depth - 1];
const fillColorSpec = layer && layer.shape && layer.shape.fillColor;
- const fill = fillColorSpec || 'rgba(128,0,0,0.5)';
+ const fill = fillColorSpec ?? 'rgba(128,0,0,0.5)';
const shapeFillColor = typeof fill === 'function' ? fill(node, node.sortIndex, node.parent.children) : fill;
const { r, g, b, opacity } = stringToRGB(shapeFillColor);
const fillColor = argsToRGBString(r, g, b, opacity * opacityMultiplier);
@@ -149,9 +149,9 @@ export interface RectangleConstruction {
y1: Pixels;
}
-function rectangleConstruction(treeHeight: number, topGroove: number) {
- return function (node: ShapeTreeNode): RectangleConstruction {
- return node.depth < treeHeight
+function rectangleConstruction(treeHeight: number, topGroove: number | null) {
+ return function rectangleConstructionClosure(node: ShapeTreeNode): RectangleConstruction {
+ return node.depth < treeHeight && topGroove !== null
? {
x0: node.x0,
y0: node.y0px,
@@ -167,6 +167,66 @@ function rectangleConstruction(treeHeight: number, topGroove: number) {
};
}
+const rawChildNodes = (
+ partitionLayout: PartitionLayout,
+ tree: HierarchyOfArrays,
+ topGroove: number,
+ width: number,
+ height: number,
+ clockwiseSectors: boolean,
+ specialFirstInnermostSector: boolean,
+ maxDepth: number,
+): Array => {
+ const totalValue = tree.reduce((p: number, n: ArrayEntry): number => p + mapEntryValue(n), 0);
+ switch (partitionLayout) {
+ case PartitionLayout.sunburst:
+ const sunburstValueToAreaScale = TAU / totalValue;
+ const sunburstAreaAccessor = (e: ArrayEntry) => sunburstValueToAreaScale * mapEntryValue(e);
+ return sunburst(tree, sunburstAreaAccessor, { x0: 0, y0: -1 }, clockwiseSectors, specialFirstInnermostSector);
+
+ case PartitionLayout.treemap:
+ const treemapInnerArea = isTreemap(partitionLayout) ? width * height : 1; // assuming 1 x 1 unit square
+ const treemapValueToAreaScale = treemapInnerArea / totalValue;
+ const treemapAreaAccessor = (e: ArrayEntry) => treemapValueToAreaScale * mapEntryValue(e);
+ return treemap(tree, treemapAreaAccessor, topGrooveAccessor(topGroove), grooveAccessor, {
+ x0: -width / 2,
+ y0: -height / 2,
+ width,
+ height,
+ });
+
+ case PartitionLayout.icicle:
+ case PartitionLayout.flame:
+ const icicleLayout = isIcicle(partitionLayout);
+ const multiplier = icicleLayout ? -1 : 1;
+ const icicleValueToAreaScale = width / totalValue;
+ const icicleAreaAccessor = (e: ArrayEntry) => icicleValueToAreaScale * mapEntryValue(e);
+ const icicleRowHeight = height / maxDepth;
+ const result = sunburst(
+ tree,
+ icicleAreaAccessor,
+ { x0: -width / 2, y0: (multiplier * height) / 2 - icicleRowHeight },
+ true,
+ false,
+ icicleRowHeight,
+ );
+ return icicleLayout
+ ? result
+ : result.map(({ y0, y1, ...rest }) => ({ y0: height - y1, y1: height - y0, ...rest }));
+
+ default:
+ // Let's ensure TS complains if we add a new PartitionLayout type in the future without creating a `case` for it
+ // Hopefully, a future TS version will do away with the need for this boilerplate `default`. Now TS even needs a `default` even if all possible cases are covered.
+ // Even in runtime it does something sensible (returns the empty set); explicit throwing is avoided as it can deopt the function
+ return ((layout: never) => layout ?? [])(partitionLayout);
+ }
+};
+
+export const isTreemap = (partitionLayout: PartitionLayout) => partitionLayout === PartitionLayout.treemap;
+export const isSunburst = (partitionLayout: PartitionLayout) => partitionLayout === PartitionLayout.sunburst;
+const isIcicle = (partitionLayout: PartitionLayout) => partitionLayout === PartitionLayout.icicle;
+const isFlame = (partitionLayout: PartitionLayout) => partitionLayout === PartitionLayout.flame;
+
/** @internal */
export function shapeViewModel(
textMeasure: TextMeasure,
@@ -207,25 +267,25 @@ export function shapeViewModel(
return nullShapeViewModel(config, diskCenter);
}
- const totalValue = tree.reduce((p: number, n: ArrayEntry): number => p + mapEntryValue(n), 0);
-
- const sunburstValueToAreaScale = TAU / totalValue;
- const sunburstAreaAccessor = (e: ArrayEntry) => sunburstValueToAreaScale * mapEntryValue(e);
- const treemapLayout = partitionLayout === PartitionLayout.treemap;
- const treemapInnerArea = treemapLayout ? width * height : 1; // assuming 1 x 1 unit square
- const treemapValueToAreaScale = treemapInnerArea / totalValue;
- const treemapAreaAccessor = (e: ArrayEntry) => treemapValueToAreaScale * mapEntryValue(e);
-
- const rawChildNodes: Array = treemapLayout
- ? treemap(tree, treemapAreaAccessor, topGrooveAccessor(topGroove), grooveAccessor, {
- x0: -width / 2,
- y0: -height / 2,
- width,
- height,
- })
- : sunburst(tree, sunburstAreaAccessor, { x0: 0, y0: -1 }, clockwiseSectors, specialFirstInnermostSector);
+ const treemapLayout = isTreemap(partitionLayout);
+ const sunburstLayout = isSunburst(partitionLayout);
+ const icicleLayout = isIcicle(partitionLayout);
+ const flameLayout = isFlame(partitionLayout);
+ const longestPath = ([, { children, path }]: ArrayEntry): number =>
+ children.length > 0 ? children.reduce((p, n) => Math.max(p, longestPath(n)), 0) : path.length;
+ const maxDepth = longestPath(tree[0]) - 2; // don't include the root node
+ const childNodes = rawChildNodes(
+ partitionLayout,
+ tree,
+ topGroove,
+ width,
+ height,
+ clockwiseSectors,
+ specialFirstInnermostSector,
+ maxDepth,
+ );
- const shownChildNodes = rawChildNodes.filter((n: Part) => {
+ const shownChildNodes = childNodes.filter((n: Part) => {
const layerIndex = entryValue(n.node).depth - 1;
const layer = layers[layerIndex];
return !layer || !layer.showAccessor || layer.showAccessor(entryKey(n.node));
@@ -237,7 +297,7 @@ export function shapeViewModel(
const innerRadius: Radius = outerRadius - (1 - emptySizeRatio) * outerRadius;
const treeHeight = shownChildNodes.reduce((p: number, n: Part) => Math.max(p, entryValue(n.node).depth), 0); // 1: pie, 2: two-ring donut etc.
const ringThickness = (outerRadius - innerRadius) / treeHeight;
- const partToShapeFn = partToShapeTreeNode(treemapLayout, innerRadius, ringThickness);
+ const partToShapeFn = partToShapeTreeNode(!sunburstLayout, innerRadius, ringThickness);
const quadViewModel = makeQuadViewModel(
shownChildNodes.slice(1).map(partToShapeFn),
layers,
@@ -248,32 +308,31 @@ export function shapeViewModel(
// fill text
const roomCondition = (n: ShapeTreeNode) => {
const diff = n.x1 - n.x0;
- return treemapLayout
- ? n.x1 - n.x0 > minFontSize && n.y1px - n.y0px > minFontSize
- : (diff < 0 ? TAU + diff : diff) * ringSectorMiddleRadius(n) > Math.max(minFontSize, linkLabel.maximumSection);
+ return sunburstLayout
+ ? (diff < 0 ? TAU + diff : diff) * ringSectorMiddleRadius(n) > Math.max(minFontSize, linkLabel.maximumSection)
+ : n.x1 - n.x0 > minFontSize && n.y1px - n.y0px > minFontSize;
};
const nodesWithRoom = quadViewModel.filter(roomCondition);
- const outsideFillNodes = fillOutside && !treemapLayout ? nodesWithRoom : [];
+ const outsideFillNodes = fillOutside && sunburstLayout ? nodesWithRoom : [];
- const textFillOrigins = nodesWithRoom.map(treemapLayout ? rectangleFillOrigins : sectorFillOrigins(fillOutside));
+ const textFillOrigins = nodesWithRoom.map(sunburstLayout ? sectorFillOrigins(fillOutside) : rectangleFillOrigins);
const valueFormatter = valueGetter === percentValueGetter ? specifiedPercentFormatter : specifiedValueFormatter;
- const getRowSets = treemapLayout
+ const getRowSets = sunburstLayout
? fillTextLayout(
- rectangleConstruction(treeHeight, topGroove),
- getRectangleRowGeometry,
- () => 0,
- containerBackgroundColor,
- )
- : fillTextLayout(
ringSectorConstruction(config, innerRadius, ringThickness),
getSectorRowGeometry,
inSectorRotation(config.horizontalTextEnforcer, config.horizontalTextAngleThreshold),
containerBackgroundColor,
+ )
+ : fillTextLayout(
+ rectangleConstruction(treeHeight, treemapLayout ? topGroove : null),
+ getRectangleRowGeometry,
+ () => 0,
+ containerBackgroundColor,
);
-
const rowSets: RowSet[] = getRowSets(
textMeasure,
rawTextGetter,
@@ -283,7 +342,7 @@ export function shapeViewModel(
config,
layers,
textFillOrigins,
- treemapLayout,
+ !sunburstLayout,
!treemapLayout,
);
@@ -294,13 +353,13 @@ export function shapeViewModel(
const currentY = [-height, -height, -height, -height];
const nodesWithoutRoom =
- fillOutside || treemapLayout
+ fillOutside || treemapLayout || icicleLayout || flameLayout
? [] // outsideFillNodes and linkLabels are in inherent conflict due to very likely overlaps
: quadViewModel.filter((n: ShapeTreeNode) => {
const id = nodeId(n);
const foundInFillText = rowSets.find((r: RowSet) => r.id === id);
// successful text render if found, and has some row(s)
- return !(foundInFillText && foundInFillText.rows.length !== 0);
+ return !(foundInFillText && foundInFillText.rows.length > 0);
});
const maxLinkedLabelTextLength = config.linkLabel.maxTextLength;
const linkLabelViewModels = linkTextLayout(
@@ -321,7 +380,7 @@ export function shapeViewModel(
const pickQuads: PickFunction = (x, y) =>
quadViewModel.filter(
- treemapLayout
+ treemapLayout || icicleLayout || flameLayout
? ({ x0, y0, x1, y1 }) => x0 <= x && x <= x1 && y0 <= y && y <= y1
: ({ x0, y0px, x1, y1px }) => {
const angleX = (Math.atan2(y, x) + TAU / 4 + TAU) % TAU;
diff --git a/src/chart_types/partition_chart/renderer/canvas/canvas_renderers.ts b/src/chart_types/partition_chart/renderer/canvas/canvas_renderers.ts
index 6834206f51..119f4a59b7 100644
--- a/src/chart_types/partition_chart/renderer/canvas/canvas_renderers.ts
+++ b/src/chart_types/partition_chart/renderer/canvas/canvas_renderers.ts
@@ -19,7 +19,6 @@
import { clearCanvas, renderLayers, withContext } from '../../../../renderers/canvas';
import { Color } from '../../../../utils/commons';
-import { PartitionLayout } from '../../layout/types/config_types';
import { Pixels } from '../../layout/types/geometry_types';
import {
LinkLabelVM,
@@ -33,6 +32,7 @@ import { addOpacity } from '../../layout/utils/calcs';
import { TAU } from '../../layout/utils/constants';
import { cssFontShorthand } from '../../layout/utils/measure';
import { LinkLabelsViewModelSpec } from '../../layout/viewmodel/link_text_layout';
+import { isSunburst } from '../../layout/viewmodel/viewmodel';
// the burnout avoidance in the center of the pie
const LINE_WIDTH_MULT = 10; // border can be a maximum 1/LINE_WIDTH_MULT - th of the sector angle, otherwise the border would dominate
@@ -266,9 +266,7 @@ export function renderPartitionCanvas2d(
// bottom layer: sectors (pie slices, ring sectors etc.)
(ctx: CanvasRenderingContext2D) =>
- config.partitionLayout === PartitionLayout.treemap
- ? renderRectangles(ctx, quadViewModel)
- : renderSectors(ctx, quadViewModel),
+ isSunburst(config.partitionLayout) ? renderSectors(ctx, quadViewModel) : renderRectangles(ctx, quadViewModel),
// all the fill-based, potentially multirow text, whether inside or outside the sector
(ctx: CanvasRenderingContext2D) => renderRowSets(ctx, rowSets, linkLineColor),
diff --git a/src/chart_types/partition_chart/renderer/dom/highlighter.tsx b/src/chart_types/partition_chart/renderer/dom/highlighter.tsx
index c94f4a273c..729f5e2845 100644
--- a/src/chart_types/partition_chart/renderer/dom/highlighter.tsx
+++ b/src/chart_types/partition_chart/renderer/dom/highlighter.tsx
@@ -20,10 +20,12 @@
import React from 'react';
import { Dimensions } from '../../../../utils/dimensions';
+import { configMetadata } from '../../layout/config/config';
import { PartitionLayout } from '../../layout/types/config_types';
import { PointObject } from '../../layout/types/geometry_types';
import { QuadViewModel } from '../../layout/types/viewmodel_types';
import { TAU } from '../../layout/utils/constants';
+import { isSunburst, isTreemap } from '../../layout/viewmodel/viewmodel';
/** @internal */
export interface HighlighterProps {
@@ -97,26 +99,12 @@ function renderSector(geometry: QuadViewModel, key: string, style: SVGStyle) {
return ;
}
-function renderGeometries(geometries: QuadViewModel[], partitionLayout: PartitionLayout, style: SVGStyle) {
- let maxDepth = -1;
+function renderGeometries(geoms: QuadViewModel[], partitionLayout: PartitionLayout, style: SVGStyle) {
+ const maxDepth = geoms.reduce((acc, geom) => Math.max(acc, geom.depth), 0);
// we should render only the deepest geometries of the tree to avoid overlaying highlighted geometries
- if (partitionLayout === PartitionLayout.treemap) {
- maxDepth = geometries.reduce((acc, geom) => Math.max(acc, geom.depth), 0);
- }
- return geometries
- .filter((geometry) => {
- if (maxDepth !== -1) {
- return geometry.depth >= maxDepth;
- }
- return true;
- })
- .map((geometry, index) => {
- if (partitionLayout === PartitionLayout.sunburst) {
- return renderSector(geometry, `${index}`, style);
- }
-
- return renderRectangles(geometry, `${index}`, style);
- });
+ const highlightedGeoms = isTreemap(partitionLayout) ? geoms.filter((g) => g.depth >= maxDepth) : geoms;
+ const renderGeom = isSunburst(partitionLayout) ? renderSector : renderRectangles;
+ return highlightedGeoms.map((geometry, index) => renderGeom(geometry, `${index}`, style));
}
/** @internal */
@@ -143,7 +131,7 @@ export class HighlighterComponent extends React.Component {
- {partitionLayout === PartitionLayout.sunburst && (
+ {isSunburst(partitionLayout) ? (
{
mask={`url(#${maskId})`}
className="echHighlighter__mask"
/>
- )}
- {partitionLayout === PartitionLayout.treemap && (
+ ) : (
)}
>
@@ -201,5 +188,5 @@ export const DEFAULT_PROPS: HighlighterProps = {
},
outerRadius: 10,
renderAsOverlay: false,
- partitionLayout: PartitionLayout.sunburst,
+ partitionLayout: configMetadata.partitionLayout.dflt,
};
diff --git a/src/chart_types/partition_chart/state/selectors/tree.ts b/src/chart_types/partition_chart/state/selectors/tree.ts
index e8c0f76d02..8d744be838 100644
--- a/src/chart_types/partition_chart/state/selectors/tree.ts
+++ b/src/chart_types/partition_chart/state/selectors/tree.ts
@@ -20,11 +20,13 @@
import createCachedSelector from 're-reselect';
import { ChartTypes } from '../../..';
-import { SpecTypes } from '../../../../specs/constants';
+import { SpecTypes } from '../../../../specs';
import { GlobalChartState } from '../../../../state/chart_state';
import { getSpecsFromStore } from '../../../../state/utils';
-import { HierarchyOfArrays } from '../../layout/utils/group_by_rollup';
+import { configMetadata } from '../../layout/config/config';
+import { childOrders, HierarchyOfArrays } from '../../layout/utils/group_by_rollup';
import { getHierarchyOfArrays } from '../../layout/viewmodel/hierarchy_of_arrays';
+import { isSunburst, isTreemap } from '../../layout/viewmodel/viewmodel';
import { PartitionSpec } from '../../specs';
const getSpecs = (state: GlobalChartState) => state.specs;
@@ -38,6 +40,13 @@ export const getTree = createCachedSelector(
return [];
}
const { data, valueAccessor, layers } = pieSpecs[0];
- return getHierarchyOfArrays(data, valueAccessor, [() => null, ...layers.map(({ groupByRollup }) => groupByRollup)]);
+ const layout = pieSpecs[0].config.partitionLayout ?? configMetadata.partitionLayout.dflt;
+ const sorter = isTreemap(layout) || isSunburst(layout) ? childOrders.descending : null;
+ return getHierarchyOfArrays(
+ data,
+ valueAccessor,
+ [() => null, ...layers.map(({ groupByRollup }) => groupByRollup)],
+ sorter,
+ );
},
)((state) => state.chartId);
diff --git a/src/mocks/hierarchical/index.ts b/src/mocks/hierarchical/index.ts
index dbd0d18fe7..ae729fd636 100644
--- a/src/mocks/hierarchical/index.ts
+++ b/src/mocks/hierarchical/index.ts
@@ -19,6 +19,7 @@
import { manyPieMock } from './many_pie';
import { miniSunburstMock } from './mini_sunburst';
+import { observabilityTreeMock } from './observability_tree';
import { pieMock } from './pie';
import { sunburstMock } from './sunburst';
@@ -27,4 +28,5 @@ export const mocks = {
sunburst: sunburstMock,
miniSunburst: miniSunburstMock,
manyPie: manyPieMock,
+ observabilityTree: observabilityTreeMock,
};
diff --git a/src/mocks/hierarchical/observability_tree.ts b/src/mocks/hierarchical/observability_tree.ts
new file mode 100644
index 0000000000..6498c78329
--- /dev/null
+++ b/src/mocks/hierarchical/observability_tree.ts
@@ -0,0 +1,23 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// source of data: Martin Spier's https://github.com/spiermar/d3-flame-graph
+
+// prettier-ignore
+export const observabilityTreeMock = {c:[{n:'genunix`syscall_mstate',v:89},{c:[{c:[{c:[{c:[{c:[{c:[{c:[{c:[{n:'unix`page_lookup_create',v:1}],n:'unix`page_lookup',v:1}],n:'ufs`ufs_getpage',v:1}],n:'genunix`fop_getpage',v:1},{c:[{c:[{c:[{c:[{c:[{n:'genunix`pvn_plist_init',v:1},{n:'unix`lgrp_mem_choose',v:1},{c:[{c:[{c:[{n:'unix`mutex_enter',v:1}],n:'unix`page_get_mnode_freelist',v:1}],n:'unix`page_get_freelist',v:1}],n:'unix`page_create_va',v:1},{c:[{n:'unix`page_lookup_create',v:1}],n:'unix`page_lookup',v:1}],n:'genunix`swap_getapage',v:4}],n:'genunix`swap_getpage',v:4}],n:'genunix`fop_getpage',v:4},{c:[{c:[{n:'unix`hwblkclr',v:3}],n:'unix`pfnzero',v:3}],n:'unix`pagezero',v:3}],n:'genunix`anon_zero',v:7}],n:'genunix`segvn_faultpage',v:7},{n:'ufs`ufs_getpage',v:1},{c:[{c:[{c:[{c:[{c:[{c:[{c:[{c:[{n:'unix`hment_compare',v:1}],n:'genunix`avl_find',v:1}],n:'genunix`avl_add',v:1}],n:'unix`hment_insert',v:2}],n:'unix`hment_assign',v:2}],n:'unix`hati_pte_map',v:2}],n:'unix`hati_load_common',v:2}],n:'unix`hat_memload',v:2}],n:'unix`hat_memload_region',v:2}],n:'genunix`segvn_fault',v:11}],n:'genunix`as_fault',v:12},{n:'genunix`segvn_fault',v:1}],n:'unix`pagefault',v:13}],n:'unix`trap',v:13}],n:'unix`0xfffffffffb8001d6',v:13},{n:'unix`0xfffffffffb800c7c',v:42},{n:'unix`0xfffffffffb800c81',v:2},{c:[{n:'genunix`gethrtime_unscaled',v:4},{c:[{c:[{n:'unix`tsc_gethrtimeunscaled',v:11},{n:'unix`tsc_read',v:186}],n:'genunix`gethrtime_unscaled',v:203},{n:'unix`tsc_gethrtimeunscaled',v:13}],n:'genunix`syscall_mstate',v:355},{n:'unix`atomic_add_64',v:110}],n:'unix`0xfffffffffb800c86',v:472},{c:[{n:'genunix`audit_getstate',v:27},{n:'genunix`clear_stale_fd',v:10},{n:'genunix`disp_lock_exit',v:27},{c:[{n:'FSS`fss_preempt',v:1},{n:'genunix`audit_getstate',v:15},{n:'genunix`clear_stale_fd',v:44},{c:[{n:'unix`clear_int_flag',v:39},{n:'unix`do_splx',v:1993},{c:[{c:[{c:[{n:'unix`do_splx',v:1}],n:'genunix`disp_lock_exit_nopreempt',v:1}],n:'unix`preempt',v:1}],n:'unix`kpreempt',v:1}],n:'genunix`disp_lock_exit',v:2096},{n:'genunix`sigcheck',v:1},{c:[{n:'unix`clear_int_flag',v:180},{n:'unix`splr',v:400}],n:'genunix`thread_lock',v:670},{n:'unix`do_splx',v:31},{n:'unix`i_ddi_splhigh',v:23},{n:'unix`lock_clear_splx',v:28},{n:'unix`lock_try',v:778},{n:'unix`lwp_getdatamodel',v:6},{c:[{c:[{c:[{c:[{c:[{n:'unix`tsc_gethrtimeunscaled',v:1}],n:'genunix`mstate_thread_onproc_time',v:1}],n:'unix`caps_charge_adjust',v:1}],n:'unix`cpucaps_charge',v:3},{c:[{n:'unix`cmt_balance',v:1},{c:[{n:'unix`bitset_in_set',v:1}],n:'unix`cpu_wakeup_mwait',v:1}],n:'unix`setbackdq',v:5}],n:'FSS`fss_preempt',v:8},{n:'unix`do_splx',v:1},{c:[{n:'genunix`disp_lock_exit_high',v:1},{c:[{n:'unix`membar_enter',v:1}],n:'unix`disp',v:1},{n:'unix`do_splx',v:1},{c:[{c:[{n:'genunix`schedctl_save',v:1}],n:'genunix`savectx',v:2}],n:'unix`resume',v:2}],n:'unix`swtch',v:5}],n:'unix`preempt',v:14},{n:'unix`prunstop',v:36},{n:'unix`splr',v:92},{n:'unix`splx',v:6}],n:'genunix`post_syscall',v:4245},{n:'genunix`thread_lock',v:33},{n:'unix`lwp_getdatamodel',v:3},{n:'unix`prunstop',v:2}],n:'unix`0xfffffffffb800c91',v:4361},{c:[{n:'genunix`gethrtime_unscaled',v:7},{c:[{c:[{n:'unix`tsc_gethrtimeunscaled',v:17},{n:'unix`tsc_read',v:160}],n:'genunix`gethrtime_unscaled',v:182},{n:'unix`tsc_gethrtimeunscaled',v:12}],n:'genunix`syscall_mstate',v:412},{n:'unix`atomic_add_64',v:95}],n:'unix`0xfffffffffb800ca0',v:517},{n:'unix`_sys_rtt',v:6},{c:[{c:[{c:[{c:[{c:[{c:[{n:'genunix`cpu_decay',v:1}],n:'genunix`cpu_grow',v:1}],n:'genunix`cpu_update_pct',v:1}],n:'genunix`new_mstate',v:1}],n:'unix`trap',v:1}],n:'unix`sys_rtt_common',v:1}],n:'unix`_sys_rtt_ints_disabled',v:1},{c:[{c:[{c:[{c:[{c:[{c:[{c:[{n:'doorfs`door_close',v:1}],n:'namefs`nm_close',v:1}],n:'genunix`fop_close',v:1}],n:'genunix`closef',v:1}],n:'genunix`close_exec',v:1}],n:'genunix`exec_common',v:1}],n:'genunix`exece',v:1}],n:'unix`_sys_sysenter_post_swapgs',v:1},{c:[{n:'genunix`gethrtime_unscaled',v:11},{c:[{c:[{c:[{c:[{c:[{c:[{c:[{c:[{c:[{c:[{n:'unix`mtype_func',v:1},{n:'unix`mutex_enter',v:1}],n:'unix`page_get_mnode_freelist',v:2}],n:'unix`page_get_freelist',v:2}],n:'unix`page_create_va',v:3}],n:'genunix`pvn_read_kluster',v:3}],n:'ufs`ufs_getpage_ra',v:3}],n:'ufs`ufs_getpage',v:3}],n:'genunix`fop_getpage',v:3}],n:'genunix`segvn_faulta',v:3}],n:'genunix`as_faulta',v:3}],n:'genunix`memcntl',v:3},{c:[{c:[{c:[{c:[{c:[{c:[{c:[{c:[{n:'unix`htable_lookup',v:1}],n:'unix`htable_walk',v:1}],n:'unix`hat_unload_callback',v:1}],n:'genunix`segvn_unmap',v:1}],n:'genunix`as_unmap',v:1}],n:'unix`mmapobj_map_elf',v:1}],n:'unix`mmapobj_map_interpret',v:1}],n:'unix`mmapobj',v:1}],n:'genunix`mmapobjsys',v:1},{c:[{n:'genunix`copen',v:7},{c:[{n:'genunix`audit_getstate',v:62},{c:[{n:'genunix`audit_falloc',v:8},{c:[{c:[{c:[{c:[{c:[{n:'unix`swtch',v:1}],n:'unix`preempt',v:1}],n:'unix`kpreempt',v:1}],n:'unix`sys_rtt_common',v:1}],n:'unix`_sys_rtt_ints_disabled',v:1}],n:'genunix`audit_getstate',v:66},{n:'genunix`audit_unfalloc',v:32},{n:'genunix`crfree',v:9},{n:'genunix`crhold',v:5},{n:'genunix`cv_broadcast',v:16},{c:[{c:[{n:'genunix`kmem_cache_alloc',v:11},{c:[{n:'genunix`kmem_cache_alloc',v:66},{n:'unix`mutex_enter',v:122},{n:'unix`mutex_exit',v:46}],n:'genunix`kmem_zalloc',v:280},{n:'unix`bzero',v:8}],n:'genunix`audit_falloc',v:313},{n:'genunix`crhold',v:11},{n:'genunix`kmem_cache_alloc',v:49},{n:'genunix`kmem_zalloc',v:13},{c:[{n:'genunix`fd_find',v:13},{n:'genunix`fd_reserve',v:9},{c:[{n:'genunix`fd_find',v:161},{n:'genunix`fd_reserve',v:15}],n:'genunix`ufalloc_file',v:294},{n:'unix`mutex_enter',v:197},{n:'unix`mutex_exit',v:29}],n:'genunix`ufalloc',v:551},{n:'genunix`ufalloc_file',v:20},{n:'unix`atomic_add_32',v:134},{n:'unix`mutex_enter',v:99},{n:'unix`mutex_exit',v:58}],n:'genunix`falloc',v:1363},{n:'genunix`fd_reserve',v:8},{n:'genunix`kmem_cache_alloc',v:9},{n:'genunix`kmem_cache_free',v:5},{n:'genunix`lookupnameat',v:69},{n:'genunix`set_errno',v:24},{c:[{n:'genunix`audit_getstate',v:31},{n:'genunix`cv_broadcast',v:25},{n:'genunix`fd_reserve',v:35}],n:'genunix`setf',v:187},{n:'genunix`ufalloc',v:10},{c:[{c:[{n:'genunix`kmem_cache_free',v:5},{c:[{n:'genunix`kmem_cache_free',v:73},{n:'unix`mutex_enter',v:111},{n:'unix`mutex_exit',v:55}],n:'genunix`kmem_free',v:288}],n:'genunix`audit_unfalloc',v:340},{n:'genunix`crfree',v:13},{n:'genunix`kmem_cache_free',v:51},{n:'genunix`kmem_free',v:11},{n:'unix`atomic_add_32_nv',v:100},{n:'unix`mutex_enter',v:97},{n:'unix`mutex_exit',v:56}],n:'genunix`unfalloc',v:729},{c:[{c:[{c:[{c:[{n:'genunix`audit_getstate',v:16},{n:'genunix`fop_lookup',v:55},{c:[{n:'genunix`audit_getstate',v:21},{n:'genunix`crgetmapped',v:55},{n:'genunix`fop_inactive',v:39},{c:[{n:'genunix`crgetmapped',v:57},{n:'genunix`dnlc_lookup',v:26},{n:'genunix`fop_lookup',v:85},{n:'genunix`kmem_alloc',v:73},{n:'genunix`traverse',v:30},{n:'genunix`vfs_matchops',v:28},{c:[{c:[{n:'genunix`kmem_cache_alloc',v:241},{n:'unix`mutex_enter',v:366},{n:'unix`mutex_exit',v:149}],n:'genunix`kmem_alloc',v:934},{n:'genunix`kmem_cache_alloc',v:32}],n:'genunix`vn_setpath',v:1969},{c:[{n:'genunix`crgetmapped',v:36},{c:[{n:'genunix`crgetmapped',v:58},{n:'genunix`dnlc_lookup',v:70},{n:'genunix`vn_rele',v:14},{n:'ufs`ufs_iaccess',v:91},{c:[{n:'genunix`crgetuid',v:30},{c:[{n:'genunix`memcmp',v:38},{c:[{n:'genunix`memcmp',v:277}],n:'unix`bcmp',v:295}],n:'genunix`dnlc_lookup',v:1843},{n:'genunix`secpolicy_vnode_access2',v:72},{n:'genunix`vn_rele',v:39},{c:[{n:'genunix`crgetuid',v:22},{n:'genunix`secpolicy_vnode_access2',v:217}],n:'ufs`ufs_iaccess',v:648},{n:'unix`bcmp',v:42},{n:'unix`mutex_enter',v:980},{n:'unix`mutex_exit',v:350},{n:'unix`rw_enter',v:525},{n:'unix`rw_exit',v:439}],n:'ufs`ufs_lookup',v:5399}],n:'genunix`fop_lookup',v:6470},{n:'genunix`kmem_cache_alloc',v:39},{c:[{n:'genunix`rwst_exit',v:18},{n:'genunix`rwst_tryenter',v:32},{n:'genunix`vn_mountedvfs',v:11},{n:'genunix`vn_vfslocks_getlock',v:62},{n:'genunix`vn_vfslocks_rele',v:50},{c:[{n:'genunix`kmem_alloc',v:32},{n:'genunix`rwst_enter_common',v:32},{n:'genunix`rwst_init',v:28},{c:[{n:'genunix`rwst_enter_common',v:264},{n:'unix`mutex_enter',v:337},{n:'unix`mutex_exit',v:105}],n:'genunix`rwst_tryenter',v:734},{c:[{n:'genunix`cv_init',v:53},{c:[{c:[{n:'genunix`kmem_cpu_reload',v:2}],n:'genunix`kmem_cache_alloc',v:168},{n:'unix`mutex_enter',v:379},{n:'unix`mutex_exit',v:155}],n:'genunix`kmem_alloc',v:795},{n:'genunix`kmem_cache_alloc',v:29},{c:[{n:'genunix`cv_init',v:65},{n:'unix`mutex_init',v:53}],n:'genunix`rwst_init',v:236},{n:'unix`mutex_init',v:46}],n:'genunix`vn_vfslocks_getlock',v:1357},{n:'unix`mutex_enter',v:727},{n:'unix`mutex_exit',v:371}],n:'genunix`vn_vfsrlock',v:3342},{c:[{n:'genunix`cv_broadcast',v:25},{n:'genunix`kmem_free',v:35},{n:'genunix`rwst_destroy',v:32},{c:[{n:'genunix`cv_broadcast',v:40}],n:'genunix`rwst_exit',v:167},{n:'genunix`vn_vfslocks_getlock',v:120},{c:[{n:'genunix`cv_destroy',v:77},{n:'genunix`kmem_cache_free',v:22},{c:[{n:'genunix`kmem_cache_free',v:154},{n:'unix`mutex_enter',v:316},{n:'unix`mutex_exit',v:148}],n:'genunix`kmem_free',v:693},{c:[{n:'genunix`cv_destroy',v:42},{n:'unix`mutex_destroy',v:176}],n:'genunix`rwst_destroy',v:296},{n:'unix`mutex_destroy',v:31}],n:'genunix`vn_vfslocks_rele',v:1420},{n:'unix`mutex_enter',v:1202},{n:'unix`mutex_exit',v:512}],n:'genunix`vn_vfsunlock',v:3578}],n:'genunix`traverse',v:7243},{n:'genunix`vfs_getops',v:21},{c:[{n:'genunix`vfs_getops',v:157},{n:'unix`membar_consumer',v:123}],n:'genunix`vfs_matchops',v:336},{n:'genunix`vn_alloc',v:20},{n:'genunix`vn_exists',v:17},{n:'genunix`vn_mountedvfs',v:30},{n:'genunix`vn_setops',v:41},{n:'genunix`vn_vfsrlock',v:13},{n:'genunix`vn_vfsunlock',v:40},{n:'lofs`lfind',v:26},{n:'lofs`lsave',v:27},{n:'lofs`makelfsnode',v:28},{c:[{n:'genunix`kmem_cache_alloc',v:234},{n:'genunix`kmem_cpu_reload',v:1},{c:[{n:'genunix`kmem_cache_alloc',v:179},{n:'genunix`vn_recycle',v:33},{c:[{c:[{n:'genunix`vsd_free',v:155}],n:'genunix`vn_recycle',v:319},{n:'genunix`vsd_free',v:14}],n:'genunix`vn_reinit',v:424},{n:'unix`mutex_enter',v:318},{n:'unix`mutex_exit',v:142}],n:'genunix`vn_alloc',v:1189},{n:'genunix`vn_exists',v:50},{n:'genunix`vn_reinit',v:48},{n:'genunix`vn_setops',v:160},{n:'lofs`lfind',v:278},{n:'lofs`lsave',v:162},{n:'lofs`makelfsnode',v:82},{n:'lofs`table_lock_enter',v:220},{n:'unix`atomic_cas_64',v:318},{n:'unix`membar_consumer',v:237},{n:'unix`mutex_enter',v:640},{n:'unix`mutex_exit',v:138}],n:'lofs`makelonode',v:4212},{n:'lofs`table_lock_enter',v:43},{n:'ufs`ufs_lookup',v:46},{n:'unix`atomic_add_32',v:325},{n:'unix`mutex_exit',v:26}],n:'lofs`lo_lookup',v:19887},{n:'lofs`makelonode',v:39},{n:'unix`bcopy',v:896},{n:'unix`mutex_enter',v:947},{n:'unix`mutex_exit',v:337},{c:[{c:[{c:[{n:'unix`dispatch_hilevel',v:1}],n:'unix`do_interrupt',v:1}],n:'unix`_interrupt',v:1}],n:'unix`strlen',v:2659},{n:'zfs`specvp_check',v:10},{n:'zfs`zfs_fastaccesschk_execute',v:4},{c:[{n:'genunix`crgetuid',v:6},{c:[{n:'genunix`memcmp',v:3},{c:[{n:'genunix`memcmp',v:38}],n:'unix`bcmp',v:45}],n:'genunix`dnlc_lookup',v:263},{n:'unix`bcmp',v:11},{n:'unix`mutex_enter',v:309},{n:'unix`mutex_exit',v:135},{n:'zfs`specvp_check',v:20},{c:[{n:'genunix`crgetuid',v:2}],n:'zfs`zfs_fastaccesschk_execute',v:50}],n:'zfs`zfs_lookup',v:946}],n:'genunix`fop_lookup',v:29216},{n:'genunix`fsop_root',v:62},{n:'genunix`pn_fixslash',v:44},{n:'genunix`pn_getcomponent',v:454},{c:[{c:[{n:'lofs`lo_root',v:80},{n:'unix`mutex_enter',v:95},{n:'unix`mutex_exit',v:59}],n:'genunix`fsop_root',v:297},{n:'genunix`rwst_exit',v:12},{n:'genunix`rwst_tryenter',v:37},{n:'genunix`vn_mountedvfs',v:20},{n:'genunix`vn_rele',v:19},{n:'genunix`vn_vfslocks_getlock',v:47},{n:'genunix`vn_vfslocks_rele',v:34},{c:[{n:'genunix`kmem_alloc',v:11},{n:'genunix`rwst_enter_common',v:28},{n:'genunix`rwst_init',v:13},{c:[{n:'genunix`rwst_enter_common',v:314},{n:'unix`mutex_enter',v:238},{n:'unix`mutex_exit',v:49}],n:'genunix`rwst_tryenter',v:628},{c:[{n:'genunix`cv_init',v:56},{c:[{n:'genunix`kmem_cache_alloc',v:126},{n:'unix`mutex_enter',v:252},{n:'unix`mutex_exit',v:95}],n:'genunix`kmem_alloc',v:533},{n:'genunix`kmem_cache_alloc',v:17},{c:[{n:'genunix`cv_init',v:49},{n:'unix`mutex_init',v:38}],n:'genunix`rwst_init',v:173},{n:'unix`mutex_init',v:31}],n:'genunix`vn_vfslocks_getlock',v:973},{n:'unix`mutex_enter',v:455},{n:'unix`mutex_exit',v:250}],n:'genunix`vn_vfsrlock',v:2414},{c:[{n:'genunix`cv_broadcast',v:14},{n:'genunix`kmem_free',v:17},{n:'genunix`rwst_destroy',v:20},{c:[{n:'genunix`cv_broadcast',v:19}],n:'genunix`rwst_exit',v:110},{n:'genunix`vn_vfslocks_getlock',v:79},{c:[{n:'genunix`cv_destroy',v:81},{n:'genunix`kmem_cache_free',v:18},{c:[{n:'genunix`kmem_cache_free',v:116},{n:'unix`mutex_enter',v:195},{n:'unix`mutex_exit',v:90}],n:'genunix`kmem_free',v:457},{c:[{n:'genunix`cv_destroy',v:31},{n:'unix`mutex_destroy',v:53}],n:'genunix`rwst_destroy',v:146},{n:'unix`mutex_destroy',v:17}],n:'genunix`vn_vfslocks_rele',v:903},{n:'unix`mutex_enter',v:823},{n:'unix`mutex_exit',v:356}],n:'genunix`vn_vfsunlock',v:2372},{n:'lofs`lo_root',v:31},{n:'unix`mutex_enter',v:95},{n:'unix`mutex_exit',v:56}],n:'genunix`traverse',v:5557},{n:'genunix`vn_mountedvfs',v:43},{c:[{n:'genunix`crgetmapped',v:31},{c:[{n:'genunix`crgetmapped',v:41},{n:'lofs`freelonode',v:35},{c:[{n:'genunix`kmem_cache_free',v:29},{n:'genunix`vn_free',v:26},{n:'genunix`vn_invalid',v:20},{n:'genunix`vn_rele',v:25},{c:[{c:[{n:'genunix`kmem_cpu_reload',v:1}],n:'genunix`kmem_cache_free',v:184},{n:'genunix`kmem_free',v:115},{c:[{c:[{n:'genunix`kmem_cpu_reload',v:4}],n:'genunix`kmem_cache_free',v:215},{n:'genunix`kmem_cpu_reload',v:5},{c:[{n:'genunix`kmem_cache_free',v:209},{n:'unix`mutex_enter',v:299},{n:'unix`mutex_exit',v:160}],n:'genunix`kmem_free',v:785},{n:'genunix`vsd_free',v:48},{n:'unix`mutex_enter',v:314},{n:'unix`mutex_exit',v:171}],n:'genunix`vn_free',v:1663},{n:'genunix`vn_invalid',v:47},{n:'genunix`vn_rele',v:64},{n:'genunix`vsd_free',v:17},{n:'lofs`table_lock_enter',v:189},{n:'unix`membar_consumer',v:106},{n:'unix`mutex_enter',v:905},{n:'unix`mutex_exit',v:358},{n:'unix`strlen',v:1238}],n:'lofs`freelonode',v:5313},{n:'lofs`table_lock_enter',v:44},{n:'unix`atomic_add_32',v:292},{n:'unix`mutex_enter',v:279},{n:'unix`mutex_exit',v:212}],n:'lofs`lo_inactive',v:6307}],n:'genunix`fop_inactive',v:6689},{n:'lofs`lo_inactive',v:21}],n:'genunix`vn_rele',v:6943},{n:'genunix`vn_setpath',v:58},{n:'genunix`vn_vfsrlock',v:12},{n:'genunix`vn_vfsunlock',v:20},{n:'lofs`lo_lookup',v:65},{n:'unix`mutex_enter',v:575},{n:'unix`mutex_exit',v:379},{n:'unix`strlen',v:107},{n:'zfs`zfs_lookup',v:22}],n:'genunix`lookuppnvp',v:44242},{n:'genunix`pn_fixslash',v:14},{n:'genunix`pn_getcomponent',v:41},{n:'genunix`traverse',v:17},{n:'genunix`vn_mountedvfs',v:56},{n:'genunix`vn_rele',v:73},{c:[{n:'unix`mutex_delay_default',v:1},{n:'unix`tsc_read',v:1}],n:'unix`mutex_vector_enter',v:2}],n:'genunix`lookuppnatcred',v:44681},{n:'genunix`lookuppnvp',v:10},{c:[{n:'unix`copyinstr',v:25},{n:'unix`copystr',v:598}],n:'genunix`pn_get_buf',v:687},{n:'unix`copyinstr',v:18},{n:'unix`mutex_enter',v:320},{n:'unix`mutex_exit',v:163}],n:'genunix`lookupnameatcred',v:45978},{n:'genunix`lookuppnatcred',v:12},{n:'genunix`pn_get_buf',v:13}],n:'genunix`lookupnameat',v:46075},{n:'genunix`lookupnameatcred',v:22}],n:'genunix`vn_openat',v:46342},{n:'unix`mutex_enter',v:303},{n:'unix`mutex_exit',v:38}],n:'genunix`copen',v:49444},{n:'genunix`falloc',v:36},{n:'genunix`set_errno',v:9},{n:'genunix`setf',v:16},{n:'genunix`unfalloc',v:39},{n:'genunix`vn_openat',v:14}],n:'genunix`openat',v:49647}],n:'genunix`open',v:49669},{n:'genunix`openat',v:17},{c:[{c:[{c:[{n:'genunix`dotoprocs',v:1}],n:'genunix`doprio',v:1}],n:'genunix`priocntl_common',v:1}],n:'genunix`priocntlsys',v:1},{c:[{c:[{c:[{c:[{c:[{c:[{c:[{c:[{n:'genunix`dnlc_lookup',v:1}],n:'ufs`ufs_lookup',v:1}],n:'genunix`fop_lookup',v:1}],n:'lofs`lo_lookup',v:1}],n:'genunix`fop_lookup',v:1}],n:'genunix`lookuppnvp',v:1}],n:'genunix`lookuppnatcred',v:1}],n:'genunix`lookuppn',v:1}],n:'genunix`resolvepath',v:1},{c:[{c:[{c:[{c:[{c:[{c:[{c:[{n:'genunix`kmem_cache_free',v:1}],n:'genunix`kmem_free',v:1}],n:'genunix`removectx',v:1}],n:'genunix`schedctl_lwp_cleanup',v:1}],n:'genunix`exitlwps',v:1},{c:[{c:[{c:[{c:[{c:[{c:[{c:[{n:'unix`hment_compare',v:2}],n:'genunix`avl_find',v:2}],n:'unix`hment_remove',v:2},{n:'unix`page_numtopp_nolock',v:1}],n:'unix`hat_pte_unmap',v:3}],n:'unix`hat_unload_callback',v:3}],n:'genunix`segvn_unmap',v:3}],n:'genunix`as_free',v:3}],n:'genunix`relvm',v:3},{c:[{c:[{c:[{c:[{n:'genunix`vmem_free',v:1}],n:'genunix`segkp_release_internal',v:1}],n:'genunix`segkp_release',v:1}],n:'genunix`schedctl_freepage',v:1}],n:'genunix`schedctl_proc_cleanup',v:1}],n:'genunix`proc_exit',v:5}],n:'genunix`exit',v:5}],n:'genunix`rexit',v:5},{c:[{c:[{n:'unix`tsc_gethrtimeunscaled',v:43},{n:'unix`tsc_read',v:367}],n:'genunix`gethrtime_unscaled',v:420},{n:'unix`tsc_gethrtimeunscaled',v:59}],n:'genunix`syscall_mstate',v:1336},{n:'unix`atomic_add_64',v:205}],n:'unix`sys_syscall',v:51908}],n:'root',v:57412};
diff --git a/stories/icicle/01_unix_icicle.tsx b/stories/icicle/01_unix_icicle.tsx
new file mode 100644
index 0000000000..d4bbecdf3b
--- /dev/null
+++ b/stories/icicle/01_unix_icicle.tsx
@@ -0,0 +1,43 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+
+import { Chart, Datum, Partition, PartitionLayout, Settings } from '../../src';
+import { STORYBOOK_LIGHT_THEME } from '../shared';
+import { config, getFlatData, getLayerSpec, maxDepth } from '../utils/hierarchical_input_utils';
+import { viridis18 as palette } from '../utils/utils';
+
+const color = palette.slice().reverse();
+
+export const Example = () => {
+ return (
+
+
+ d.value as number}
+ valueFormatter={() => ''}
+ layers={getLayerSpec(color)}
+ config={{ ...config, partitionLayout: PartitionLayout.icicle }}
+ />
+
+ );
+};
diff --git a/stories/icicle/02_unix_flame.tsx b/stories/icicle/02_unix_flame.tsx
new file mode 100644
index 0000000000..b76a714eab
--- /dev/null
+++ b/stories/icicle/02_unix_flame.tsx
@@ -0,0 +1,43 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+
+import { Chart, Datum, Partition, PartitionLayout, Settings } from '../../src';
+import { STORYBOOK_LIGHT_THEME } from '../shared';
+import { config, getFlatData, getLayerSpec, maxDepth } from '../utils/hierarchical_input_utils';
+import { plasma18 as palette } from '../utils/utils';
+
+const color = palette.slice().reverse();
+
+export const Example = () => {
+ return (
+
+
+ d.value as number}
+ valueFormatter={() => ''}
+ layers={getLayerSpec(color)}
+ config={{ ...config, partitionLayout: PartitionLayout.flame }}
+ />
+
+ );
+};
diff --git a/stories/icicle/icicle.stories.tsx b/stories/icicle/icicle.stories.tsx
new file mode 100644
index 0000000000..82e96bf81b
--- /dev/null
+++ b/stories/icicle/icicle.stories.tsx
@@ -0,0 +1,30 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { SB_SOURCE_PANEL } from '../utils/storybook';
+
+export default {
+ title: 'Flame (@alpha)',
+ parameters: {
+ options: { selectedPanel: SB_SOURCE_PANEL },
+ },
+};
+
+export { Example as flameChart } from './02_unix_flame';
+export { Example as icicleChart } from './01_unix_icicle';
diff --git a/stories/interactions/4_sunburst_slice_clicks.tsx b/stories/interactions/4_sunburst_slice_clicks.tsx
index 5aa1a54122..1ac43f5d46 100644
--- a/stories/interactions/4_sunburst_slice_clicks.tsx
+++ b/stories/interactions/4_sunburst_slice_clicks.tsx
@@ -26,7 +26,7 @@ import { STORYBOOK_LIGHT_THEME } from '../shared';
import {
indexInterpolatedFillColor,
interpolatorCET2s,
- categoricalFillColor,
+ discreteColor,
colorBrewerCategoricalPastel12,
} from '../utils/utils';
@@ -87,7 +87,7 @@ export const Example = () => {
// pick color from color palette based on mean angle - rather distinct colors in the inner ring
return indexInterpolatedFillColor(interpolatorCET2s)(d, (d.x0 + d.x1) / 2 / (2 * Math.PI), []);
}
- return categoricalFillColor(colorBrewerCategoricalPastel12)(d.sortIndex);
+ return discreteColor(colorBrewerCategoricalPastel12)(d.sortIndex);
},
},
},
@@ -100,7 +100,7 @@ export const Example = () => {
// pick color from color palette based on mean angle - rather distinct colors in the inner ring
return indexInterpolatedFillColor(interpolatorCET2s)(d, (d.x0 + d.x1) / 2 / (2 * Math.PI), []);
}
- return categoricalFillColor(colorBrewerCategoricalPastel12)(d.sortIndex);
+ return discreteColor(colorBrewerCategoricalPastel12)(d.sortIndex);
},
},
},
diff --git a/stories/legend/10_sunburst.tsx b/stories/legend/10_sunburst.tsx
index 448678c414..0cf4951048 100644
--- a/stories/legend/10_sunburst.tsx
+++ b/stories/legend/10_sunburst.tsx
@@ -26,7 +26,7 @@ import { ShapeTreeNode } from '../../src/chart_types/partition_chart/layout/type
import { mocks } from '../../src/mocks/hierarchical';
import { STORYBOOK_LIGHT_THEME } from '../shared';
import {
- categoricalFillColor,
+ discreteColor,
colorBrewerCategoricalStark9,
countryLookup,
productLookup,
@@ -54,15 +54,14 @@ export const Example = () => {
groupByRollup: (d: Datum) => d.sitc1,
nodeLabel: (d: any) => productLookup[d].name,
shape: {
- fillColor: (d: ShapeTreeNode) => categoricalFillColor(colorBrewerCategoricalStark9, 0.7)(d.sortIndex),
+ fillColor: (d: ShapeTreeNode) => discreteColor(colorBrewerCategoricalStark9, 0.7)(d.sortIndex),
},
},
{
groupByRollup: (d: Datum) => countryLookup[d.dest].continentCountry.slice(0, 2),
nodeLabel: (d: any) => regionLookup[d].regionName,
shape: {
- fillColor: (d: ShapeTreeNode) =>
- categoricalFillColor(colorBrewerCategoricalStark9, 0.5)(d.parent.sortIndex),
+ fillColor: (d: ShapeTreeNode) => discreteColor(colorBrewerCategoricalStark9, 0.5)(d.parent.sortIndex),
},
},
{
@@ -70,7 +69,7 @@ export const Example = () => {
nodeLabel: (d: any) => countryLookup[d].name,
shape: {
fillColor: (d: ShapeTreeNode) =>
- categoricalFillColor(colorBrewerCategoricalStark9, 0.3)(d.parent.parent.sortIndex),
+ discreteColor(colorBrewerCategoricalStark9, 0.3)(d.parent.parent.sortIndex),
},
},
]}
diff --git a/stories/stylings/20_partition_background.tsx b/stories/stylings/20_partition_background.tsx
index bccab7e430..2db37ff4bc 100644
--- a/stories/stylings/20_partition_background.tsx
+++ b/stories/stylings/20_partition_background.tsx
@@ -25,7 +25,7 @@ import { config } from '../../src/chart_types/partition_chart/layout/config/conf
import { ShapeTreeNode } from '../../src/chart_types/partition_chart/layout/types/viewmodel_types';
import { mocks } from '../../src/mocks/hierarchical';
import {
- categoricalFillColor,
+ discreteColor,
colorBrewerCategoricalStark9,
countryLookup,
productLookup,
@@ -55,15 +55,14 @@ export const Example = () => {
groupByRollup: (d: Datum) => d.sitc1,
nodeLabel: (d: any) => productLookup[d].name,
shape: {
- fillColor: (d: ShapeTreeNode) => categoricalFillColor(colorBrewerCategoricalStark9, 0.7)(d.sortIndex),
+ fillColor: (d: ShapeTreeNode) => discreteColor(colorBrewerCategoricalStark9, 0.7)(d.sortIndex),
},
},
{
groupByRollup: (d: Datum) => countryLookup[d.dest].continentCountry.slice(0, 2),
nodeLabel: (d: any) => regionLookup[d].regionName,
shape: {
- fillColor: (d: ShapeTreeNode) =>
- categoricalFillColor(colorBrewerCategoricalStark9, 0.5)(d.parent.sortIndex),
+ fillColor: (d: ShapeTreeNode) => discreteColor(colorBrewerCategoricalStark9, 0.5)(d.parent.sortIndex),
},
},
{
@@ -71,7 +70,7 @@ export const Example = () => {
nodeLabel: (d: any) => countryLookup[d].name,
shape: {
fillColor: (d: ShapeTreeNode) =>
- categoricalFillColor(colorBrewerCategoricalStark9, 0.3)(d.parent.parent.sortIndex),
+ discreteColor(colorBrewerCategoricalStark9, 0.3)(d.parent.parent.sortIndex),
},
},
]}
diff --git a/stories/sunburst/15_single_sunburst.tsx b/stories/sunburst/15_single_sunburst.tsx
index 124ac6d25f..94400fb77d 100644
--- a/stories/sunburst/15_single_sunburst.tsx
+++ b/stories/sunburst/15_single_sunburst.tsx
@@ -25,7 +25,7 @@ import { ShapeTreeNode } from '../../src/chart_types/partition_chart/layout/type
import { mocks } from '../../src/mocks/hierarchical';
import { STORYBOOK_LIGHT_THEME } from '../shared';
import {
- categoricalFillColor,
+ discreteColor,
colorBrewerCategoricalStark9,
countryLookup,
productLookup,
@@ -45,15 +45,14 @@ export const Example = () => (
groupByRollup: (d: Datum) => d.sitc1,
nodeLabel: (d: any) => productLookup[d].name,
shape: {
- fillColor: (d: ShapeTreeNode) => categoricalFillColor(colorBrewerCategoricalStark9, 0.7)(d.sortIndex),
+ fillColor: (d: ShapeTreeNode) => discreteColor(colorBrewerCategoricalStark9, 0.7)(d.sortIndex),
},
},
{
groupByRollup: (d: Datum) => countryLookup[d.dest].continentCountry.slice(0, 2),
nodeLabel: (d: any) => regionLookup[d].regionName.replace(/\s/g, '\u00A0'),
shape: {
- fillColor: (d: ShapeTreeNode) =>
- categoricalFillColor(colorBrewerCategoricalStark9, 0.5)(d.parent.sortIndex),
+ fillColor: (d: ShapeTreeNode) => discreteColor(colorBrewerCategoricalStark9, 0.5)(d.parent.sortIndex),
},
},
{
@@ -61,7 +60,7 @@ export const Example = () => (
nodeLabel: (d: any) => countryLookup[d].name.replace(/\s/g, '\u00A0'),
shape: {
fillColor: (d: ShapeTreeNode) =>
- categoricalFillColor(colorBrewerCategoricalStark9, 0.3)(d.parent.parent.sortIndex),
+ discreteColor(colorBrewerCategoricalStark9, 0.3)(d.parent.parent.sortIndex),
},
},
]}
diff --git a/stories/sunburst/26_percentage.tsx b/stories/sunburst/26_percentage.tsx
index 5c7dd3add4..db53cb0405 100644
--- a/stories/sunburst/26_percentage.tsx
+++ b/stories/sunburst/26_percentage.tsx
@@ -25,7 +25,7 @@ import { ShapeTreeNode } from '../../src/chart_types/partition_chart/layout/type
import { mocks } from '../../src/mocks/hierarchical';
import { STORYBOOK_LIGHT_THEME } from '../shared';
import {
- categoricalFillColor,
+ discreteColor,
colorBrewerCategoricalStark9,
countryLookup,
productLookup,
@@ -47,15 +47,14 @@ export const Example = () => (
groupByRollup: (d: Datum) => d.sitc1,
nodeLabel: (d: any) => productLookup[d].name,
shape: {
- fillColor: (d: ShapeTreeNode) => categoricalFillColor(colorBrewerCategoricalStark9, 0.7)(d.sortIndex),
+ fillColor: (d: ShapeTreeNode) => discreteColor(colorBrewerCategoricalStark9, 0.7)(d.sortIndex),
},
},
{
groupByRollup: (d: Datum) => countryLookup[d.dest].continentCountry.slice(0, 2),
nodeLabel: (d: any) => regionLookup[d].regionName,
shape: {
- fillColor: (d: ShapeTreeNode) =>
- categoricalFillColor(colorBrewerCategoricalStark9, 0.5)(d.parent.sortIndex),
+ fillColor: (d: ShapeTreeNode) => discreteColor(colorBrewerCategoricalStark9, 0.5)(d.parent.sortIndex),
},
},
{
@@ -63,7 +62,7 @@ export const Example = () => (
nodeLabel: (d: any) => countryLookup[d].name,
shape: {
fillColor: (d: ShapeTreeNode) =>
- categoricalFillColor(colorBrewerCategoricalStark9, 0.3)(d.parent.parent.sortIndex),
+ discreteColor(colorBrewerCategoricalStark9, 0.3)(d.parent.parent.sortIndex),
},
},
]}
diff --git a/stories/sunburst/27_heterogeneous_depth.tsx b/stories/sunburst/27_heterogeneous_depth.tsx
index a12411cfb0..6cf4fa06e0 100644
--- a/stories/sunburst/27_heterogeneous_depth.tsx
+++ b/stories/sunburst/27_heterogeneous_depth.tsx
@@ -26,7 +26,7 @@ import { PrimitiveValue } from '../../src/chart_types/partition_chart/layout/uti
import { mocks } from '../../src/mocks/hierarchical';
import { STORYBOOK_LIGHT_THEME } from '../shared';
import {
- categoricalFillColor,
+ discreteColor,
colorBrewerCategoricalStark9,
countryLookup,
productLookup,
@@ -46,15 +46,14 @@ export const Example = () => (
groupByRollup: (d: Datum) => d.sitc1,
nodeLabel: (d: PrimitiveValue) => d !== null && productLookup[d].name,
shape: {
- fillColor: (d: ShapeTreeNode) => categoricalFillColor(colorBrewerCategoricalStark9, 0.7)(d.sortIndex),
+ fillColor: (d: ShapeTreeNode) => discreteColor(colorBrewerCategoricalStark9, 0.7)(d.sortIndex),
},
},
{
groupByRollup: (d: Datum) => countryLookup[d.dest].continentCountry.slice(0, 2),
nodeLabel: (d: PrimitiveValue) => d !== null && regionLookup[d].regionName,
shape: {
- fillColor: (d: ShapeTreeNode) =>
- categoricalFillColor(colorBrewerCategoricalStark9, 0.5)(d.parent.sortIndex),
+ fillColor: (d: ShapeTreeNode) => discreteColor(colorBrewerCategoricalStark9, 0.5)(d.parent.sortIndex),
},
},
{
@@ -63,7 +62,7 @@ export const Example = () => (
showAccessor: (d: PrimitiveValue) => !(['chn', 'hkg', 'jpn', 'kor'] as PrimitiveValue[]).includes(d),
shape: {
fillColor: (d: ShapeTreeNode) =>
- categoricalFillColor(colorBrewerCategoricalStark9, 0.3)(d.parent.parent.sortIndex),
+ discreteColor(colorBrewerCategoricalStark9, 0.3)(d.parent.parent.sortIndex),
},
},
]}
diff --git a/stories/sunburst/3_value_formatted_2.tsx b/stories/sunburst/3_value_formatted_2.tsx
index 401f0f0667..4fc81533b7 100644
--- a/stories/sunburst/3_value_formatted_2.tsx
+++ b/stories/sunburst/3_value_formatted_2.tsx
@@ -23,7 +23,7 @@ import { Chart, Datum, Partition } from '../../src';
import { config } from '../../src/chart_types/partition_chart/layout/config/config';
import { ShapeTreeNode } from '../../src/chart_types/partition_chart/layout/types/viewmodel_types';
import { mocks } from '../../src/mocks/hierarchical';
-import { categoricalFillColor, colorBrewerCategoricalPastel12, productLookup } from '../utils/utils';
+import { discreteColor, colorBrewerCategoricalPastel12, productLookup } from '../utils/utils';
export const Example = () => (
@@ -47,7 +47,7 @@ export const Example = () => (
},
},
shape: {
- fillColor: (d: ShapeTreeNode) => categoricalFillColor(colorBrewerCategoricalPastel12)(d.sortIndex),
+ fillColor: (d: ShapeTreeNode) => discreteColor(colorBrewerCategoricalPastel12)(d.sortIndex),
},
},
]}
diff --git a/stories/sunburst/9_sunburst_three_layers.tsx b/stories/sunburst/9_sunburst_three_layers.tsx
index 8a8b0b8aae..f3218e8d38 100644
--- a/stories/sunburst/9_sunburst_three_layers.tsx
+++ b/stories/sunburst/9_sunburst_three_layers.tsx
@@ -26,7 +26,7 @@ import { ShapeTreeNode } from '../../src/chart_types/partition_chart/layout/type
import { mocks } from '../../src/mocks/hierarchical';
import { STORYBOOK_LIGHT_THEME } from '../shared';
import {
- categoricalFillColor,
+ discreteColor,
colorBrewerCategoricalStark9,
countryLookup,
productLookup,
@@ -47,7 +47,7 @@ export const Example = () => (
nodeLabel: (d: any) => productLookup[d].name,
fillLabel: { maximizeFontSize: boolean('Maximize font size layer 1', true) },
shape: {
- fillColor: (d: ShapeTreeNode) => categoricalFillColor(colorBrewerCategoricalStark9, 0.7)(d.sortIndex),
+ fillColor: (d: ShapeTreeNode) => discreteColor(colorBrewerCategoricalStark9, 0.7)(d.sortIndex),
},
},
{
@@ -55,8 +55,7 @@ export const Example = () => (
nodeLabel: (d: any) => regionLookup[d].regionName,
fillLabel: { maximizeFontSize: boolean('Maximize font size layer 2', true) },
shape: {
- fillColor: (d: ShapeTreeNode) =>
- categoricalFillColor(colorBrewerCategoricalStark9, 0.5)(d.parent.sortIndex),
+ fillColor: (d: ShapeTreeNode) => discreteColor(colorBrewerCategoricalStark9, 0.5)(d.parent.sortIndex),
},
},
{
@@ -65,7 +64,7 @@ export const Example = () => (
fillLabel: { maximizeFontSize: boolean('Maximize font size layer 3', true) },
shape: {
fillColor: (d: ShapeTreeNode) =>
- categoricalFillColor(colorBrewerCategoricalStark9, 0.3)(d.parent.parent.sortIndex),
+ discreteColor(colorBrewerCategoricalStark9, 0.3)(d.parent.parent.sortIndex),
},
},
]}
diff --git a/stories/treemap/2_one_layer_2.tsx b/stories/treemap/2_one_layer_2.tsx
index 41ffac1305..d01b4f0023 100644
--- a/stories/treemap/2_one_layer_2.tsx
+++ b/stories/treemap/2_one_layer_2.tsx
@@ -25,7 +25,7 @@ import { ShapeTreeNode } from '../../src/chart_types/partition_chart/layout/type
import { arrayToLookup } from '../../src/chart_types/partition_chart/layout/utils/calcs';
import { mocks } from '../../src/mocks/hierarchical';
import { productDimension } from '../../src/mocks/hierarchical/dimension_codes';
-import { categoricalFillColor, colorBrewerCategoricalPastel12 } from '../utils/utils';
+import { discreteColor, colorBrewerCategoricalPastel12 } from '../utils/utils';
const productLookup = arrayToLookup((d: Datum) => d.sitc1, productDimension);
@@ -48,7 +48,7 @@ export const Example = () => (
},
},
shape: {
- fillColor: (d: ShapeTreeNode) => categoricalFillColor(colorBrewerCategoricalPastel12)(d.sortIndex),
+ fillColor: (d: ShapeTreeNode) => discreteColor(colorBrewerCategoricalPastel12)(d.sortIndex),
},
},
]}
diff --git a/stories/utils/hierarchical_input_utils.tsx b/stories/utils/hierarchical_input_utils.tsx
new file mode 100644
index 0000000000..3d738036a8
--- /dev/null
+++ b/stories/utils/hierarchical_input_utils.tsx
@@ -0,0 +1,84 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { Datum, RecursivePartial } from '../../src';
+import { Config } from '../../src/chart_types/partition_chart/layout/types/config_types';
+import { PrimitiveValue } from '../../src/chart_types/partition_chart/layout/utils/group_by_rollup';
+import { mocks } from '../../src/mocks/hierarchical';
+import { discreteColor } from './utils';
+
+const raw = mocks.observabilityTree;
+
+interface Node {
+ c?: Node[];
+ n: string;
+ v: number;
+}
+
+type Row = { [layerKey: string]: unknown; value: number; depth: number };
+
+const flatTree = ({ c, n, v }: Node, depth: number): Row[] => {
+ if (!c) {
+ return [{ [`layer_${depth}`]: n, value: v, depth }];
+ }
+ // as of writing this, the test runner can't run c.flatMap(...)
+ const childrenRows = c.reduce((a, child) => [...a, ...flatTree(child, depth + 1)], []);
+ const childrenTotal = childrenRows.reduce((p, { value }) => p + value, 0);
+ const missing = Math.max(0, v - childrenTotal);
+ if (missing > 0) {
+ childrenRows.unshift({ [`layer_${depth + 1}`]: undefined, value: missing / 2, depth });
+ childrenRows.push({ [`layer_${depth + 1}`]: undefined, value: missing / 2, depth });
+ }
+ childrenRows.forEach((innerChild) => {
+ innerChild[`layer_${depth}`] = n;
+ });
+ return childrenRows;
+};
+
+/** @internal */
+export const getFlatData = () => flatTree(raw, 0);
+
+/** @internal */
+export const maxDepth = getFlatData().reduce((p, n) => Math.max(p, n.depth), 0);
+
+/** @internal */
+export const getLayerSpec = (color: [string, string, string][]) =>
+ [...new Array(maxDepth + 1)].map((_, depth) => ({
+ groupByRollup: (d: Datum) => d[`layer_${depth}`],
+ nodeLabel: (d: PrimitiveValue) => String(d),
+ showAccessor: (d: PrimitiveValue) => d !== undefined,
+ shape: {
+ fillColor: () => discreteColor(color, 0.8)(depth),
+ },
+ }));
+
+/** @internal */
+export const config: RecursivePartial = {
+ fontFamily: 'Arial',
+ fillLabel: {
+ valueFormatter: (d: number) => d,
+ textInvertible: true,
+ fontWeight: 500,
+ },
+ margin: { top: 0, bottom: 0, left: 0, right: 0 },
+ minFontSize: 5,
+ maxFontSize: 9,
+ idealFontSizeJump: 1.01,
+ backgroundColor: 'rgba(229,229,229,1)',
+};
diff --git a/stories/utils/utils.ts b/stories/utils/utils.ts
index 975526d58f..5894548a28 100644
--- a/stories/utils/utils.ts
+++ b/stories/utils/utils.ts
@@ -36,7 +36,122 @@ export const indexInterpolatedFillColor = (colorMaker: ColorMaker) => (d: any, i
// colorbrewer2.org based, categorical color example
type RGBStrings = [string, string, string][];
const colorBrewerExportMatcher = /rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/;
-const colorStringToTuple = (s: string) => (colorBrewerExportMatcher.exec(s) as string[]).slice(1);
+const rgbStringToTuple = (s: string) => (colorBrewerExportMatcher.exec(s) as string[]).slice(1);
+const hexStringToTuple = (s: string) => [
+ String(parseInt(s.slice(1, 3), 16)),
+ String(parseInt(s.slice(3, 5), 16)),
+ String(parseInt(s.slice(5, 7), 16)),
+];
+
+export const plasma18 = [
+ '#0d0887',
+ '#2f0596',
+ '#4903a0',
+ '#6100a7',
+ '#7801a8',
+ '#8e0ca4',
+ '#a21d9a',
+ '#b42e8d',
+ '#c43e7f',
+ '#d24f71',
+ '#de6164',
+ '#e97257',
+ '#f3854b',
+ '#f99a3e',
+ '#fdaf31',
+ '#fdc627',
+ '#f8df25',
+ '#f0f921',
+].map(hexStringToTuple) as RGBStrings;
+
+export const viridis18 = [
+ '#440154',
+ '#481769',
+ '#472a7a',
+ '#433d84',
+ '#3d4e8a',
+ '#355e8d',
+ '#2e6d8e',
+ '#297b8e',
+ '#23898e',
+ '#1f978b',
+ '#21a585',
+ '#2eb37c',
+ '#46c06f',
+ '#65cb5e',
+ '#89d548',
+ '#b0dd2f',
+ '#d8e219',
+ '#fde725',
+].map(hexStringToTuple) as RGBStrings;
+
+export const cividis18 = [
+ '#002051',
+ '#002b64',
+ '#0f356c',
+ '#23406e',
+ '#374a6e',
+ '#4b556d',
+ '#5c606e',
+ '#6c6b70',
+ '#797673',
+ '#858176',
+ '#928d78',
+ '#9f9978',
+ '#aea575',
+ '#bfb26f',
+ '#d2bf66',
+ '#e4cd5a',
+ '#f4db4e',
+ '#fdea45',
+].map(hexStringToTuple) as RGBStrings;
+
+export const inferno18 = [
+ '#000004',
+ '#0a0722',
+ '#1e0c45',
+ '#380962',
+ '#510e6c',
+ '#69166e',
+ '#801f6c',
+ '#982766',
+ '#b0315b',
+ '#c63d4d',
+ '#d94d3d',
+ '#e9612b',
+ '#f47918',
+ '#fa9407',
+ '#fcb014',
+ '#f8cd37',
+ '#f2ea69',
+ '#fcffa4',
+].map(hexStringToTuple) as RGBStrings;
+
+export const colorBrewerSequential9: RGBStrings = [
+ 'rgb(255,247,251)',
+ 'rgb(236,231,242)',
+ 'rgb(208,209,230)',
+ 'rgb(166,189,219)',
+ 'rgb(116,169,207)',
+ 'rgb(54,144,192)',
+ 'rgb(5,112,176)',
+ 'rgb(4,90,141)',
+ 'rgb(2,56,88)',
+].map(rgbStringToTuple) as RGBStrings;
+
+export const colorBrewerDiverging11: RGBStrings = [
+ 'rgb(158,1,66)',
+ 'rgb(213,62,79)',
+ 'rgb(244,109,67)',
+ 'rgb(253,174,97)',
+ 'rgb(254,224,139)',
+ 'rgb(255,255,191)',
+ 'rgb(230,245,152)',
+ 'rgb(171,221,164)',
+ 'rgb(102,194,165)',
+ 'rgb(50,136,189)',
+ 'rgb(94,79,162)',
+].map(rgbStringToTuple) as RGBStrings;
export const colorBrewerCategorical12: RGBStrings = [
'rgb(166,206,227)',
@@ -51,7 +166,7 @@ export const colorBrewerCategorical12: RGBStrings = [
'rgb(106,61,154)',
'rgb(255,255,153)',
'rgb(177,89,40)',
-].map(colorStringToTuple) as RGBStrings;
+].map(rgbStringToTuple) as RGBStrings;
export const colorBrewerCategoricalPastel12: RGBStrings = [
'rgb(166,206,227)',
@@ -66,7 +181,7 @@ export const colorBrewerCategoricalPastel12: RGBStrings = [
'rgb(106,61,154)',
'rgb(255,255,153)',
'rgb(177,89,40)',
-].map(colorStringToTuple) as RGBStrings;
+].map(rgbStringToTuple) as RGBStrings;
export const colorBrewerCategoricalStark9: RGBStrings = [
'rgb(228,26,28)',
@@ -78,9 +193,9 @@ export const colorBrewerCategoricalStark9: RGBStrings = [
'rgb(166,86,40)',
'rgb(247,129,191)',
'rgb(153,153,153)',
-].map(colorStringToTuple) as RGBStrings;
+].map(rgbStringToTuple) as RGBStrings;
-export const categoricalFillColor = (categoricalColors: RGBStrings, opacity = 1) => (i: number) =>
+export const discreteColor = (categoricalColors: RGBStrings, opacity = 1) => (i: number) =>
`rgba(${categoricalColors[i % categoricalColors.length].concat([opacity.toString()]).join(',')})`;
export const decreasingOpacityCET2 = (opacity: number) => (d: any, i: number, a: any[]) =>