From 72c0d1bc16581f085b5d3f730a10514c74f4e215 Mon Sep 17 00:00:00 2001 From: Robert Monfera Date: Mon, 12 Apr 2021 13:02:51 +0200 Subject: [PATCH] feat(partition): order slices and sectors (#1112) --- packages/osd-charts/api/charts.api.md | 12 +- ...d-slices-visually-looks-correct-1-snap.png | Bin 0 -> 55494 bytes .../layout/utils/group_by_rollup.ts | 33 +++--- .../partition_chart/layout/utils/treemap.ts | 40 ++++++- .../viewmodel/hierarchy_of_arrays.test.ts | 2 +- .../layout/viewmodel/hierarchy_of_arrays.ts | 32 ++++-- .../layout/viewmodel/viewmodel.ts | 19 +++- .../partition_chart/specs/index.ts | 15 ++- packages/osd-charts/src/index.ts | 1 + packages/osd-charts/src/utils/accessor.ts | 9 +- packages/osd-charts/src/utils/common.ts | 3 +- .../stories/sunburst/33_ordered_slices.tsx | 107 ++++++++++++++++++ .../stories/sunburst/sunburst.stories.tsx | 1 + packages/osd-charts/stories/utils/utils.ts | 15 +++ 14 files changed, 239 insertions(+), 50 deletions(-) create mode 100644 packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-ordered-slices-visually-looks-correct-1-snap.png create mode 100644 packages/osd-charts/stories/sunburst/33_ordered_slices.tsx diff --git a/packages/osd-charts/api/charts.api.md b/packages/osd-charts/api/charts.api.md index c411d71d461e..3352d7c837b7 100644 --- a/packages/osd-charts/api/charts.api.md +++ b/packages/osd-charts/api/charts.api.md @@ -21,6 +21,9 @@ export type AccessorFn = UnaryAccessorFn; // @public export type AccessorObjectKey = string; +// @public +export type AdditiveNumber = number; + // @public (undocumented) export const AGGREGATE_KEY = "value"; @@ -1295,6 +1298,9 @@ export interface NodeDescriptor { [AGGREGATE_KEY]: number; } +// @public +export type NodeSorter = (a: ArrayEntry, b: ArrayEntry) => number; + // @public (undocumented) export type NonAny = number | boolean | string | symbol | null; @@ -1356,7 +1362,7 @@ export interface PartitionFillLabel extends LabelConfig { clipText: boolean; } -// @public (undocumented) +// @public export interface PartitionLayer { // Warning: (ae-forgotten-export) The symbol "ExtendedFillLabelConfig" needs to be exported by the entry point index.d.ts // @@ -1372,6 +1378,8 @@ export interface PartitionLayer { }; // (undocumented) showAccessor?: ShowAccessor; + // (undocumented) + sortPredicate?: NodeSorter | null; } // @public (undocumented) @@ -2055,7 +2063,7 @@ export type UnboundedDomainWithInterval = DomainBase; export type UpperBoundedDomain = DomainBase & UpperBound; // @public (undocumented) -export type ValueAccessor = (d: Datum) => number; +export type ValueAccessor = (d: Datum) => AdditiveNumber; // @public (undocumented) export type ValueFormatter = (value: number) => string; diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-ordered-slices-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-ordered-slices-visually-looks-correct-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..95b890352550ba7f738c5c5f6c4d55828a4d128f GIT binary patch literal 55494 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ν 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 number; -type NodeSorter = (a: ArrayEntry, b: ArrayEntry) => number; + +/** + * Binary predicate function used for `[].sort`ing partitions represented as ArrayEntries + * @public + */ +export type NodeSorter = (a: ArrayEntry, b: ArrayEntry) => number; /** @public */ export const entryKey = ([key]: ArrayEntry) => key; @@ -109,8 +114,6 @@ export function sortIndexAccessor(n: ArrayEntry) { export function pathAccessor(n: ArrayEntry) { return entryValue(n)[PATH_KEY]; } -const ascending: Sorter = (a, b) => a - b; -const descending: Sorter = (a, b) => b - a; /** @public */ export function getNodeName(node: ArrayNode) { @@ -182,7 +185,7 @@ function getRootArrayNode(): ArrayNode { } /** @internal */ -export function mapsToArrays(root: HierarchyOfMaps, sorter: NodeSorter | null): HierarchyOfArrays { +export function mapsToArrays(root: HierarchyOfMaps, sortSpecs: (NodeSorter | null)[]): HierarchyOfArrays { const groupByMap = (node: HierarchyOfMaps, parent: ArrayNode) => { const items = Array.from( node, @@ -206,8 +209,15 @@ export function mapsToArrays(root: HierarchyOfMaps, sorter: NodeSorter | null): return [key, newValue]; }, ); - if (sorter !== null) { - items.sort(sorter); + if (sortSpecs.some((s) => s !== null)) { + items.sort((e1: ArrayEntry, e2: ArrayEntry) => { + const node1 = e1[1]; + const node2 = e2[1]; + if (node1[DEPTH_KEY] !== node2[DEPTH_KEY]) return node1[DEPTH_KEY] - node2[DEPTH_KEY]; + const depth = node1[DEPTH_KEY]; + const sorterWithinLayer = sortSpecs[depth]; + return sorterWithinLayer ? sorterWithinLayer(e1, e2) : node2.value - node1.value; + }); } return items.map((n: ArrayEntry, i) => { entryValue(n).sortIndex = i; @@ -229,17 +239,6 @@ export function mapEntryValue(entry: ArrayEntry) { return entryValue(entry)[AGGREGATE_KEY]; } -/** @internal */ -export function aggregateComparator(accessor: (v: any) => any, sorter: Sorter): NodeSorter { - return (a, b) => sorter(accessor(a), accessor(b)); -} - -/** @internal */ -export const childOrders = { - ascending, - descending, -}; - // type MeanReduction = { sum: number; count: number }; // type MedianReduction = Array; diff --git a/packages/osd-charts/src/chart_types/partition_chart/layout/utils/treemap.ts b/packages/osd-charts/src/chart_types/partition_chart/layout/utils/treemap.ts index d2f9671d6ad9..cb1d45b2d794 100644 --- a/packages/osd-charts/src/chart_types/partition_chart/layout/utils/treemap.ts +++ b/packages/osd-charts/src/chart_types/partition_chart/layout/utils/treemap.ts @@ -17,10 +17,12 @@ * under the License. */ +import { $Values as Values } from 'utility-types'; + import { GOLDEN_RATIO } from '../../../../common/constants'; import { Pixels } from '../../../../common/geometry'; import { Part } from '../../../../common/text_utils'; -import { ArrayEntry, CHILDREN_KEY, entryValue, HierarchyOfArrays } from './group_by_rollup'; +import { ArrayEntry, CHILDREN_KEY, DEPTH_KEY, entryValue, HierarchyOfArrays } from './group_by_rollup'; const MAX_U_PADDING_RATIO = 0.0256197; // this limits area distortion to <10% (which occurs due to pixel padding) with very small rectangles const MAX_TOP_PADDING_RATIO = 0.33; // this limits further area distortion to ~33% @@ -62,7 +64,28 @@ const NullLayoutElement: LayoutElement = { sectionOffsets: [], }; -function bestVector(nodes: HierarchyOfArrays, height: number, areaAccessor: (e: ArrayEntry) => number): LayoutElement { +/** + * Specifies whether partitions are laid out horizontally, vertically or treemap-like tiling for preferably squarish aspect ratios + * @public + */ +export const LayerLayout = Object.freeze({ + horizontal: 'horizontal' as const, + vertical: 'vertical' as const, + squarifying: 'squarifying' as const, +}); + +/** + * Specifies whether partitions are laid out horizontally, vertically or treemap-like tiling for preferably squarish aspect ratios + * @public + */ +export type LayerLayout = Values; // could use ValuesType + +function bestVector( + nodes: HierarchyOfArrays, + height: number, + areaAccessor: (e: ArrayEntry) => number, + layout: LayerLayout, +): LayoutElement { let previousWorstAspectRatio = -1; let currentWorstAspectRatio = 0; @@ -75,9 +98,9 @@ function bestVector(nodes: HierarchyOfArrays, height: number, areaAccessor: (e: previousWorstAspectRatio = currentWorstAspectRatio; currentVectorLayout = layVector(nodes.slice(0, currentCount), height, areaAccessor); currentWorstAspectRatio = leastSquarishAspectRatio(currentVectorLayout); - } while (currentCount++ < nodes.length && currentWorstAspectRatio > previousWorstAspectRatio); + } while (currentCount++ < nodes.length && (layout || currentWorstAspectRatio > previousWorstAspectRatio)); - return currentWorstAspectRatio >= previousWorstAspectRatio ? currentVectorLayout : previousVectorLayout; + return layout || currentWorstAspectRatio >= previousWorstAspectRatio ? currentVectorLayout : previousVectorLayout; } function vectorNodeCoordinates(vectorLayout: LayoutElement, x0Base: number, y0Base: number, vertical: boolean) { @@ -107,12 +130,15 @@ export function treemap( width: outerWidth, height: outerHeight, }: { x0: number; y0: number; width: number; height: number }, + layouts: LayerLayout[], ): Array { if (nodes.length === 0) return []; // some bias toward horizontal rectangles with a golden ratio of width to height - const vertical = outerWidth / GOLDEN_RATIO <= outerHeight; + const depth = nodes[0][1][DEPTH_KEY] - 1; + const layerLayout = layouts[depth] ?? null; + const vertical = layerLayout === LayerLayout.vertical || (!layerLayout && outerWidth / GOLDEN_RATIO <= outerHeight); const independentSize = vertical ? outerWidth : outerHeight; - const vectorElements = bestVector(nodes, independentSize, areaAccessor); + const vectorElements = bestVector(nodes, independentSize, areaAccessor, layerLayout); const vector = vectorNodeCoordinates(vectorElements, outerX0, outerY0, vertical); const { dependentSize } = vectorElements; return vector @@ -143,6 +169,7 @@ export function treemap( width, height, }, + layouts, ); }), ) @@ -155,6 +182,7 @@ export function treemap( vertical ? { x0: outerX0, y0: outerY0 + dependentSize, width: outerWidth, height: outerHeight - dependentSize } : { x0: outerX0 + dependentSize, y0: outerY0, width: outerWidth - dependentSize, height: outerHeight }, + layouts, ), ); } diff --git a/packages/osd-charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.test.ts b/packages/osd-charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.test.ts index 95f96d20d3c4..d4e529421180 100644 --- a/packages/osd-charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.test.ts +++ b/packages/osd-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, null); + const outerResult = getHierarchyOfArrays(rawFacts, valueAccessor, groupByRollupAccessors, []); expect(outerResult.length).toBe(1); const results = outerResult[0]; diff --git a/packages/osd-charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.ts b/packages/osd-charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.ts index 0cf410d17811..088a6f5ec2aa 100644 --- a/packages/osd-charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.ts +++ b/packages/osd-charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.ts @@ -25,19 +25,32 @@ import { Datum, ValueAccessor, ValueFormatter } from '../../../../utils/common'; import { Layer } from '../../specs'; import { PartitionLayout } from '../types/config_types'; import { - aggregateComparator, aggregators, - childOrders, CHILDREN_KEY, groupByRollup, HIERARCHY_ROOT_KEY, HierarchyOfArrays, mapEntryValue, mapsToArrays, + NodeSorter, Sorter, } from '../utils/group_by_rollup'; import { isSunburst, isTreemap } from './viewmodel'; +function aggregateComparator(accessor: (v: any) => any, sorter: Sorter): NodeSorter { + return (a, b) => sorter(accessor(a), accessor(b)); +} + +const ascending: Sorter = (a, b) => a - b; +const descending: Sorter = (a, b) => b - a; + +const childOrders = { + ascending, + descending, +}; + +const descendingValueNodes = aggregateComparator(mapEntryValue, childOrders.descending); + /** * @internal */ @@ -45,7 +58,7 @@ export function getHierarchyOfArrays( rawFacts: Relation, valueAccessor: ValueAccessor, groupByRollupAccessors: IndexedAccessorFn[], - sorter: Sorter | null = childOrders.descending, + sortSpecs: (NodeSorter | null)[], ): HierarchyOfArrays { const aggregator = aggregators.sum; @@ -62,27 +75,26 @@ 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), - sorter && aggregateComparator(mapEntryValue, sorter), - ); + return mapsToArrays(groupByRollup(groupByRollupAccessors, valueAccessor, aggregator, facts), sortSpecs); } +const sorter = (layout: PartitionLayout) => ({ sortPredicate }: Layer) => + sortPredicate || (isTreemap(layout) || isSunburst(layout) ? descendingValueNodes : null); + /** @internal */ export function partitionTree( data: Datum[], valueAccessor: ValueAccessor, layers: Layer[], defaultLayout: PartitionLayout, - layout: PartitionLayout = defaultLayout, + partitionLayout: PartitionLayout = defaultLayout, ) { - const sorter = isTreemap(layout) || isSunburst(layout) ? childOrders.descending : null; return getHierarchyOfArrays( data, valueAccessor, // eslint-disable-next-line no-shadow [() => HIERARCHY_ROOT_KEY, ...layers.map(({ groupByRollup }) => groupByRollup)], - sorter, + [null, ...layers.map(sorter(partitionLayout))], ); } diff --git a/packages/osd-charts/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts b/packages/osd-charts/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts index bb0d58213958..24e7fba21d43 100644 --- a/packages/osd-charts/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts +++ b/packages/osd-charts/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts @@ -239,12 +239,19 @@ const rawChildNodes = ( 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: 0, - y0: 0, - width, - height, - }); + return treemap( + tree, + treemapAreaAccessor, + topGrooveAccessor(topGroove), + grooveAccessor, + { + x0: 0, + y0: 0, + width, + height, + }, + [], + ); case PartitionLayout.icicle: case PartitionLayout.flame: diff --git a/packages/osd-charts/src/chart_types/partition_chart/specs/index.ts b/packages/osd-charts/src/chart_types/partition_chart/specs/index.ts index e83f01533a81..744bf907a529 100644 --- a/packages/osd-charts/src/chart_types/partition_chart/specs/index.ts +++ b/packages/osd-charts/src/chart_types/partition_chart/specs/index.ts @@ -35,14 +35,18 @@ import { } from '../../../utils/common'; import { config, percentFormatter } from '../layout/config'; import { Config, FillFontSizeRange, FillLabelConfig } from '../layout/types/config_types'; -import { ShapeTreeNode, ValueGetter, NodeColorAccessor } from '../layout/types/viewmodel_types'; -import { AGGREGATE_KEY, PrimitiveValue } from '../layout/utils/group_by_rollup'; +import { NodeColorAccessor, ShapeTreeNode, ValueGetter } from '../layout/types/viewmodel_types'; +import { AGGREGATE_KEY, NodeSorter, PrimitiveValue } from '../layout/utils/group_by_rollup'; interface ExtendedFillLabelConfig extends FillLabelConfig, FillFontSizeRange {} -/** @public */ +/** + * Specification for a given layer in the partition chart + * @public + */ export interface Layer { groupByRollup: IndexedAccessorFn; + sortPredicate?: NodeSorter | null; nodeLabel?: LabelAccessor; fillLabel?: Partial; showAccessor?: ShowAccessor; @@ -69,7 +73,10 @@ const defaultProps = { ], }; -/** @public */ +/** + * Specifies the partition chart + * @public + */ export interface PartitionSpec extends Spec { specType: typeof SpecType.Series; chartType: typeof ChartType.Partition; diff --git a/packages/osd-charts/src/index.ts b/packages/osd-charts/src/index.ts index a61f37dde4bf..79f96e6284f9 100644 --- a/packages/osd-charts/src/index.ts +++ b/packages/osd-charts/src/index.ts @@ -98,3 +98,4 @@ export * from './utils/themes/merge_utils'; export { MODEL_KEY } from './chart_types/partition_chart/layout/config'; export { LegendStrategy } from './chart_types/partition_chart/layout/utils/highlighted_geoms'; export { Ratio } from './common/geometry'; +export { AdditiveNumber } from './utils/accessor'; diff --git a/packages/osd-charts/src/utils/accessor.ts b/packages/osd-charts/src/utils/accessor.ts index 71875a549f36..906bb334df04 100644 --- a/packages/osd-charts/src/utils/accessor.ts +++ b/packages/osd-charts/src/utils/accessor.ts @@ -100,9 +100,6 @@ export function getAccessorFormatLabel(accessor: AccessorFormat, label: string): /** * Helper function to get accessor value from string, number or function - * - * @param {Datum} datum - * @param {AccessorString|AccessorFn} accessor * @internal */ export function getAccessorValue(datum: Datum, accessor: Accessor | AccessorFn) { @@ -112,3 +109,9 @@ export function getAccessorValue(datum: Datum, accessor: Accessor | AccessorFn) return datum[accessor]; } + +/** + * Additive numbers: numbers whose semantics are conducive to addition; eg. counts and sums are additive, but averages aren't + * @public + */ +export type AdditiveNumber = number; diff --git a/packages/osd-charts/src/utils/common.ts b/packages/osd-charts/src/utils/common.ts index 34c5f5a7b526..062c87c11af6 100644 --- a/packages/osd-charts/src/utils/common.ts +++ b/packages/osd-charts/src/utils/common.ts @@ -21,6 +21,7 @@ import { $Values } from 'utility-types'; import { v1 as uuidV1 } from 'uuid'; import { PrimitiveValue } from '../chart_types/partition_chart/layout/utils/group_by_rollup'; +import { AdditiveNumber } from './accessor'; import { Point } from './point'; /** @public */ @@ -465,7 +466,7 @@ export function getUniqueValues(fullArray: T[], uniqueProperty: keyof T, filt /** @public */ export type ValueFormatter = (value: number) => string; /** @public */ -export type ValueAccessor = (d: Datum) => number; +export type ValueAccessor = (d: Datum) => AdditiveNumber; /** @public */ export type LabelAccessor = (value: PrimitiveValue) => string; /** @public */ diff --git a/packages/osd-charts/stories/sunburst/33_ordered_slices.tsx b/packages/osd-charts/stories/sunburst/33_ordered_slices.tsx new file mode 100644 index 000000000000..281ebaa9d220 --- /dev/null +++ b/packages/osd-charts/stories/sunburst/33_ordered_slices.tsx @@ -0,0 +1,107 @@ +/* + * 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 { boolean } from '@storybook/addon-knobs'; +import React from 'react'; + +import { AdditiveNumber, ArrayEntry, Chart, Datum, MODEL_KEY, Partition, ShapeTreeNode } from '../../src'; +import { config } from '../../src/chart_types/partition_chart/layout/config'; +import { discreteColor, countryLookup, colorBrewerCategoricalPastel12B } from '../utils/utils'; + +const categoricalColors = colorBrewerCategoricalPastel12B.slice(3); + +const data = [ + { region: 'Americas', dest: 'usa', other: false, exportVal: 553359100104 }, + { region: 'Americas', dest: 'Other', other: true, exportVal: 753359100104 }, + { region: 'Asia', dest: 'chn', other: false, exportVal: 392617281424 }, + { region: 'Asia', dest: 'jpn', other: false, exportVal: 177490158520 }, + { region: 'Asia', dest: 'kor', other: false, exportVal: 177421375512 }, + { region: 'Asia', dest: 'Other', other: true, exportVal: 277421375512 }, + { region: 'Europe', dest: 'deu', other: false, exportVal: 253250650864 }, + { region: 'Europe', dest: 'smr', other: false, exportVal: 135443006088 }, + { region: 'Europe', dest: 'Other', other: true, exportVal: 205443006088 }, + { region: 'Africa', dest: 'Other', other: true, exportVal: 305443006088 }, +]; + +const sortPredicate = ([name1, node1]: ArrayEntry, [name2, node2]: ArrayEntry) => { + // unconditionally put "Other" to the end (as the "Other" slice may be larger than a regular slice, yet should be at the end) + if (name1 === 'Other' && name2 !== 'Other') return 1; + if (name2 === 'Other' && name1 !== 'Other') return -1; + + // otherwise, use the decreasing value order + return node2.value - node1.value; +}; + +/* Equivalent, since math ops cleanly coerce false, true to 0, 1 +const sortPredicate = ([name1, node1]: ArrayEntry, [name2, node2]: ArrayEntry) => + (name1 === 'Other') - (name2 === 'Other') || node2.value - node1.value; +*/ + +export const Example = () => { + return ( + + d.exportVal as AdditiveNumber} + valueFormatter={(d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}`} + layers={[ + { + groupByRollup: (d: Datum) => d.region, + nodeLabel: (d: any) => d, + fillLabel: { + valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}`, + textInvertible: true, + fontWeight: 600, + fontStyle: 'italic', + valueFont: { + fontFamily: 'Menlo', + fontStyle: 'normal', + fontWeight: 100, + }, + }, + shape: { + fillColor: (d: ShapeTreeNode) => discreteColor(categoricalColors)(d.sortIndex), + }, + }, + { + groupByRollup: (d: Datum) => d.dest, + nodeLabel: (d: any) => countryLookup[d]?.name ?? d, + sortPredicate: boolean('Move "Other" to end', true) ? sortPredicate : null, + fillLabel: { + textInvertible: true, + fontWeight: 600, + fontStyle: 'italic', + maxFontSize: 16, + valueFont: { + fontFamily: 'Menlo', + fontStyle: 'normal', + fontWeight: 100, + }, + }, + shape: { + fillColor: (d: ShapeTreeNode) => discreteColor(categoricalColors, 0.5)(d[MODEL_KEY].sortIndex), + }, + }, + ]} + config={{ outerSizeRatio: 0.96, specialFirstInnermostSector: false, clockwiseSectors: true }} + /> + + ); +}; diff --git a/packages/osd-charts/stories/sunburst/sunburst.stories.tsx b/packages/osd-charts/stories/sunburst/sunburst.stories.tsx index da34e546590f..eef9d1226cf6 100644 --- a/packages/osd-charts/stories/sunburst/sunburst.stories.tsx +++ b/packages/osd-charts/stories/sunburst/sunburst.stories.tsx @@ -62,3 +62,4 @@ export { Example as customStroke } from './29_custom_stroke'; export { Example as largestCircle } from './30_largest_circle'; export { Example as boldLinkValue } from './31_bold_link_value'; export { Example as customTooltip } from './32_custom_tooltip'; +export { Example as orderedSlices } from './33_ordered_slices'; diff --git a/packages/osd-charts/stories/utils/utils.ts b/packages/osd-charts/stories/utils/utils.ts index b2629e0cfc46..6448a1a60d2c 100644 --- a/packages/osd-charts/stories/utils/utils.ts +++ b/packages/osd-charts/stories/utils/utils.ts @@ -183,6 +183,21 @@ export const colorBrewerCategoricalPastel12: RGBStrings = [ 'rgb(177,89,40)', ].map(rgbStringToTuple) as RGBStrings; +export const colorBrewerCategoricalPastel12B: RGBStrings = [ + 'rgb(141,211,199)', + 'rgb(255,255,179)', + 'rgb(190,186,218)', + 'rgb(251,128,114)', + 'rgb(128,177,211)', + 'rgb(253,180,98)', + 'rgb(179,222,105)', + 'rgb(252,205,229)', + 'rgb(217,217,217)', + 'rgb(188,128,189)', + 'rgb(204,235,197)', + 'rgb(255,237,111)', +].map(rgbStringToTuple) as RGBStrings; + export const colorBrewerCategoricalStark9: RGBStrings = [ 'rgb(228,26,28)', 'rgb(55,126,184)',