diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-bold-link-value-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-bold-link-value-visually-looks-correct-1-snap.png new file mode 100644 index 000000000000..1c3579a26c74 Binary files /dev/null and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-bold-link-value-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-clockwise-no-special-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-clockwise-no-special-visually-looks-correct-1-snap.png index c2d853546851..a35c7fb836d3 100644 Binary files a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-clockwise-no-special-visually-looks-correct-1-snap.png and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-clockwise-no-special-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-counter-clockwise-special-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-counter-clockwise-special-visually-looks-correct-1-snap.png index 261a4c45a701..4240f370d1ac 100644 Binary files a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-counter-clockwise-special-visually-looks-correct-1-snap.png and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-counter-clockwise-special-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-custom-stroke-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-custom-stroke-visually-looks-correct-1-snap.png new file mode 100644 index 000000000000..54081a1ff042 Binary files /dev/null and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-custom-stroke-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-donut-chart-with-fill-labels-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-donut-chart-with-fill-labels-visually-looks-correct-1-snap.png index faa757070b1a..f80bc21e71a2 100644 Binary files a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-donut-chart-with-fill-labels-visually-looks-correct-1-snap.png and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-donut-chart-with-fill-labels-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-heterogeneous-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-heterogeneous-visually-looks-correct-1-snap.png index 1401f6c71685..98e98a3dc997 100644 Binary files a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-heterogeneous-visually-looks-correct-1-snap.png and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-heterogeneous-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-high-number-of-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-high-number-of-slices-visually-looks-correct-1-snap.png index 6af238a9e33c..e72f224b8d08 100644 Binary files a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-high-number-of-slices-visually-looks-correct-1-snap.png and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-high-number-of-slices-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-largest-circle-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-largest-circle-visually-looks-correct-1-snap.png new file mode 100644 index 000000000000..93f82da18ecd Binary files /dev/null and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-largest-circle-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-linked-labels-only-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-linked-labels-only-visually-looks-correct-1-snap.png index f76a32fc0231..d943b852d659 100644 Binary files a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-linked-labels-only-visually-looks-correct-1-snap.png and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-linked-labels-only-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-most-basic-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-most-basic-visually-looks-correct-1-snap.png index 32506856c3ed..ccb4a73e2c33 100644 Binary files a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-most-basic-visually-looks-correct-1-snap.png and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-most-basic-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-negative-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-negative-visually-looks-correct-1-snap.png index 9770ba7a14ef..0a123ae6449f 100644 Binary files a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-negative-visually-looks-correct-1-snap.png and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-negative-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-not-a-number-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-not-a-number-visually-looks-correct-1-snap.png index 9770ba7a14ef..0a123ae6449f 100644 Binary files a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-not-a-number-visually-looks-correct-1-snap.png and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-not-a-number-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-percentage-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-percentage-visually-looks-correct-1-snap.png index 1fd2638829bd..ed8f2f43e360 100644 Binary files a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-percentage-visually-looks-correct-1-snap.png and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-percentage-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-some-zero-value-slice-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-some-zero-value-slice-visually-looks-correct-1-snap.png index 2b686da9d8f6..29c1ce5b308b 100644 Binary files a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-some-zero-value-slice-visually-looks-correct-1-snap.png and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-some-zero-value-slice-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-three-layers-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-three-layers-visually-looks-correct-1-snap.png index 4e8f2d92b979..b537e7bc68e1 100644 Binary files a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-three-layers-visually-looks-correct-1-snap.png and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-three-layers-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-two-layers-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-two-layers-visually-looks-correct-1-snap.png index 0ddea4c30d8b..521fc020481c 100644 Binary files a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-two-layers-visually-looks-correct-1-snap.png and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-two-layers-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-visually-looks-correct-1-snap.png index acedfecdc654..ed9d68440d75 100644 Binary files a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-visually-looks-correct-1-snap.png and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-with-categorical-color-palette-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-with-categorical-color-palette-visually-looks-correct-1-snap.png index b7b8d7ad6caa..bd5190bd82ad 100644 Binary files a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-with-categorical-color-palette-visually-looks-correct-1-snap.png and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-with-categorical-color-palette-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-with-fill-labels-visually-looks-correct-1-snap.png b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-with-fill-labels-visually-looks-correct-1-snap.png index b48e2d814dca..b6485261e977 100644 Binary files a/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-with-fill-labels-visually-looks-correct-1-snap.png and b/packages/osd-charts/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-with-fill-labels-visually-looks-correct-1-snap.png differ diff --git a/packages/osd-charts/src/chart_types/partition_chart/layout/config/config.ts b/packages/osd-charts/src/chart_types/partition_chart/layout/config/config.ts index 40edc4ae7e9b..c57d5b64364b 100644 --- a/packages/osd-charts/src/chart_types/partition_chart/layout/config/config.ts +++ b/packages/osd-charts/src/chart_types/partition_chart/layout/config/config.ts @@ -60,6 +60,25 @@ export function percentFormatter(d: number): string { return `${Math.round(d)}%`; } +const fontSettings = { + fontFamily: { + dflt: 'Sans-Serif', + type: 'string', + }, + fontSize: { dflt: 12, min: 4, max: 32, type: 'number' }, + fontStyle: { + dflt: 'normal', + type: 'string', + values: FONT_STYLES, + }, + fontVariant: { + dflt: 'normal', + type: 'string', + values: FONT_VARIANTS, + }, + fontWeight: { dflt: 400, min: 100, max: 900, type: 'number' }, +}; + const valueFont = { type: 'group', values: { @@ -70,17 +89,9 @@ const valueFont = { type: 'string', }, */ - fontWeight: { dflt: 400, min: 100, max: 900, type: 'number' }, - fontStyle: { - dflt: 'normal', - type: 'string', - values: FONT_STYLES, - }, - fontVariant: { - dflt: 'normal', - type: 'string', - values: FONT_VARIANTS, - }, + fontWeight: fontSettings.fontWeight, + fontStyle: fontSettings.fontStyle, + fontVariant: fontSettings.fontVariant, }, }; @@ -161,17 +172,7 @@ export const configMetadata = { values: { textColor: { dflt: '#000000', type: 'color' }, textInvertible: { dflt: false, type: 'boolean' }, - fontWeight: { dflt: 400, min: 100, max: 900, type: 'number' }, - fontStyle: { - dflt: 'normal', - type: 'string', - values: FONT_STYLES, - }, - fontVariant: { - dflt: 'normal', - type: 'string', - values: FONT_VARIANTS, - }, + ...fontSettings, valueGetter: { dflt: sumValueGetter, type: 'function', @@ -196,7 +197,7 @@ export const configMetadata = { reconfigurable: true, documentation: 'Uses linked labels below this limit of the outer sector arc length (in pixels)', }, - fontSize: { dflt: 12, min: 4, max: 32, type: 'number' }, + ...fontSettings, gap: { dflt: 10, min: 6, max: 16, type: 'number' }, spacing: { dflt: 2, min: 0, max: 16, type: 'number' }, horizontalStemLength: { dflt: 10, min: 6, max: 16, type: 'number' }, @@ -233,6 +234,7 @@ export const configMetadata = { // other backgroundColor: { dflt: '#ffffff', type: 'color' }, sectorLineWidth: { dflt: 1, min: 0, max: 4, type: 'number' }, + sectorLineStroke: { dflt: 'white', type: 'string' }, colors: { dflt: 'turbo', type: 'palette', values: Object.keys(palettes) }, palettes: { dflt: palettes, type: 'palettes', reconfigurable: false }, }; diff --git a/packages/osd-charts/src/chart_types/partition_chart/layout/types/config_types.ts b/packages/osd-charts/src/chart_types/partition_chart/layout/types/config_types.ts index 5af1490afb4d..81ba72a1967f 100644 --- a/packages/osd-charts/src/chart_types/partition_chart/layout/types/config_types.ts +++ b/packages/osd-charts/src/chart_types/partition_chart/layout/types/config_types.ts @@ -19,7 +19,7 @@ import { Distance, Pixels, Radian, Radius, Ratio, SizeRatio, TimeMs } from './geometry_types'; import { Font, FontFamily, PartialFont } from './types'; import { $Values as Values } from 'utility-types'; -import { Color, ValueFormatter } from '../../../../utils/commons'; +import { Color, StrokeStyle, ValueFormatter } from '../../../../utils/commons'; export const PartitionLayout = Object.freeze({ sunburst: 'sunburst' as 'sunburst', @@ -86,9 +86,10 @@ export interface StaticConfig { // linked labels (primarily: single-line) linkLabel: LinkLabelConfig; - // other + // global backgroundColor: Color; sectorLineWidth: Pixels; + sectorLineStroke: StrokeStyle; } export type EasingFunction = (x: Ratio) => Ratio; diff --git a/packages/osd-charts/src/chart_types/partition_chart/layout/types/viewmodel_types.ts b/packages/osd-charts/src/chart_types/partition_chart/layout/types/viewmodel_types.ts index db57301f7306..5d119719ce9c 100644 --- a/packages/osd-charts/src/chart_types/partition_chart/layout/types/viewmodel_types.ts +++ b/packages/osd-charts/src/chart_types/partition_chart/layout/types/viewmodel_types.ts @@ -30,7 +30,10 @@ export type LinkLabelVM = { text: string; valueText: string; width: Distance; + valueWidth: Distance; verticalOffset: Distance; + labelFontSpec: Font; + valueFontSpec: Font; }; export interface RowBox extends Font { @@ -65,6 +68,7 @@ export interface RowSet { export interface QuadViewModel extends ShapeTreeNode { strokeWidth: number; + strokeStyle: string; fillColor: string; } diff --git a/packages/osd-charts/src/chart_types/partition_chart/layout/viewmodel/link_text_layout.ts b/packages/osd-charts/src/chart_types/partition_chart/layout/viewmodel/link_text_layout.ts index 7436d448f966..fb7b174985b4 100644 --- a/packages/osd-charts/src/chart_types/partition_chart/layout/viewmodel/link_text_layout.ts +++ b/packages/osd-charts/src/chart_types/partition_chart/layout/viewmodel/link_text_layout.ts @@ -70,9 +70,26 @@ export function linkTextLayout( const stemToX = x + north * west * cy - west * relativeY; const stemToY = cy; const text = rawTextGetter(node); - const { width, emHeightAscent, emHeightDescent } = measure(linkLabel.fontSize, [ - { fontFamily: config.fontFamily, ...linkLabel, text }, - ])[0]; + const valueText = valueFormatter(valueGetter(node)); + const labelFontSpec = { + fontStyle: 'normal', + fontVariant: 'normal', + fontFamily: config.fontFamily, + fontWeight: 'normal', + ...linkLabel, + text, + }; + const valueFontSpec = { + fontStyle: 'normal', + fontVariant: 'normal', + fontFamily: config.fontFamily, + fontWeight: 'normal', + ...linkLabel, + ...linkLabel.valueFont, + text: valueText, + }; + const { width, emHeightAscent, emHeightDescent } = measure(linkLabel.fontSize, [labelFontSpec])[0]; + const { width: valueWidth } = measure(linkLabel.fontSize, [valueFontSpec])[0]; return { link: [ [x0, y0], @@ -83,9 +100,12 @@ export function linkTextLayout( translate: [stemToX + west * (linkLabel.horizontalStemLength + linkLabel.gap), stemToY], textAlign: side ? 'left' : 'right', text, - valueText: valueFormatter(valueGetter(node)), + valueText, width, + valueWidth, verticalOffset: -(emHeightDescent + emHeightAscent) / 2, // meaning, `middle` + labelFontSpec, + valueFontSpec, }; }); } 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 9e2cd0a8084c..983b341a6ae7 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 @@ -62,7 +62,7 @@ import { parentAccessor, sortIndexAccessor, } from '../utils/group_by_rollup'; -import { ValueAccessor, ValueFormatter } from '../../../../utils/commons'; +import { StrokeStyle, ValueAccessor, ValueFormatter } from '../../../../utils/commons'; import { percentValueGetter } from '../config/config'; function paddingAccessor(n: ArrayEntry) { @@ -99,6 +99,7 @@ export function makeQuadViewModel( childNodes: ShapeTreeNode[], layers: Layer[], sectorLineWidth: Pixels, + sectorLineStroke: StrokeStyle, ): Array { return childNodes.map((node) => { const opacityMultiplier = 1; // could alter in the future, eg. in response to interactions @@ -109,7 +110,8 @@ export function makeQuadViewModel( const { r, g, b, opacity } = stringToRGB(shapeFillColor); const fillColor = argsToRGBString(r, g, b, opacity * opacityMultiplier); const strokeWidth = sectorLineWidth; - return { strokeWidth, fillColor, ...node }; + const strokeStyle = sectorLineStroke; + return { strokeWidth, strokeStyle, fillColor, ...node }; }); } @@ -166,6 +168,7 @@ export function shapeViewModel( specialFirstInnermostSector, minFontSize, partitionLayout, + sectorLineWidth, } = config; const innerWidth = width * (1 - Math.min(1, margin.left + margin.right)); @@ -222,7 +225,7 @@ export function shapeViewModel( // use the smaller of the two sizes, as a circle fits into a square const circleMaximumSize = Math.min(innerWidth, innerHeight); - const outerRadius: Radius = (outerSizeRatio * circleMaximumSize) / 2; + const outerRadius: Radius = Math.min(outerSizeRatio * circleMaximumSize, circleMaximumSize - sectorLineWidth) / 2; const innerRadius: Radius = outerRadius - (1 - emptySizeRatio) * outerRadius; const treeHeight = shownChildNodes.reduce((p: number, n: any) => Math.max(p, entryValue(n.node).depth), 0); // 1: pie, 2: two-ring donut etc. const ringThickness = (outerRadius - innerRadius) / treeHeight; @@ -249,6 +252,7 @@ export function shapeViewModel( ), layers, config.sectorLineWidth, + config.sectorLineStroke, ); // fill text diff --git a/packages/osd-charts/src/chart_types/partition_chart/renderer/canvas/canvas_renderers.ts b/packages/osd-charts/src/chart_types/partition_chart/renderer/canvas/canvas_renderers.ts index 13260a45cfa0..71c1641615b3 100644 --- a/packages/osd-charts/src/chart_types/partition_chart/renderer/canvas/canvas_renderers.ts +++ b/packages/osd-charts/src/chart_types/partition_chart/renderer/canvas/canvas_renderers.ts @@ -29,7 +29,7 @@ import { import { TAU } from '../../layout/utils/math'; import { PartitionLayout } from '../../layout/types/config_types'; import { cssFontShorthand } from '../../layout/utils/measure'; -import { withContext, renderLayers, clearCanvas } from '../../../../renderers/canvas'; +import { clearCanvas, renderLayers, withContext } from '../../../../renderers/canvas'; // 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 @@ -62,7 +62,7 @@ function renderRowSets(ctx: CanvasRenderingContext2D, rowSets: RowSet[]) { function renderTaperedBorder( ctx: CanvasRenderingContext2D, - { strokeWidth, fillColor, x0, x1, y0px, y1px }: QuadViewModel, + { strokeWidth, strokeStyle, fillColor, x0, x1, y0px, y1px }: QuadViewModel, ) { const X0 = x0 - TAU / 4; const X1 = x1 - TAU / 4; @@ -89,18 +89,19 @@ function renderTaperedBorder( ctx.arc(0, 0, y0px, X1, X0, true); ctx.stroke(); - ctx.fillStyle = 'white'; + ctx.fillStyle = strokeStyle; // each side (radial 'line') is modeled as a pentagon (some lines can be short arcs though) ctx.beginPath(); const yThreshold = Math.max(TAPER_OFF_LIMIT, (LINE_WIDTH_MULT * strokeWidth) / (X1 - X0)); const beta = strokeWidth / yThreshold; // angle where strokeWidth equals the lineWidthMult limit at a radius of yThreshold ctx.arc(0, 0, y0px, X0, X0 + beta * (yThreshold / y0px)); - ctx.arc(0, 0, yThreshold, X0 + beta, X0 + beta); + ctx.arc(0, 0, Math.min(yThreshold, y1px), X0 + beta, X0 + beta); ctx.arc(0, 0, y1px, X0 + beta * (yThreshold / y1px), X0, true); ctx.arc(0, 0, y0px, X0, X0); ctx.fill(); } else { + ctx.strokeStyle = strokeStyle; ctx.stroke(); } } @@ -164,28 +165,41 @@ function renderLinkLabels( ctx: CanvasRenderingContext2D, linkLabelFontSize: Pixels, linkLabelLineWidth: Pixels, - fontFamily: string, linkLabelTextColor: string, viewModels: LinkLabelVM[], ) { + const labelValueGap = linkLabelFontSize / 2; // one en space withContext(ctx, (ctx) => { ctx.lineWidth = linkLabelLineWidth; ctx.strokeStyle = linkLabelTextColor; ctx.fillStyle = linkLabelTextColor; - ctx.font = `${400} ${linkLabelFontSize}px ${fontFamily}`; - viewModels.forEach(({ link, translate, textAlign, text, valueText }: LinkLabelVM) => { - ctx.beginPath(); - ctx.moveTo(...link[0]); - link.slice(1).forEach((point) => ctx.lineTo(...point)); - ctx.stroke(); - withContext(ctx, (ctx) => { - ctx.translate(...translate); - ctx.scale(1, -1); // flip for text rendering not to be upside down - ctx.textAlign = textAlign; - // only use a colon if both text and valueText are non-zero length strings - ctx.fillText(text + (text && valueText ? ': ' : '') + valueText, 0, 0); - }); - }); + viewModels.forEach( + ({ + link, + translate, + textAlign, + text, + valueText, + width, + labelFontSpec, + valueFontSpec, + valueWidth, + }: LinkLabelVM) => { + ctx.beginPath(); + ctx.moveTo(...link[0]); + link.slice(1).forEach((point) => ctx.lineTo(...point)); + ctx.stroke(); + withContext(ctx, (ctx) => { + ctx.translate(...translate); + ctx.scale(1, -1); // flip for text rendering not to be upside down + ctx.textAlign = textAlign; + ctx.font = `${labelFontSpec.fontStyle} ${labelFontSpec.fontVariant} ${labelFontSpec.fontWeight} ${linkLabelFontSize}px ${labelFontSpec.fontFamily}`; + ctx.fillText(text, textAlign === 'right' ? -valueWidth - labelValueGap : 0, 0); + ctx.font = `${valueFontSpec.fontStyle} ${valueFontSpec.fontVariant} ${valueFontSpec.fontWeight} ${linkLabelFontSize}px ${valueFontSpec.fontFamily}`; + ctx.fillText(valueText, textAlign === 'left' ? width + labelValueGap : 0, 0); + }); + }, + ); }); } @@ -195,7 +209,7 @@ export function renderPartitionCanvas2d( dpr: number, { config, quadViewModel, rowSets, outsideLinksViewModel, linkLabelViewModels, diskCenter }: ShapeViewModel, ) { - const { sectorLineWidth, linkLabel, fontFamily /*, backgroundColor*/ } = config; + const { sectorLineWidth, sectorLineStroke, linkLabel /*, backgroundColor*/ } = config; const linkLabelTextColor = addOpacity(linkLabel.textColor, linkLabel.textOpacity); @@ -219,7 +233,7 @@ export function renderPartitionCanvas2d( ctx.scale(1, -1); ctx.lineJoin = 'round'; - ctx.strokeStyle = 'white'; // todo make it configurable just like sectorLineWidth + ctx.strokeStyle = sectorLineStroke; ctx.lineWidth = sectorLineWidth; // painter's algorithm, like that of SVG: the sequence determines what overdraws what; first element of the array is drawn first @@ -245,14 +259,7 @@ export function renderPartitionCanvas2d( // all the text and link lines for single-row outside texts (ctx: CanvasRenderingContext2D) => - renderLinkLabels( - ctx, - linkLabel.fontSize, - linkLabel.lineWidth, - fontFamily, - linkLabelTextColor, - linkLabelViewModels, - ), + renderLinkLabels(ctx, linkLabel.fontSize, linkLabel.lineWidth, linkLabelTextColor, linkLabelViewModels), ]); }); } diff --git a/packages/osd-charts/src/utils/commons.ts b/packages/osd-charts/src/utils/commons.ts index 6521c3471e17..3838e64f8e92 100644 --- a/packages/osd-charts/src/utils/commons.ts +++ b/packages/osd-charts/src/utils/commons.ts @@ -24,6 +24,7 @@ export type Datum = any; // unknown; export type Rotation = 0 | 90 | -90 | 180; export type Rendering = 'canvas' | 'svg'; export type Color = string; +export type StrokeStyle = Color; // now narrower than string | CanvasGradient | CanvasPattern export const Position = Object.freeze({ Top: 'top' as 'top', diff --git a/packages/osd-charts/stories/sunburst/29_custom_stroke.tsx b/packages/osd-charts/stories/sunburst/29_custom_stroke.tsx new file mode 100644 index 000000000000..7183bcf0b6b2 --- /dev/null +++ b/packages/osd-charts/stories/sunburst/29_custom_stroke.tsx @@ -0,0 +1,50 @@ +/* + * 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 { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { countryLookup, indexInterpolatedFillColor, interpolatorCET2s } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.origin, + nodeLabel: (d: Datum) => countryLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.sunburst, + linkLabel: { maxCount: 15, textColor: 'white' }, + sectorLineStroke: 'rgb(26, 27, 32)', // same as the dark theme + sectorLineWidth: 1.2, + }} + /> + +); diff --git a/packages/osd-charts/stories/sunburst/2_value_formatted.tsx b/packages/osd-charts/stories/sunburst/2_value_formatted.tsx index 860933d5534e..cccbd02d47ec 100644 --- a/packages/osd-charts/stories/sunburst/2_value_formatted.tsx +++ b/packages/osd-charts/stories/sunburst/2_value_formatted.tsx @@ -48,7 +48,7 @@ export const example = () => ( }, }, ]} - config={{ outerSizeRatio: 0.9 }} + config={{ outerSizeRatio: 0.9, linkLabel: { fontStyle: 'italic', valueFont: { fontWeight: 900 } } }} /> ); diff --git a/packages/osd-charts/stories/sunburst/30_largest_circle.tsx b/packages/osd-charts/stories/sunburst/30_largest_circle.tsx new file mode 100644 index 000000000000..c9e00c70298b --- /dev/null +++ b/packages/osd-charts/stories/sunburst/30_largest_circle.tsx @@ -0,0 +1,51 @@ +/* + * 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 { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.sunburst, + linkLabel: { maximumSection: Infinity, maxCount: 0 }, + sectorLineWidth: 10, + sectorLineStroke: 'lightgrey', + outerSizeRatio: 1, + }} + /> + +); diff --git a/packages/osd-charts/stories/sunburst/31_bold_link_value.tsx b/packages/osd-charts/stories/sunburst/31_bold_link_value.tsx new file mode 100644 index 000000000000..4032bab95be1 --- /dev/null +++ b/packages/osd-charts/stories/sunburst/31_bold_link_value.tsx @@ -0,0 +1,45 @@ +/* + * 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 { Chart, Datum, Partition } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true, valueFont: { fontWeight: 900, fontStyle: 'italic' } }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ linkLabel: { valueFont: { fontWeight: 900, fontStyle: 'italic' } } }} + /> + +); diff --git a/packages/osd-charts/stories/sunburst/sunburst.stories.tsx b/packages/osd-charts/stories/sunburst/sunburst.stories.tsx index d27b3649f261..8cc0282a0b58 100644 --- a/packages/osd-charts/stories/sunburst/sunburst.stories.tsx +++ b/packages/osd-charts/stories/sunburst/sunburst.stories.tsx @@ -55,3 +55,6 @@ export { example as noLabels } from './25_no_labels'; export { example as percentage } from './26_percentage'; export { example as heterogeneous } from './27_heterogeneous_depth'; export { example as notANumber } from './28_not_a_number'; +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';