From 100145b227b122bc082b6038ca23f8d166ec0363 Mon Sep 17 00:00:00 2001 From: Robert Monfera Date: Fri, 11 Jun 2021 00:34:00 +0200 Subject: [PATCH] fix(partition): getLegendItemsExtra no longer assumes a singleton (#1199) * fix: legendPath in callbacks gained an additional new element which is a BREAKING CHANGE * refactor: exported `HIERARCHY_ROOT_KEY` and also added `NULL_SMALL_MULTIPLES_KEY` --- .github/workflows/api_extractor_check.yaml | 4 +-- packages/charts/api/charts.api.md | 10 ++++-- .../layout/utils/group_by_rollup.ts | 34 ++++++++++++++++--- .../viewmodel/hierarchy_of_arrays.test.ts | 2 +- .../layout/viewmodel/hierarchy_of_arrays.ts | 6 +++- .../layout/viewmodel/viewmodel.ts | 2 +- .../partition_chart/partition.test.tsx | 15 +++++++- .../selectors/get_legend_items_extra.test.ts | 34 +++++++++---------- .../state/selectors/get_legend_items_extra.ts | 11 ++++-- .../state/selectors/picked_shapes.test.ts | 17 ++++++---- .../partition_chart/state/selectors/tree.ts | 12 +++++-- packages/charts/src/state/actions/legend.ts | 13 ++++++- stories/small_multiples/7_sunbursts.tsx | 1 + 13 files changed, 120 insertions(+), 41 deletions(-) diff --git a/.github/workflows/api_extractor_check.yaml b/.github/workflows/api_extractor_check.yaml index a4f636fd1d..60618af39a 100644 --- a/.github/workflows/api_extractor_check.yaml +++ b/.github/workflows/api_extractor_check.yaml @@ -20,7 +20,7 @@ jobs: if: ${{ failure() }} uses: LouisBrunner/diff-action@v0.1.0 with: - old: api/charts.api.md - new: tmp/charts.api.md + old: packages/charts/api/charts.api.md + new: packages/charts/tmp/charts.api.md mode: deletion tolerance: better diff --git a/packages/charts/api/charts.api.md b/packages/charts/api/charts.api.md index b96d4941ac..8d7f80ce4a 100644 --- a/packages/charts/api/charts.api.md +++ b/packages/charts/api/charts.api.md @@ -1027,6 +1027,9 @@ export interface HeatmapSpec extends Spec { ySortPredicate: Predicate; } +// @public +export const HIERARCHY_ROOT_KEY: Key; + // @public (undocumented) export type HierarchyOfArrays = Array; @@ -1132,7 +1135,7 @@ export interface LegendColorPickerProps { // @public (undocumented) export type LegendItemListener = (series: SeriesIdentifier[]) => void; -// @public (undocumented) +// @public export type LegendPath = LegendPathElement[]; // @public (undocumented) @@ -1325,6 +1328,9 @@ export type NodeSorter = (a: ArrayEntry, b: ArrayEntry) => number; // @public (undocumented) export type NonAny = number | boolean | string | symbol | null; +// @public +export const NULL_SMALL_MULTIPLES_KEY: Key; + // @public (undocumented) export interface Opacity { opacity: number; @@ -1528,7 +1534,7 @@ export interface Postfixes { y1AccessorFormat?: string; } -// @public (undocumented) +// @public export type PrimitiveValue = string | number | null; // @public diff --git a/packages/charts/src/chart_types/partition_chart/layout/utils/group_by_rollup.ts b/packages/charts/src/chart_types/partition_chart/layout/utils/group_by_rollup.ts index e87f758e82..e7d077b028 100644 --- a/packages/charts/src/chart_types/partition_chart/layout/utils/group_by_rollup.ts +++ b/packages/charts/src/chart_types/partition_chart/layout/utils/group_by_rollup.ts @@ -56,6 +56,7 @@ export interface NodeDescriptor { export type ArrayEntry = [Key, ArrayNode]; /** @public */ export type HierarchyOfArrays = Array; + /** @public */ export interface ArrayNode extends NodeDescriptor { [CHILDREN_KEY]: HierarchyOfArrays; @@ -65,15 +66,30 @@ export interface ArrayNode extends NodeDescriptor { } type HierarchyOfMaps = Map; + interface MapNode extends NodeDescriptor { [CHILDREN_KEY]?: HierarchyOfMaps; [PARENT_KEY]?: ArrayNode; } -/** @internal */ +/** + * Used in the first position of a `LegendPath` array, which indicates the stringified value of the `groupBy` value + * in case of small multiples, but has no applicable `groupBy` for singleton (non-small-multiples) charts + * @public + */ +export const NULL_SMALL_MULTIPLES_KEY: Key = '__null_small_multiples_key__'; + +/** + * Indicates that a node is the root of a specific partition chart, eg. the root of a single pie chart, or one pie + * chart in a small multiples setting. Used in the second position of a `LegendPath` array + * @public + */ export const HIERARCHY_ROOT_KEY: Key = '__root_key__'; -/** @public */ +/** + * A primitive JavaScript value, possibly further restricted + * @public + */ export type PrimitiveValue = string | number | null; // there could be more but sufficient for now /** @public */ export type Key = CategoryKey; @@ -90,26 +106,32 @@ export type NodeSorter = (a: ArrayEntry, b: ArrayEntry) => number; export const entryKey = ([key]: ArrayEntry) => key; /** @public */ export const entryValue = ([, value]: ArrayEntry) => value; + /** @public */ export function depthAccessor(n: ArrayEntry) { return entryValue(n)[DEPTH_KEY]; } + /** @public */ export function aggregateAccessor(n: ArrayEntry): number { return entryValue(n)[AGGREGATE_KEY]; } + /** @public */ export function parentAccessor(n: ArrayEntry): ArrayNode { return entryValue(n)[PARENT_KEY]; } + /** @public */ export function childrenAccessor(n: ArrayEntry) { return entryValue(n)[CHILDREN_KEY]; } + /** @public */ export function sortIndexAccessor(n: ArrayEntry) { return entryValue(n)[SORT_INDEX_KEY]; } + /** @public */ export function pathAccessor(n: ArrayEntry) { return entryValue(n)[PATH_KEY]; @@ -185,7 +207,11 @@ function getRootArrayNode(): ArrayNode { } /** @internal */ -export function mapsToArrays(root: HierarchyOfMaps, sortSpecs: (NodeSorter | null)[]): HierarchyOfArrays { +export function mapsToArrays( + root: HierarchyOfMaps, + sortSpecs: (NodeSorter | null)[], + innerGroups: LegendPath, +): HierarchyOfArrays { const groupByMap = (node: HierarchyOfMaps, parent: ArrayNode) => { const items = Array.from( node, @@ -230,7 +256,7 @@ export function mapsToArrays(root: HierarchyOfMaps, sortSpecs: (NodeSorter | nul mapNode[PATH_KEY] = newPath; // in-place mutation, so disabled `no-param-reassign` mapNode.children.forEach((entry) => buildPaths(entry, newPath)); }; - buildPaths(tree[0], []); + buildPaths(tree[0], innerGroups); return tree; } diff --git a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.test.ts b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.test.ts index d4e5294211..7ca376a17d 100644 --- a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.test.ts +++ b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.test.ts @@ -32,7 +32,7 @@ const groupByRollupAccessors = [() => null, (d: any) => d.sitc1]; describe('Test', () => { test('getHierarchyOfArrays should omit zero and negative values', () => { - const outerResult = getHierarchyOfArrays(rawFacts, valueAccessor, groupByRollupAccessors, []); + const outerResult = getHierarchyOfArrays(rawFacts, valueAccessor, groupByRollupAccessors, [], []); expect(outerResult.length).toBe(1); const results = outerResult[0]; diff --git a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.ts b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.ts index 9e5f5043db..8da19c18c3 100644 --- a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.ts +++ b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.ts @@ -20,6 +20,7 @@ import { LegendItemExtraValues } from '../../../../common/legend'; import { SeriesKey } from '../../../../common/series_id'; import { Relation } from '../../../../common/text_utils'; +import { LegendPath } from '../../../../state/actions/legend'; import { IndexedAccessorFn } from '../../../../utils/accessor'; import { Datum, ValueAccessor, ValueFormatter } from '../../../../utils/common'; import { Layer } from '../../specs'; @@ -60,6 +61,7 @@ export function getHierarchyOfArrays( valueAccessor: ValueAccessor, groupByRollupAccessors: IndexedAccessorFn[], sortSpecs: (NodeSorter | null)[], + innerGroups: LegendPath, ): HierarchyOfArrays { const aggregator = aggregators.sum; @@ -76,7 +78,7 @@ export function getHierarchyOfArrays( // We can precompute things invariant of how the rectangle is divvied up. // By introducing `scale`, we no longer need to deal with the dichotomy of // size as data value vs size as number of pixels in the rectangle - return mapsToArrays(groupByRollup(groupByRollupAccessors, valueAccessor, aggregator, facts), sortSpecs); + return mapsToArrays(groupByRollup(groupByRollupAccessors, valueAccessor, aggregator, facts), sortSpecs, innerGroups); } const sorter = (layout: PartitionLayout) => ({ sortPredicate }: Layer, i: number) => @@ -96,6 +98,7 @@ export function partitionTree( layers: Layer[], defaultLayout: PartitionLayout, partitionLayout: PartitionLayout = defaultLayout, + innerGroups: LegendPath, ) { return getHierarchyOfArrays( data, @@ -103,6 +106,7 @@ export function partitionTree( // eslint-disable-next-line no-shadow [() => HIERARCHY_ROOT_KEY, ...layers.map(({ groupByRollup }) => groupByRollup)], [null, ...layers.map(sorter(partitionLayout))], + innerGroups, ); } diff --git a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts index b1d7c293a0..12343722e1 100644 --- a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts +++ b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts @@ -262,7 +262,7 @@ const rawChildNodes = ( const icicleLayout = isIcicle(partitionLayout); const icicleValueToAreaScale = width / totalValue; const icicleAreaAccessor = (e: ArrayEntry) => icicleValueToAreaScale * mapEntryValue(e); - const icicleRowHeight = height / maxDepth; + const icicleRowHeight = height / (maxDepth - 1); const result = sunburst(tree, icicleAreaAccessor, { x0: 0, y0: -icicleRowHeight }, true, false, icicleRowHeight); return icicleLayout ? result diff --git a/packages/charts/src/chart_types/partition_chart/partition.test.tsx b/packages/charts/src/chart_types/partition_chart/partition.test.tsx index c102890dad..21629c4b20 100644 --- a/packages/charts/src/chart_types/partition_chart/partition.test.tsx +++ b/packages/charts/src/chart_types/partition_chart/partition.test.tsx @@ -23,7 +23,7 @@ import { MockGlobalSpec, MockSeriesSpec } from '../../mocks/specs'; import { MockStore } from '../../mocks/store'; import { GlobalChartState } from '../../state/chart_state'; import { LegendItemLabel } from '../../state/selectors/get_legend_items_labels'; -import { HIERARCHY_ROOT_KEY } from './layout/utils/group_by_rollup'; +import { HIERARCHY_ROOT_KEY, NULL_SMALL_MULTIPLES_KEY } from './layout/utils/group_by_rollup'; import { computeLegendSelector } from './state/selectors/compute_legend'; import { getLegendItemsLabels } from './state/selectors/get_legend_items_labels'; @@ -136,6 +136,7 @@ describe('Retain hierarchy even with arbitrary names', () => { childId: 'A', color: 'rgba(128, 0, 0, 0.5)', path: [ + { index: 0, value: NULL_SMALL_MULTIPLES_KEY }, { index: 0, value: HIERARCHY_ROOT_KEY }, { index: 0, value: 'A' }, ], @@ -148,6 +149,7 @@ describe('Retain hierarchy even with arbitrary names', () => { childId: 'A', color: 'rgba(128, 0, 0, 0.5)', path: [ + { index: 0, value: NULL_SMALL_MULTIPLES_KEY }, { index: 0, value: HIERARCHY_ROOT_KEY }, { index: 0, value: 'A' }, { index: 0, value: 'A' }, @@ -161,6 +163,7 @@ describe('Retain hierarchy even with arbitrary names', () => { childId: 'B', color: 'rgba(128, 0, 0, 0.5)', path: [ + { index: 0, value: NULL_SMALL_MULTIPLES_KEY }, { index: 0, value: HIERARCHY_ROOT_KEY }, { index: 0, value: 'A' }, { index: 1, value: 'B' }, @@ -174,6 +177,7 @@ describe('Retain hierarchy even with arbitrary names', () => { childId: 'B', color: 'rgba(128, 0, 0, 0.5)', path: [ + { index: 0, value: NULL_SMALL_MULTIPLES_KEY }, { index: 0, value: HIERARCHY_ROOT_KEY }, { index: 1, value: 'B' }, ], @@ -186,6 +190,7 @@ describe('Retain hierarchy even with arbitrary names', () => { childId: 'A', color: 'rgba(128, 0, 0, 0.5)', path: [ + { index: 0, value: NULL_SMALL_MULTIPLES_KEY }, { index: 0, value: HIERARCHY_ROOT_KEY }, { index: 1, value: 'B' }, { index: 0, value: 'A' }, @@ -199,6 +204,7 @@ describe('Retain hierarchy even with arbitrary names', () => { childId: 'B', color: 'rgba(128, 0, 0, 0.5)', path: [ + { index: 0, value: NULL_SMALL_MULTIPLES_KEY }, { index: 0, value: HIERARCHY_ROOT_KEY }, { index: 1, value: 'B' }, { index: 1, value: 'B' }, @@ -212,6 +218,7 @@ describe('Retain hierarchy even with arbitrary names', () => { childId: 'C', color: 'rgba(128, 0, 0, 0.5)', path: [ + { index: 0, value: NULL_SMALL_MULTIPLES_KEY }, { index: 0, value: HIERARCHY_ROOT_KEY }, { index: 2, value: 'C' }, ], @@ -224,6 +231,7 @@ describe('Retain hierarchy even with arbitrary names', () => { childId: 'A', color: 'rgba(128, 0, 0, 0.5)', path: [ + { index: 0, value: NULL_SMALL_MULTIPLES_KEY }, { index: 0, value: HIERARCHY_ROOT_KEY }, { index: 2, value: 'C' }, { index: 0, value: 'A' }, @@ -237,6 +245,7 @@ describe('Retain hierarchy even with arbitrary names', () => { childId: 'B', color: 'rgba(128, 0, 0, 0.5)', path: [ + { index: 0, value: NULL_SMALL_MULTIPLES_KEY }, { index: 0, value: HIERARCHY_ROOT_KEY }, { index: 2, value: 'C' }, { index: 1, value: 'B' }, @@ -262,6 +271,7 @@ describe('Retain hierarchy even with arbitrary names', () => { childId: 'A', color: 'rgba(128, 0, 0, 0.5)', path: [ + { index: 0, value: NULL_SMALL_MULTIPLES_KEY }, { index: 0, value: HIERARCHY_ROOT_KEY, @@ -280,6 +290,7 @@ describe('Retain hierarchy even with arbitrary names', () => { childId: 'A', color: 'rgba(128, 0, 0, 0.5)', path: [ + { index: 0, value: NULL_SMALL_MULTIPLES_KEY }, { index: 0, value: HIERARCHY_ROOT_KEY, @@ -315,6 +326,7 @@ describe('Retain hierarchy even with arbitrary names', () => { childId: 'C', color: 'rgba(128, 0, 0, 0.5)', path: [ + { index: 0, value: NULL_SMALL_MULTIPLES_KEY }, { index: 0, value: HIERARCHY_ROOT_KEY, @@ -333,6 +345,7 @@ describe('Retain hierarchy even with arbitrary names', () => { childId: 'B', color: 'rgba(128, 0, 0, 0.5)', path: [ + { index: 0, value: NULL_SMALL_MULTIPLES_KEY }, { index: 0, value: HIERARCHY_ROOT_KEY, diff --git a/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_extra.test.ts b/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_extra.test.ts index eead0e7371..6ce0c26a89 100644 --- a/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_extra.test.ts +++ b/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_extra.test.ts @@ -69,25 +69,25 @@ describe('Partition - Legend item extra values', () => { const extraValues = getLegendItemsExtra(store.getState()); expect([...extraValues.keys()]).toEqual([ - '0', '0__0', '0__0__0', '0__0__0__0', + '0__0__0__0__0', + '0__0__0__0__1', '0__0__0__1', + '0__0__0__1__0', + '0__0__0__1__1', + '0__0__0__1__2', '0__0__1', '0__0__1__0', + '0__0__1__0__0', + '0__0__1__0__1', '0__0__1__1', + '0__0__1__1__0', + '0__0__1__1__1', '0__0__1__2', - '0__1', - '0__1__0', - '0__1__0__0', - '0__1__0__1', - '0__1__1', - '0__1__1__0', - '0__1__1__1', - '0__1__2', - '0__1__2__0', - '0__1__2__1', + '0__0__1__2__0', + '0__0__1__2__1', ]); expect(extraValues.values()).toMatchSnapshot(); }); @@ -97,7 +97,7 @@ describe('Partition - Legend item extra values', () => { MockStore.addSpecs([settings, spec], store); const extraValues = getLegendItemsExtra(store.getState()); - expect([...extraValues.keys()]).toEqual(['0', '0__0', '0__1']); + expect([...extraValues.keys()]).toEqual(['0__0', '0__0__0', '0__0__1']); expect(extraValues.values()).toMatchSnapshot(); }); @@ -107,14 +107,14 @@ describe('Partition - Legend item extra values', () => { const extraValues = getLegendItemsExtra(store.getState()); expect([...extraValues.keys()]).toEqual([ - '0', '0__0', '0__0__0', + '0__0__0__0', + '0__0__0__1', '0__0__1', - '0__1', - '0__1__0', - '0__1__1', - '0__1__2', + '0__0__1__0', + '0__0__1__1', + '0__0__1__2', ]); expect(extraValues.values()).toMatchSnapshot(); }); diff --git a/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_extra.ts b/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_extra.ts index 007664d162..d5a47a336c 100644 --- a/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_extra.ts +++ b/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_extra.ts @@ -31,8 +31,15 @@ import { getTrees } from './tree'; export const getLegendItemsExtra = createCachedSelector( [getPartitionSpec, getSettingsSpecSelector, getTrees], (spec, { legendMaxDepth }, trees): Map => { + const emptyMap = new Map(); return spec && !Number.isNaN(legendMaxDepth) && legendMaxDepth > 0 - ? getExtraValueMap(spec.layers, spec.valueFormatter, trees[0].tree, legendMaxDepth) // singleton! wrt inner small multiples - : new Map(); + ? trees.reduce((result, { tree }) => { + const treeData = getExtraValueMap(spec.layers, spec.valueFormatter, tree, legendMaxDepth); + for (const [key, value] of treeData) { + result.set(key, value); + } + return result; + }, emptyMap) + : emptyMap; }, )(getChartIdSelector); diff --git a/packages/charts/src/chart_types/partition_chart/state/selectors/picked_shapes.test.ts b/packages/charts/src/chart_types/partition_chart/state/selectors/picked_shapes.test.ts index fbbf74f4c5..3fe11b307d 100644 --- a/packages/charts/src/chart_types/partition_chart/state/selectors/picked_shapes.test.ts +++ b/packages/charts/src/chart_types/partition_chart/state/selectors/picked_shapes.test.ts @@ -35,17 +35,17 @@ import { onMouseDown, onMouseUp, onPointerMove } from '../../../../state/actions import { upsertSpec, specParsed } from '../../../../state/actions/specs'; import { chartStoreReducer, GlobalChartState } from '../../../../state/chart_state'; import { Datum } from '../../../../utils/common'; -import { HIERARCHY_ROOT_KEY } from '../../layout/utils/group_by_rollup'; +import { HIERARCHY_ROOT_KEY, NULL_SMALL_MULTIPLES_KEY } from '../../layout/utils/group_by_rollup'; import { PartitionSpec } from '../../specs'; import { partitionMultiGeometries } from './geometries'; import { createOnElementClickCaller } from './on_element_click_caller'; -describe('Picked shapes selector', () => { - function initStore() { - const storeReducer = chartStoreReducer('chartId'); - return createStore(storeReducer); - } +function initStore() { + const storeReducer = chartStoreReducer('chartId'); + return createStore(storeReducer); +} +describe('Picked shapes selector', () => { function addSeries(store: Store, spec: PartitionSpec, settings?: Partial) { store.dispatch(upsertSpec(MockGlobalSpec.settings(settings))); store.dispatch(upsertSpec(spec)); @@ -131,6 +131,7 @@ describe('Picked shapes selector', () => { depth: 1, sortIndex: 1, path: [ + { index: 0, value: NULL_SMALL_MULTIPLES_KEY }, { index: 0, value: HIERARCHY_ROOT_KEY }, { index: 1, value: 'b' }, ], @@ -142,6 +143,7 @@ describe('Picked shapes selector', () => { depth: 2, sortIndex: 1, path: [ + { index: 0, value: NULL_SMALL_MULTIPLES_KEY }, { index: 0, value: HIERARCHY_ROOT_KEY }, { index: 1, value: 'b' }, { index: 1, value: 'b' }, @@ -211,6 +213,7 @@ describe('Picked shapes selector', () => { depth: 1, sortIndex: 0, path: [ + { index: 0, value: 'a' }, { index: 0, value: HIERARCHY_ROOT_KEY }, { index: 0, value: 'a' }, ], @@ -252,6 +255,7 @@ describe('Picked shapes selector', () => { depth: 1, sortIndex: 1, path: [ + { index: 0, value: NULL_SMALL_MULTIPLES_KEY }, { index: 0, value: HIERARCHY_ROOT_KEY }, { index: 1, value: 'b' }, ], @@ -263,6 +267,7 @@ describe('Picked shapes selector', () => { depth: 2, sortIndex: 1, path: [ + { index: 0, value: NULL_SMALL_MULTIPLES_KEY }, { index: 0, value: HIERARCHY_ROOT_KEY }, { index: 1, value: 'b' }, { index: 1, value: 'b' }, diff --git a/packages/charts/src/chart_types/partition_chart/state/selectors/tree.ts b/packages/charts/src/chart_types/partition_chart/state/selectors/tree.ts index e87a4ff04b..0323322a48 100644 --- a/packages/charts/src/chart_types/partition_chart/state/selectors/tree.ts +++ b/packages/charts/src/chart_types/partition_chart/state/selectors/tree.ts @@ -35,7 +35,7 @@ import { getSmallMultiplesSpecs } from '../../../../state/selectors/get_small_mu import { getSpecsFromStore } from '../../../../state/utils'; import { Datum } from '../../../../utils/common'; import { configMetadata } from '../../layout/config'; -import { HierarchyOfArrays } from '../../layout/utils/group_by_rollup'; +import { HierarchyOfArrays, NULL_SMALL_MULTIPLES_KEY } from '../../layout/utils/group_by_rollup'; import { partitionTree } from '../../layout/viewmodel/hierarchy_of_arrays'; import { PartitionSpec } from '../../specs'; import { getPartitionSpecs } from './get_partition_specs'; @@ -84,7 +84,7 @@ function getTreesForSpec( }, new Map()); return Array.from(groups) .sort(getPredicateFn(sort)) - .map(([groupKey, subData]) => ({ + .map(([groupKey, subData], innerIndex) => ({ name: format(groupKey), smAccessorValue: groupKey, style: smStyle, @@ -94,6 +94,7 @@ function getTreesForSpec( layers, configMetadata.partitionLayout.dflt, config.partitionLayout, + [{ index: innerIndex, value: String(groupKey) }], ), })); } else { @@ -102,7 +103,12 @@ function getTreesForSpec( name: '', smAccessorValue: '', style: smStyle, - tree: partitionTree(data, valueAccessor, layers, configMetadata.partitionLayout.dflt, config.partitionLayout), + tree: partitionTree(data, valueAccessor, layers, configMetadata.partitionLayout.dflt, config.partitionLayout, [ + { + index: 0, + value: NULL_SMALL_MULTIPLES_KEY, + }, + ]), }, ]; } diff --git a/packages/charts/src/state/actions/legend.ts b/packages/charts/src/state/actions/legend.ts index b4cd9f1c0e..2ed2187c30 100644 --- a/packages/charts/src/state/actions/legend.ts +++ b/packages/charts/src/state/actions/legend.ts @@ -32,13 +32,24 @@ export const ON_TOGGLE_DESELECT_SERIES = 'ON_TOGGLE_DESELECT_SERIES'; /** @public */ export type LegendPathElement = { index: number; value: CategoryKey }; -/** @public */ +/** + * This is an array that defines a path for chart types characterized by hierarchical breakdown of the data, currently + * partition charts. With partition charts, + * - element index 0 is the `groupBy` breakdown: a panel `index` number, and a stringified category `value` + * - if the chart is a singleton, ie. there's no trellising, it's `{index: 0, value: NULL_SMALL_MULTIPLES_KEY}` + * - element index 1 represents the singular root of a specific pie etc. chart `{index: 0, value: HIERARCHY_ROOT_KEY}` + * - element index 2 represents the primary breakdown categories within a pie/treemap/etc. + * - element index 3 the next level breakdown, if any (eg. a ring around the pie, ie. sunburst) + * etc. + * @public + */ export type LegendPath = LegendPathElement[]; interface LegendItemOverAction { type: typeof ON_LEGEND_ITEM_OVER; legendPath: LegendPath; } + interface LegendItemOutAction { type: typeof ON_LEGEND_ITEM_OUT; } diff --git a/stories/small_multiples/7_sunbursts.tsx b/stories/small_multiples/7_sunbursts.tsx index b84f509891..e4592f278c 100644 --- a/stories/small_multiples/7_sunbursts.tsx +++ b/stories/small_multiples/7_sunbursts.tsx @@ -80,6 +80,7 @@ export const Example = () => {