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 000000000000..95b890352550 Binary files /dev/null and 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 differ diff --git a/packages/osd-charts/src/chart_types/partition_chart/layout/utils/group_by_rollup.ts b/packages/osd-charts/src/chart_types/partition_chart/layout/utils/group_by_rollup.ts index dfbd1b7ba580..e87f758e82cf 100644 --- a/packages/osd-charts/src/chart_types/partition_chart/layout/utils/group_by_rollup.ts +++ b/packages/osd-charts/src/chart_types/partition_chart/layout/utils/group_by_rollup.ts @@ -79,7 +79,12 @@ export type PrimitiveValue = string | number | null; // there could be more but export type Key = CategoryKey; /** @public */ export type Sorter = (a: number, b: number) => 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)',