Upgrade elastic charts v43.1.1 (#121593)

This commit is contained in:
Nick Partridge 2022-01-27 05:15:33 -06:00 committed by GitHub
parent 9575ebbcaf
commit 71e9c609fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 1150 additions and 953 deletions

View file

@ -103,7 +103,7 @@
"@elastic/apm-rum": "^5.10.1",
"@elastic/apm-rum-react": "^1.3.3",
"@elastic/apm-synthtrace": "link:bazel-bin/packages/elastic-apm-synthtrace",
"@elastic/charts": "40.2.0",
"@elastic/charts": "43.1.1",
"@elastic/datemath": "link:bazel-bin/packages/elastic-datemath",
"@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.1.0-canary.2",
"@elastic/ems-client": "8.0.0",

View file

@ -4,11 +4,11 @@ exports[`GaugeComponent renders the chart 1`] = `
<Chart
renderer="canvas"
>
<Connect(SpecInstance)
<Settings
debugState={false}
theme={Object {}}
/>
<Connect(SpecInstance)
<Component
actual={3}
bandFillColor={[Function]}
bands={

View file

@ -20,6 +20,7 @@ import {
TooltipProps,
ESFixedIntervalUnit,
ESCalendarIntervalUnit,
PartialTheme,
} from '@elastic/charts';
import type { CustomPaletteState } from '../../../../charts/public';
import { search } from '../../../../data/public';
@ -381,62 +382,61 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = memo(
}
};
const config: HeatmapSpec['config'] = {
grid: {
stroke: {
width:
args.gridConfig.strokeWidth ?? chartTheme.axes?.gridLine?.horizontal?.strokeWidth ?? 1,
color:
args.gridConfig.strokeColor ??
chartTheme.axes?.gridLine?.horizontal?.stroke ??
'#D3DAE6',
},
cellHeight: {
max: 'fill',
min: 1,
const themeOverrides: PartialTheme = {
legend: {
labelOptions: {
maxLines: args.legend.shouldTruncate ? args.legend?.maxLines ?? 1 : 0,
},
},
cell: {
maxWidth: 'fill',
maxHeight: 'fill',
label: {
visible: args.gridConfig.isCellLabelVisible ?? false,
minFontSize: 8,
maxFontSize: 18,
useGlobalMinFontSize: true, // override the min if there's a different directive upstream
heatmap: {
grid: {
stroke: {
width:
args.gridConfig.strokeWidth ??
chartTheme.axes?.gridLine?.horizontal?.strokeWidth ??
1,
color:
args.gridConfig.strokeColor ??
chartTheme.axes?.gridLine?.horizontal?.stroke ??
'#D3DAE6',
},
cellHeight: {
max: 'fill',
min: 1,
},
},
border: {
strokeWidth: 0,
cell: {
maxWidth: 'fill',
maxHeight: 'fill',
label: {
visible: args.gridConfig.isCellLabelVisible ?? false,
minFontSize: 8,
maxFontSize: 18,
useGlobalMinFontSize: true, // override the min if there's a different directive upstream
},
border: {
strokeWidth: 0,
},
},
yAxisLabel: {
visible: !!yAxisColumn && args.gridConfig.isYAxisLabelVisible,
// eui color subdued
textColor: chartTheme.axes?.tickLabel?.fill ?? '#6a717d',
padding: yAxisColumn?.name ? 8 : 0,
},
xAxisLabel: {
visible: Boolean(args.gridConfig.isXAxisLabelVisible && xAxisColumn),
// eui color subdued
textColor: chartTheme.axes?.tickLabel?.fill ?? `#6a717d`,
padding: xAxisColumn?.name ? 8 : 0,
},
brushMask: {
fill: isDarkTheme ? 'rgb(30,31,35,80%)' : 'rgb(247,247,247,50%)',
},
brushArea: {
stroke: isDarkTheme ? 'rgb(255, 255, 255)' : 'rgb(105, 112, 125)',
},
},
yAxisLabel: {
visible: !!yAxisColumn && args.gridConfig.isYAxisLabelVisible,
// eui color subdued
textColor: chartTheme.axes?.tickLabel?.fill ?? '#6a717d',
padding: yAxisColumn?.name ? 8 : 0,
name: yAxisColumn?.name ?? '',
...(yAxisColumn
? {
formatter: (v: number | string) =>
`${formatFactory(yAxisColumn.meta.params).convert(v) ?? ''}`,
}
: {}),
},
xAxisLabel: {
visible: Boolean(args.gridConfig.isXAxisLabelVisible && xAxisColumn),
// eui color subdued
textColor: chartTheme.axes?.tickLabel?.fill ?? `#6a717d`,
padding: xAxisColumn?.name ? 8 : 0,
formatter: (v: number | string) => `${xValuesFormatter.convert(v) ?? ''}`,
name: xAxisColumn?.name ?? '',
},
brushMask: {
fill: isDarkTheme ? 'rgb(30,31,35,80%)' : 'rgb(247,247,247,50%)',
},
brushArea: {
stroke: isDarkTheme ? 'rgb(255, 255, 255)' : 'rgb(105, 112, 125)',
},
timeZone,
};
return (
@ -456,14 +456,7 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = memo(
legendColorPicker={uiState ? legendColorPicker : undefined}
debugState={window._echDebugStateFlag ?? false}
tooltip={tooltip}
theme={{
...chartTheme,
legend: {
labelOptions: {
maxLines: args.legend.shouldTruncate ? args.legend?.maxLines ?? 1 : 0,
},
},
}}
theme={[themeOverrides, chartTheme]}
xDomain={{
min:
dateHistogramMeta && dateHistogramMeta.timeRange
@ -483,6 +476,7 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = memo(
type: 'bands',
bands,
}}
timeZone={timeZone}
data={chartData}
xAccessor={xAccessor}
yAccessor={yAccessor || 'unifiedY'}
@ -490,8 +484,15 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = memo(
valueFormatter={valueFormatter}
xScale={xScale}
ySortPredicate={yAxisColumn ? getSortPredicate(yAxisColumn) : 'dataIndex'}
config={config}
xSortPredicate={xAxisColumn ? getSortPredicate(xAxisColumn) : 'dataIndex'}
xAxisLabelName={xAxisColumn?.name}
yAxisLabelName={yAxisColumn?.name}
xAxisLabelFormatter={(v) => `${xValuesFormatter.convert(v) ?? ''}`}
yAxisLabelFormatter={
yAxisColumn
? (v) => `${formatFactory(yAxisColumn.meta.params).convert(v) ?? ''}`
: undefined
}
/>
</Chart>
</>

View file

@ -18,6 +18,7 @@ import {
TooltipProps,
TooltipType,
SeriesIdentifier,
PartitionLayout,
} from '@elastic/charts';
import { useEuiTheme } from '@elastic/eui';
import {
@ -47,7 +48,7 @@ import {
canFilter,
getFilterClickData,
getFilterEventData,
getConfig,
getPartitionTheme,
getColumns,
getSplitDimensionAccessor,
getColumnByAccessor,
@ -251,8 +252,8 @@ const PieComponent = (props: PieComponentProps) => {
return 1;
}, [visData.rows, metricColumn]);
const config = useMemo(
() => getConfig(visParams, chartTheme, dimensions, rescaleFactor),
const themeOverrides = useMemo(
() => getPartitionTheme(visParams, chartTheme, dimensions, rescaleFactor),
[chartTheme, visParams, dimensions, rescaleFactor]
);
const tooltip: TooltipProps = {
@ -369,7 +370,9 @@ const PieComponent = (props: PieComponentProps) => {
)}
theme={[
// Chart background should be transparent for the usage at Canvas.
{ ...chartTheme, background: { color: 'transparent' } },
{ background: { color: 'transparent' } },
themeOverrides,
chartTheme,
{
legend: {
labelOptions: {
@ -385,6 +388,8 @@ const PieComponent = (props: PieComponentProps) => {
id="pie"
smallMultiples={SMALL_MULTIPLES_ID}
data={visData.rows}
layout={PartitionLayout.sunburst}
specialFirstInnermostSector={false}
valueAccessor={(d: Datum) => getSliceValue(d, metricColumn)}
percentFormatter={(d: number) => percentFormatter.convert(d / 100)}
valueGetter={
@ -400,7 +405,6 @@ const PieComponent = (props: PieComponentProps) => {
: metricFieldFormatter.convert(d)
}
layers={layers}
config={config}
topGroove={!visParams.labels.show ? 0 : undefined}
/>
</Chart>

View file

@ -1,81 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { PartitionConfig, PartitionLayout, RecursivePartial, Theme } from '@elastic/charts';
import { LabelPositions, PieVisParams, PieContainerDimensions } from '../../common/types';
const MAX_SIZE = 1000;
export const getConfig = (
visParams: PieVisParams,
chartTheme: RecursivePartial<Theme>,
dimensions?: PieContainerDimensions,
rescaleFactor: number = 1
): RecursivePartial<PartitionConfig> => {
// On small multiples we want the labels to only appear inside
const isSplitChart = Boolean(visParams.dimensions.splitColumn || visParams.dimensions.splitRow);
const usingMargin =
dimensions && !isSplitChart
? {
margin: {
top: (1 - Math.min(1, MAX_SIZE / dimensions?.height)) / 2,
bottom: (1 - Math.min(1, MAX_SIZE / dimensions?.height)) / 2,
left: (1 - Math.min(1, MAX_SIZE / dimensions?.width)) / 2,
right: (1 - Math.min(1, MAX_SIZE / dimensions?.width)) / 2,
},
}
: null;
const usingOuterSizeRatio =
dimensions && !isSplitChart
? {
outerSizeRatio:
// Cap the ratio to 1 and then rescale
rescaleFactor * Math.min(MAX_SIZE / Math.min(dimensions?.width, dimensions?.height), 1),
}
: null;
const config: RecursivePartial<PartitionConfig> = {
partitionLayout: PartitionLayout.sunburst,
fontFamily: chartTheme.barSeriesStyle?.displayValue?.fontFamily,
...usingOuterSizeRatio,
specialFirstInnermostSector: false,
minFontSize: 10,
maxFontSize: 16,
linkLabel: {
maxCount: 5,
fontSize: 11,
textColor: chartTheme.axes?.axisTitle?.fill,
maxTextLength: visParams.labels.truncate ?? undefined,
},
sectorLineStroke: chartTheme.lineSeriesStyle?.point?.fill,
sectorLineWidth: 1.5,
circlePadding: 4,
emptySizeRatio: visParams.isDonut ? visParams.emptySizeRatio : 0,
...usingMargin,
};
if (!visParams.labels.show) {
// Force all labels to be linked, then prevent links from showing
config.linkLabel = { maxCount: 0, maximumSection: Number.POSITIVE_INFINITY };
}
if (visParams.labels.last_level && visParams.labels.show) {
config.linkLabel = {
maxCount: Number.POSITIVE_INFINITY,
maximumSection: Number.POSITIVE_INFINITY,
maxTextLength: visParams.labels.truncate ?? undefined,
};
}
if (
(visParams.labels.position === LabelPositions.INSIDE || isSplitChart) &&
visParams.labels.show
) {
config.linkLabel = { maxCount: 0 };
}
return config;
};

View file

@ -6,13 +6,7 @@
* Side Public License, v 1.
*/
import {
Datum,
PartitionFillLabel,
PartitionLayer,
ShapeTreeNode,
ArrayEntry,
} from '@elastic/charts';
import { Datum, PartitionLayer, ShapeTreeNode, ArrayEntry } from '@elastic/charts';
import { isEqual } from 'lodash';
import type { FieldFormatsStart } from 'src/plugins/field_formats/public';
import { SeriesLayer, PaletteRegistry, lightenColor } from '../../../../charts/public';
@ -137,7 +131,7 @@ export const getLayers = (
formatter: FieldFormatsStart,
syncColors: boolean
): PartitionLayer[] => {
const fillLabel: Partial<PartitionFillLabel> = {
const fillLabel: PartitionLayer['fillLabel'] = {
valueFont: {
fontWeight: 700,
},

View file

@ -6,19 +6,21 @@
* Side Public License, v 1.
*/
import { getConfig } from './get_config';
import { getPartitionTheme } from './get_partition_theme';
import { createMockPieParams } from '../mocks';
const visParams = createMockPieParams();
describe('getConfig', () => {
it('should cap the outerSizeRatio to 1', () => {
expect(getConfig(visParams, {}, { width: 400, height: 400 }).outerSizeRatio).toBe(1);
expect(
getPartitionTheme(visParams, {}, { width: 400, height: 400 }).partition?.outerSizeRatio
).toBe(1);
});
it('should not have outerSizeRatio for split chart', () => {
expect(
getConfig(
getPartitionTheme(
{
...visParams,
dimensions: {
@ -37,11 +39,11 @@ describe('getConfig', () => {
},
{},
{ width: 400, height: 400 }
).outerSizeRatio
).partition?.outerSizeRatio
).toBeUndefined();
expect(
getConfig(
getPartitionTheme(
{
...visParams,
dimensions: {
@ -60,11 +62,11 @@ describe('getConfig', () => {
},
{},
{ width: 400, height: 400 }
).outerSizeRatio
).partition?.outerSizeRatio
).toBeUndefined();
});
it('should not set outerSizeRatio if dimensions are not defined', () => {
expect(getConfig(visParams, {}).outerSizeRatio).toBeUndefined();
expect(getPartitionTheme(visParams, {}).partition?.outerSizeRatio).toBeUndefined();
});
});

View file

@ -0,0 +1,85 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { PartialTheme } from '@elastic/charts';
import { Required } from '@kbn/utility-types';
import { LabelPositions, PieVisParams, PieContainerDimensions } from '../../common/types';
const MAX_SIZE = 1000;
export const getPartitionTheme = (
visParams: PieVisParams,
chartTheme: PartialTheme,
dimensions?: PieContainerDimensions,
rescaleFactor: number = 1
): PartialTheme => {
// On small multiples we want the labels to only appear inside
const isSplitChart = Boolean(visParams.dimensions.splitColumn || visParams.dimensions.splitRow);
const paddingProps: PartialTheme | null =
dimensions && !isSplitChart
? {
chartPaddings: {
// TODO: simplify ratio logic to be static px units
top: ((1 - Math.min(1, MAX_SIZE / dimensions?.height)) / 2) * dimensions?.height,
bottom: ((1 - Math.min(1, MAX_SIZE / dimensions?.height)) / 2) * dimensions?.height,
left: ((1 - Math.min(1, MAX_SIZE / dimensions?.width)) / 2) * dimensions?.height,
right: ((1 - Math.min(1, MAX_SIZE / dimensions?.width)) / 2) * dimensions?.height,
},
}
: null;
const outerSizeRatio: PartialTheme['partition'] | null =
dimensions && !isSplitChart
? {
outerSizeRatio:
// Cap the ratio to 1 and then rescale
rescaleFactor * Math.min(MAX_SIZE / Math.min(dimensions?.width, dimensions?.height), 1),
}
: null;
const theme: Required<PartialTheme, 'partition'> = {
chartMargins: { top: 0, bottom: 0, left: 0, right: 0 },
...paddingProps,
partition: {
fontFamily: chartTheme.barSeriesStyle?.displayValue?.fontFamily,
...outerSizeRatio,
minFontSize: 10,
maxFontSize: 16,
linkLabel: {
maxCount: 5,
fontSize: 11,
textColor: chartTheme.axes?.axisTitle?.fill,
maxTextLength: visParams.labels.truncate ?? undefined,
},
sectorLineStroke: chartTheme.lineSeriesStyle?.point?.fill,
sectorLineWidth: 1.5,
circlePadding: 4,
emptySizeRatio: visParams.isDonut ? visParams.emptySizeRatio : 0,
},
};
if (!visParams.labels.show) {
// Force all labels to be linked, then prevent links from showing
theme.partition.linkLabel = { maxCount: 0, maximumSection: Number.POSITIVE_INFINITY };
}
if (visParams.labels.last_level && visParams.labels.show) {
theme.partition.linkLabel = {
maxCount: Number.POSITIVE_INFINITY,
maximumSection: Number.POSITIVE_INFINITY,
maxTextLength: visParams.labels.truncate ?? undefined,
};
}
if (
(visParams.labels.position === LabelPositions.INSIDE || isSplitChart) &&
visParams.labels.show
) {
theme.partition.linkLabel = { maxCount: 0 };
}
return theme;
};

View file

@ -10,7 +10,7 @@ export { getLayers } from './get_layers';
export { getColorPicker } from './get_color_picker';
export { getLegendActions } from './get_legend_actions';
export { canFilter, getFilterClickData, getFilterEventData } from './filter_helpers';
export { getConfig } from './get_config';
export { getPartitionTheme } from './get_partition_theme';
export { getColumns } from './get_columns';
export { getSplitDimensionAccessor } from './get_split_dimension_accessor';
export { getDistinctSeries } from './get_distinct_series';

View file

@ -12,11 +12,11 @@ import { take } from 'rxjs/operators';
import { renderHook, act } from '@testing-library/react-hooks';
import { render, act as renderAct } from '@testing-library/react';
import { LIGHT_THEME, DARK_THEME } from '@elastic/charts';
import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme';
import { ThemeService } from './theme';
import { coreMock } from '../../../../../core/public/mocks';
import { LIGHT_THEME, DARK_THEME } from '@elastic/charts';
const { uiSettings: setupMockUiSettings } = coreMock.createSetup();

View file

@ -89,9 +89,8 @@ export class ThemeService {
public init(uiSettings: CoreSetup['uiSettings']) {
this._uiSettingsDarkMode$ = uiSettings.get$<boolean>('theme:darkMode');
this._uiSettingsDarkMode$.subscribe((darkMode) => {
this._chartsTheme$.next(
darkMode ? EUI_CHARTS_THEME_DARK.theme : EUI_CHARTS_THEME_LIGHT.theme
);
const theme = darkMode ? EUI_CHARTS_THEME_DARK.theme : EUI_CHARTS_THEME_LIGHT.theme;
this._chartsTheme$.next(theme);
this._chartsBaseTheme$.next(darkMode ? DARK_THEME : LIGHT_THEME);
});
}

View file

@ -28,19 +28,21 @@ export interface BrushTriggerEvent {
data: RangeSelectContext['data'];
}
type AllSeriesAccessors = Array<[accessor: Accessor | AccessorFn, value: string | number]>;
type AllSeriesAccessors<D = any> = Array<
[accessor: Accessor<D> | AccessorFn<D>, value: string | number]
>;
/**
* returns accessor value from string or function accessor
* @param datum
* @param accessor
*/
function getAccessorValue(datum: Datum, accessor: Accessor | AccessorFn) {
function getAccessorValue<D>(datum: D, accessor: Accessor<D> | AccessorFn<D>) {
if (typeof accessor === 'function') {
return accessor(datum);
}
return datum[accessor];
return (datum as Datum)[accessor];
}
/**
@ -259,9 +261,11 @@ export const getFilterFromSeriesFn =
/**
* Helper function to transform `@elastic/charts` brush event into brush action event
*/
export const getBrushFromChartBrushEventFn =
(table: Datatable, xAccessor: Accessor | AccessorFn) =>
({ x: selectedRange }: XYBrushEvent): BrushTriggerEvent => {
export function getBrushFromChartBrushEventFn<D = never>(
table: Datatable,
xAccessor: Accessor<D> | AccessorFn<D>
) {
return ({ x: selectedRange }: XYBrushEvent): BrushTriggerEvent => {
const [start, end] = selectedRange ?? [0, 0];
const range: [number, number] = [start, end];
const column = table.columns.findIndex(({ id }) => validateAccessorId(id, xAccessor));
@ -275,3 +279,4 @@ export const getBrushFromChartBrushEventFn =
name: 'brush',
};
};
}

View file

@ -38,30 +38,32 @@ const getPointFillColor = (points: VisSeries['points'], color: string | undefine
);
};
const getAreaSeriesStyle = ({ color, lines, points }: AreaSeriesComponentProps['visData']) =>
({
line: {
opacity: isShowLines(lines, points) ? 1 : 0,
stroke: color,
strokeWidth: lines?.lineWidth !== undefined ? Number(lines.lineWidth) : 3,
visible: isShowLines(lines, points),
},
area: {
fill: color,
opacity: lines?.fill ?? 0,
visible: lines?.show ?? points?.show ?? true,
},
point: {
fill: getPointFillColor(points, color),
opacity: 1,
radius: points?.radius ?? 3,
stroke: color,
strokeWidth: points?.lineWidth ?? 2,
visible: points?.show ?? false,
shape: points?.symbol === 'cross' ? PointShape.X : points?.symbol,
},
curve: lines?.steps ? CurveType.CURVE_STEP : CurveType.LINEAR,
} as RecursivePartial<AreaSeriesStyle>);
const getAreaSeriesStyle = ({
color,
lines,
points,
}: AreaSeriesComponentProps['visData']): RecursivePartial<AreaSeriesStyle> => ({
line: {
opacity: isShowLines(lines, points) ? 1 : 0,
stroke: color,
strokeWidth: lines?.lineWidth !== undefined ? Number(lines.lineWidth) : 3,
visible: isShowLines(lines, points),
},
area: {
fill: color,
opacity: lines?.fill ?? 0,
visible: lines?.show ?? points?.show ?? true,
},
point: {
fill: getPointFillColor(points, color),
opacity: 1,
radius: points?.radius ?? 3,
stroke: color,
strokeWidth: points?.lineWidth ?? 2,
visible: points?.show ?? false,
shape: points?.symbol === 'cross' ? PointShape.X : points?.symbol,
},
});
export const AreaSeriesComponent = ({ index, groupId, visData }: AreaSeriesComponentProps) => (
<AreaSeries
@ -75,6 +77,7 @@ export const AreaSeriesComponent = ({ index, groupId, visData }: AreaSeriesCompo
data={visData._hide ? [] : visData.data}
sortIndex={index}
color={visData.color}
curve={visData.lines?.steps ? CurveType.CURVE_STEP : CurveType.LINEAR}
stackAccessors={visData.stack ? [0] : undefined}
areaSeriesStyle={getAreaSeriesStyle(visData)}
/>

View file

@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`src/legacy/core_plugins/metrics/public/visualizations/views/timeseries/decorators/area_decorator.js <AreaSeriesDecorator /> should render and match a snapshot 1`] = `
<Connect(SpecInstance)
<AreaSeries
areaSeriesStyle={
Object {
"area": Object {

View file

@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`src/legacy/core_plugins/metrics/public/visualizations/views/timeseries/decorators/bar_decorator.js <BarSeriesDecorator /> should render and match a snapshot 1`] = `
<Connect(SpecInstance)
<BarSeries
barSeriesStyle={
Object {
"rect": Object {

View file

@ -11,7 +11,7 @@ import React, { FC } from 'react';
import {
Direction,
Settings,
SettingsSpecProps,
SettingsProps,
DomainRange,
Position,
PartialTheme,
@ -50,7 +50,7 @@ type XYSettingsProps = Pick<
| 'xAxis'
| 'orderBucketsBySum'
> & {
onPointerUpdate: SettingsSpecProps['onPointerUpdate'];
onPointerUpdate: SettingsProps['onPointerUpdate'];
xDomain?: DomainRange;
adjustedXDomain?: DomainRange;
showLegend: boolean;

View file

@ -255,7 +255,7 @@ export class VisualizeChartPageObject extends FtrService {
if (isVisTypeHeatmapChart) {
const legendItems =
(await this.getEsChartDebugState(heatmapChartSelector))?.legend!.items ?? [];
(await this.getEsChartDebugState(heatmapChartSelector))?.legend?.items ?? [];
return legendItems.map(({ name }) => name);
}

View file

@ -121,6 +121,8 @@ export function PageLoadDistChart({
fit={Fit.Linear}
id={'PagesPercentage'}
name={I18LABELS.overall}
xAccessor="x"
yAccessors={['y']}
xScaleType={ScaleType.Linear}
yScaleType={ScaleType.Linear}
data={data?.pageLoadDistribution ?? []}

View file

@ -38,9 +38,15 @@ interface Props {
}
const theme: PartialTheme = {
chartMargins: { top: 0, bottom: 0, left: 0, right: 0 },
legend: {
verticalWidth: 100,
},
partition: {
linkLabel: { maximumSection: Infinity, maxCount: 0 },
outerSizeRatio: 1, // - 0.5 * Math.random(),
circlePadding: 4,
},
};
export function VisitorBreakdownChart({ loading, options }: Props) {
@ -64,6 +70,8 @@ export function VisitorBreakdownChart({ loading, options }: Props) {
data={
options?.length ? options : [{ count: 1, name: I18LABELS.noData }]
}
layout={PartitionLayout.sunburst}
clockwiseSectors={false}
valueAccessor={(d: Datum) => d.count as number}
valueGetter="percent"
percentFormatter={(d: number) =>
@ -78,14 +86,6 @@ export function VisitorBreakdownChart({ loading, options }: Props) {
},
},
]}
config={{
partitionLayout: PartitionLayout.sunburst,
linkLabel: { maximumSection: Infinity, maxCount: 0 },
margin: { top: 0, bottom: 0, left: 0, right: 0 },
outerSizeRatio: 1, // - 0.5 * Math.random(),
circlePadding: 4,
clockwiseSectors: false,
}}
/>
</Chart>
</StyleChart>

View file

@ -54,6 +54,8 @@ export function BreakdownSeries({
id={`${field}-${value}-${name}`}
key={`${field}-${value}-${name}`}
name={name}
xAccessor="x"
yAccessors={['y']}
xScaleType={ScaleType.Linear}
yScaleType={ScaleType.Linear}
curve={CurveType.CURVE_CATMULL_ROM}

View file

@ -12,6 +12,7 @@ import {
PrimitiveValue,
Settings,
TooltipInfo,
PartialTheme,
} from '@elastic/charts';
import {
EuiCheckbox,
@ -286,6 +287,18 @@ export function ServiceProfilingFlamegraph({
}, [points, highlightFilter, data]);
const chartTheme = useChartTheme();
const themeOverrides: PartialTheme = {
chartMargins: { top: 0, bottom: 0, left: 0, right: 0 },
partition: {
fillLabel: {
fontFamily: theme.eui.euiCodeFontFamily,
clipText: true,
},
fontFamily: theme.eui.euiCodeFontFamily,
minFontSize: 9,
maxFontSize: 9,
},
};
const chartSize = {
height: layers.length * 20,
@ -305,7 +318,7 @@ export function ServiceProfilingFlamegraph({
<EuiFlexItem grow>
<Chart size={chartSize}>
<Settings
theme={chartTheme}
theme={[themeOverrides, ...chartTheme]}
tooltip={{
customTooltip: (info) => (
<CustomTooltip
@ -320,20 +333,11 @@ export function ServiceProfilingFlamegraph({
id="profile_graph"
data={points}
layers={layers}
drilldown
maxRowCount={1}
layout={PartitionLayout.icicle}
valueAccessor={(d: Datum) => d.value as number}
valueFormatter={() => ''}
config={{
fillLabel: {
fontFamily: theme.eui.euiCodeFontFamily,
clipText: true,
},
drilldown: true,
fontFamily: theme.eui.euiCodeFontFamily,
minFontSize: 9,
maxFontSize: 9,
maxRowCount: 1,
partitionLayout: PartitionLayout.icicle,
}}
/>
</Chart>
</EuiFlexItem>

View file

@ -98,6 +98,7 @@ export function ServiceProfilingTimeline({
xScaleType={ScaleType.Time}
yScaleType={ScaleType.Linear}
xAccessor="x"
yAccessors={['y']}
stackAccessors={['x']}
/>
))}

View file

@ -171,7 +171,12 @@ export function BreakdownChart({
})
) : (
// When timeseries is empty, loads an AreaSeries chart to show the default empty message.
<AreaSeries id="empty_chart" data={[]} />
<AreaSeries
id="empty_chart"
xAccessor="x"
yAccessors={['y']}
data={[]}
/>
)}
</Chart>
</ChartContainer>

View file

@ -10,11 +10,11 @@ import {
Chart,
CurveType,
LineSeries,
PartialTheme,
ScaleType,
Settings,
} from '@elastic/charts';
import { EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui';
import { merge } from 'lodash';
import React from 'react';
import { useChartTheme } from '../../../../../../observability/public';
import { Coordinate } from '../../../../../typings/timeseries';
@ -60,7 +60,7 @@ export function SparkPlot({
const comparisonChartTheme = getComparisonChartTheme(theme);
const hasComparisonSeries = !!comparisonSeries?.length;
const sparkplotChartTheme = merge({}, defaultChartTheme, {
const sparkplotChartTheme: PartialTheme = {
chartMargins: { left: 0, right: 0, top: 0, bottom: 0 },
lineSeriesStyle: {
point: { opacity: 0 },
@ -69,7 +69,7 @@ export function SparkPlot({
point: { opacity: 0 },
},
...(hasComparisonSeries ? comparisonChartTheme : {}),
});
};
const colorValue = theme.eui[color];
@ -95,7 +95,7 @@ export function SparkPlot({
{hasValidTimeseries(series) ? (
<Chart size={chartSize}>
<Settings
theme={sparkplotChartTheme}
theme={[sparkplotChartTheme, ...defaultChartTheme]}
showLegend={false}
tooltip="none"
/>

View file

@ -126,13 +126,15 @@ export function TimeseriesChart({
onBrushEnd={(event) =>
onBrushEnd({ x: (event as XYBrushEvent).x, history })
}
theme={{
...chartTheme,
areaSeriesStyle: {
line: { visible: false },
theme={[
customTheme,
{
areaSeriesStyle: {
line: { visible: false },
},
},
...customTheme,
}}
...chartTheme,
]}
onPointerUpdate={setPointerEvent}
externalPointerEvents={{
tooltip: { visible: true },

View file

@ -156,28 +156,31 @@ export function TransactionDistributionChart({
<Chart>
<Settings
rotation={0}
theme={{
theme={[
{
legend: {
spacingBuffer: 100,
},
areaSeriesStyle: {
line: {
visible: true,
},
},
axes: {
tickLine: {
visible: true,
size: 5,
padding: 10,
},
tickLabel: {
fontSize: 10,
fill: euiTheme.eui.euiColorMediumShade,
padding: 0,
},
},
},
...chartTheme,
legend: {
spacingBuffer: 100,
},
areaSeriesStyle: {
line: {
visible: true,
},
},
axes: {
...chartTheme.axes,
tickLine: {
size: 5,
},
tickLabel: {
fontSize: 10,
fill: euiTheme.eui.euiColorMediumShade,
padding: 0,
},
},
}}
]}
showLegend={true}
legendPosition={Position.Bottom}
onBrushEnd={onChartSelection}

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import { PartialTheme } from '@elastic/charts';
import moment from 'moment';
import { EuiTheme } from 'src/plugins/kibana_react/common';
import {
@ -13,7 +14,7 @@ import {
} from '../../../../common/runtime_types/comparison_type_rt';
import { getDateDifference } from '../../../../common/utils/formatters';
export function getComparisonChartTheme(theme: EuiTheme) {
export function getComparisonChartTheme(theme: EuiTheme): PartialTheme {
return {
areaSeriesStyle: {
area: {

View file

@ -102,7 +102,9 @@ export const MetricDistributionChart: FC<Props> = ({
yScaleType={ScaleType.Linear}
xAccessor="x"
yAccessors={['y']}
data={chartData.length > 0 ? chartData : [{ x: 0, y: 0 }]}
data={
chartData.length > 0 ? chartData : [{ x: 0, y: 0, dataMin: 0, dataMax: 0, percent: 0 }]
}
curve={CurveType.CURVE_STEP_AFTER}
/>
</Chart>

View file

@ -14,6 +14,7 @@ import {
ShapeTreeNode,
HierarchyOfArrays,
Chart,
PartialTheme,
} from '@elastic/charts';
import { shallow } from 'enzyme';
import type { LensMultiTable } from '../../common';
@ -110,7 +111,7 @@ describe('PieVisualization component', () => {
test('it sets the correct lines per legend item', () => {
const component = shallow(<PieComponent args={args} {...getDefaultArgs()} />);
expect(component.find(Settings).prop('theme')).toEqual({
expect(component.find(Settings).prop<PartialTheme[]>('theme')[0]).toMatchObject({
background: {
color: undefined,
},
@ -395,7 +396,9 @@ describe('PieVisualization component', () => {
const component = shallow(
<PieComponent args={args} {...getDefaultArgs()} data={emptyData} />
);
expect(component.find(Partition).prop('config')?.outerSizeRatio).toBeCloseTo(1 / 1.05);
expect(
component.find(Settings).prop<PartialTheme[]>('theme')[0].partition?.outerSizeRatio
).toBeCloseTo(1 / 1.05);
});
test('it should bound the shrink the chart area to ~20% when some small slices are detected', () => {
@ -419,7 +422,9 @@ describe('PieVisualization component', () => {
const component = shallow(
<PieComponent args={args} {...getDefaultArgs()} data={emptyData} />
);
expect(component.find(Partition).prop('config')?.outerSizeRatio).toBeCloseTo(1 / 1.2);
expect(
component.find(Settings).prop<PartialTheme[]>('theme')[0].partition?.outerSizeRatio
).toBeCloseTo(1 / 1.2);
});
});
});

View file

@ -8,19 +8,18 @@
import { uniq } from 'lodash';
import React from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { Required } from '@kbn/utility-types';
import { EuiText } from '@elastic/eui';
import {
Chart,
Datum,
LayerValue,
Partition,
PartitionConfig,
PartitionLayer,
PartitionFillLabel,
RecursivePartial,
Position,
Settings,
ElementClickListener,
PartialTheme,
} from '@elastic/charts';
import { RenderMode } from 'src/plugins/expressions';
import type { LensFilterEvent } from '../types';
@ -99,7 +98,7 @@ export function PieComponent(
});
}
const fillLabel: Partial<PartitionFillLabel> = {
const fillLabel: PartitionLayer['fillLabel'] = {
valueFont: {
fontWeight: 700,
},
@ -202,42 +201,52 @@ export function PieComponent(
};
});
const { legend, partitionType: partitionLayout, label: chartType } = PartitionChartsMeta[shape];
const { legend, partitionType, label: chartType } = PartitionChartsMeta[shape];
const config: RecursivePartial<PartitionConfig> = {
partitionLayout,
fontFamily: chartTheme.barSeriesStyle?.displayValue?.fontFamily,
outerSizeRatio: 1,
specialFirstInnermostSector: true,
minFontSize: 10,
maxFontSize: 16,
// Labels are added outside the outer ring when the slice is too small
linkLabel: {
maxCount: 5,
fontSize: 11,
// Dashboard background color is affected by dark mode, which we need
// to account for in outer labels
// This does not handle non-dashboard embeddables, which are allowed to
// have different backgrounds.
textColor: chartTheme.axes?.axisTitle?.fill,
const themeOverrides: Required<PartialTheme, 'partition'> = {
chartMargins: { top: 0, bottom: 0, left: 0, right: 0 },
background: {
color: undefined, // removes background for embeddables
},
legend: {
labelOptions: { maxLines: truncateLegend ? legendMaxLines ?? 1 : 0 },
},
partition: {
fontFamily: chartTheme.barSeriesStyle?.displayValue?.fontFamily,
outerSizeRatio: 1,
minFontSize: 10,
maxFontSize: 16,
// Labels are added outside the outer ring when the slice is too small
linkLabel: {
maxCount: 5,
fontSize: 11,
// Dashboard background color is affected by dark mode, which we need
// to account for in outer labels
// This does not handle non-dashboard embeddables, which are allowed to
// have different backgrounds.
textColor: chartTheme.axes?.axisTitle?.fill,
},
sectorLineStroke: chartTheme.lineSeriesStyle?.point?.fill,
sectorLineWidth: 1.5,
circlePadding: 4,
},
sectorLineStroke: chartTheme.lineSeriesStyle?.point?.fill,
sectorLineWidth: 1.5,
circlePadding: 4,
};
if (isTreemapOrMosaicShape(shape)) {
if (hideLabels || categoryDisplay === 'hide') {
config.fillLabel = { textColor: 'rgba(0,0,0,0)' };
themeOverrides.partition.fillLabel = { textColor: 'rgba(0,0,0,0)' };
}
} else {
config.emptySizeRatio = shape === 'donut' ? emptySizeRatio : 0;
themeOverrides.partition.emptySizeRatio = shape === 'donut' ? emptySizeRatio : 0;
if (hideLabels || categoryDisplay === 'hide') {
// Force all labels to be linked, then prevent links from showing
config.linkLabel = { maxCount: 0, maximumSection: Number.POSITIVE_INFINITY };
themeOverrides.partition.linkLabel = {
maxCount: 0,
maximumSection: Number.POSITIVE_INFINITY,
};
} else if (categoryDisplay === 'inside') {
// Prevent links from showing
config.linkLabel = { maxCount: 0 };
themeOverrides.partition.linkLabel = { maxCount: 0 };
} else {
// if it contains any slice below 2% reduce the ratio
// first step: sum it up the overall sum
@ -246,7 +255,7 @@ export function PieComponent(
const smallSlices = slices.filter((value) => value < 0.02).length;
if (smallSlices) {
// shrink up to 20% to give some room for the linked values
config.outerSizeRatio = 1 / (1 + Math.min(smallSlices * 0.05, 0.2));
themeOverrides.partition.outerSizeRatio = 1 / (1 + Math.min(smallSlices * 0.05, 0.2));
}
}
}
@ -322,27 +331,19 @@ export function PieComponent(
legendMaxDepth={nestedLegend ? undefined : 1 /* Color is based only on first layer */}
onElementClick={props.interactive ?? true ? onElementClickHandler : undefined}
legendAction={props.interactive ? getLegendAction(firstTable, onClickValue) : undefined}
theme={{
...chartTheme,
background: {
...chartTheme.background,
color: undefined, // removes background for embeddables
},
legend: {
labelOptions: { maxLines: truncateLegend ? legendMaxLines ?? 1 : 0 },
},
}}
theme={[themeOverrides, chartTheme]}
baseTheme={chartBaseTheme}
/>
<Partition
id={shape}
data={firstTable.rows}
layout={partitionType}
specialFirstInnermostSector
valueAccessor={(d: Datum) => getSliceValue(d, metricColumn)}
percentFormatter={(d: number) => percentFormatter.convert(d / 100)}
valueGetter={hideLabels || numberDisplay === 'value' ? undefined : 'percent'}
valueFormatter={(d: number) => (hideLabels ? '' : formatters[metricColumn.id].convert(d))}
layers={layers}
config={config}
topGroove={hideLabels || categoryDisplay === 'hide' ? 0 : undefined}
/>
</Chart>

View file

@ -4,7 +4,7 @@ exports[`xy_expression XYChart component it renders area 1`] = `
<Chart
renderer="canvas"
>
<Connect(SpecInstance)
<Settings
allowBrushingLastHistogramBin={false}
baseTheme={Object {}}
debugState={false}
@ -43,7 +43,7 @@ exports[`xy_expression XYChart component it renders area 1`] = `
}
}
/>
<Connect(SpecInstance)
<Component
gridLine={
Object {
"strokeWidth": 1,
@ -70,7 +70,7 @@ exports[`xy_expression XYChart component it renders area 1`] = `
timeAxisLayerCount={0}
title="c"
/>
<Connect(SpecInstance)
<Component
domain={
Object {
"fit": false,
@ -108,7 +108,7 @@ exports[`xy_expression XYChart component it renders area 1`] = `
darkMode={false}
histogramMode={false}
/>
<Connect(SpecInstance)
<AreaSeries
areaSeriesStyle={
Object {
"point": Object {
@ -168,7 +168,7 @@ exports[`xy_expression XYChart component it renders area 1`] = `
}
yScaleType="linear"
/>
<Connect(SpecInstance)
<AreaSeries
areaSeriesStyle={
Object {
"point": Object {
@ -235,7 +235,7 @@ exports[`xy_expression XYChart component it renders bar 1`] = `
<Chart
renderer="canvas"
>
<Connect(SpecInstance)
<Settings
allowBrushingLastHistogramBin={false}
baseTheme={Object {}}
debugState={false}
@ -274,7 +274,7 @@ exports[`xy_expression XYChart component it renders bar 1`] = `
}
}
/>
<Connect(SpecInstance)
<Component
gridLine={
Object {
"strokeWidth": 1,
@ -301,7 +301,7 @@ exports[`xy_expression XYChart component it renders bar 1`] = `
timeAxisLayerCount={0}
title="c"
/>
<Connect(SpecInstance)
<Component
domain={
Object {
"fit": false,
@ -339,7 +339,7 @@ exports[`xy_expression XYChart component it renders bar 1`] = `
darkMode={false}
histogramMode={false}
/>
<Connect(SpecInstance)
<BarSeries
areaSeriesStyle={
Object {
"point": Object {
@ -406,7 +406,7 @@ exports[`xy_expression XYChart component it renders bar 1`] = `
}
yScaleType="linear"
/>
<Connect(SpecInstance)
<BarSeries
areaSeriesStyle={
Object {
"point": Object {
@ -480,7 +480,7 @@ exports[`xy_expression XYChart component it renders horizontal bar 1`] = `
<Chart
renderer="canvas"
>
<Connect(SpecInstance)
<Settings
allowBrushingLastHistogramBin={false}
baseTheme={Object {}}
debugState={false}
@ -519,7 +519,7 @@ exports[`xy_expression XYChart component it renders horizontal bar 1`] = `
}
}
/>
<Connect(SpecInstance)
<Component
gridLine={
Object {
"strokeWidth": 1,
@ -546,7 +546,7 @@ exports[`xy_expression XYChart component it renders horizontal bar 1`] = `
timeAxisLayerCount={0}
title="c"
/>
<Connect(SpecInstance)
<Component
domain={
Object {
"fit": false,
@ -584,7 +584,7 @@ exports[`xy_expression XYChart component it renders horizontal bar 1`] = `
darkMode={false}
histogramMode={false}
/>
<Connect(SpecInstance)
<BarSeries
areaSeriesStyle={
Object {
"point": Object {
@ -651,7 +651,7 @@ exports[`xy_expression XYChart component it renders horizontal bar 1`] = `
}
yScaleType="linear"
/>
<Connect(SpecInstance)
<BarSeries
areaSeriesStyle={
Object {
"point": Object {
@ -725,7 +725,7 @@ exports[`xy_expression XYChart component it renders line 1`] = `
<Chart
renderer="canvas"
>
<Connect(SpecInstance)
<Settings
allowBrushingLastHistogramBin={false}
baseTheme={Object {}}
debugState={false}
@ -764,7 +764,7 @@ exports[`xy_expression XYChart component it renders line 1`] = `
}
}
/>
<Connect(SpecInstance)
<Component
gridLine={
Object {
"strokeWidth": 1,
@ -791,7 +791,7 @@ exports[`xy_expression XYChart component it renders line 1`] = `
timeAxisLayerCount={0}
title="c"
/>
<Connect(SpecInstance)
<Component
domain={
Object {
"fit": false,
@ -829,7 +829,7 @@ exports[`xy_expression XYChart component it renders line 1`] = `
darkMode={false}
histogramMode={false}
/>
<Connect(SpecInstance)
<LineSeries
areaSeriesStyle={
Object {
"point": Object {
@ -889,7 +889,7 @@ exports[`xy_expression XYChart component it renders line 1`] = `
}
yScaleType="linear"
/>
<Connect(SpecInstance)
<LineSeries
areaSeriesStyle={
Object {
"point": Object {
@ -956,7 +956,7 @@ exports[`xy_expression XYChart component it renders stacked area 1`] = `
<Chart
renderer="canvas"
>
<Connect(SpecInstance)
<Settings
allowBrushingLastHistogramBin={false}
baseTheme={Object {}}
debugState={false}
@ -995,7 +995,7 @@ exports[`xy_expression XYChart component it renders stacked area 1`] = `
}
}
/>
<Connect(SpecInstance)
<Component
gridLine={
Object {
"strokeWidth": 1,
@ -1022,7 +1022,7 @@ exports[`xy_expression XYChart component it renders stacked area 1`] = `
timeAxisLayerCount={0}
title="c"
/>
<Connect(SpecInstance)
<Component
domain={
Object {
"fit": false,
@ -1060,7 +1060,7 @@ exports[`xy_expression XYChart component it renders stacked area 1`] = `
darkMode={false}
histogramMode={false}
/>
<Connect(SpecInstance)
<AreaSeries
areaSeriesStyle={
Object {
"point": Object {
@ -1124,7 +1124,7 @@ exports[`xy_expression XYChart component it renders stacked area 1`] = `
}
yScaleType="linear"
/>
<Connect(SpecInstance)
<AreaSeries
areaSeriesStyle={
Object {
"point": Object {
@ -1195,7 +1195,7 @@ exports[`xy_expression XYChart component it renders stacked bar 1`] = `
<Chart
renderer="canvas"
>
<Connect(SpecInstance)
<Settings
allowBrushingLastHistogramBin={false}
baseTheme={Object {}}
debugState={false}
@ -1234,7 +1234,7 @@ exports[`xy_expression XYChart component it renders stacked bar 1`] = `
}
}
/>
<Connect(SpecInstance)
<Component
gridLine={
Object {
"strokeWidth": 1,
@ -1261,7 +1261,7 @@ exports[`xy_expression XYChart component it renders stacked bar 1`] = `
timeAxisLayerCount={0}
title="c"
/>
<Connect(SpecInstance)
<Component
domain={
Object {
"fit": false,
@ -1299,7 +1299,7 @@ exports[`xy_expression XYChart component it renders stacked bar 1`] = `
darkMode={false}
histogramMode={false}
/>
<Connect(SpecInstance)
<BarSeries
areaSeriesStyle={
Object {
"point": Object {
@ -1370,7 +1370,7 @@ exports[`xy_expression XYChart component it renders stacked bar 1`] = `
}
yScaleType="linear"
/>
<Connect(SpecInstance)
<BarSeries
areaSeriesStyle={
Object {
"point": Object {
@ -1448,7 +1448,7 @@ exports[`xy_expression XYChart component it renders stacked horizontal bar 1`] =
<Chart
renderer="canvas"
>
<Connect(SpecInstance)
<Settings
allowBrushingLastHistogramBin={false}
baseTheme={Object {}}
debugState={false}
@ -1487,7 +1487,7 @@ exports[`xy_expression XYChart component it renders stacked horizontal bar 1`] =
}
}
/>
<Connect(SpecInstance)
<Component
gridLine={
Object {
"strokeWidth": 1,
@ -1514,7 +1514,7 @@ exports[`xy_expression XYChart component it renders stacked horizontal bar 1`] =
timeAxisLayerCount={0}
title="c"
/>
<Connect(SpecInstance)
<Component
domain={
Object {
"fit": false,
@ -1552,7 +1552,7 @@ exports[`xy_expression XYChart component it renders stacked horizontal bar 1`] =
darkMode={false}
histogramMode={false}
/>
<Connect(SpecInstance)
<BarSeries
areaSeriesStyle={
Object {
"point": Object {
@ -1623,7 +1623,7 @@ exports[`xy_expression XYChart component it renders stacked horizontal bar 1`] =
}
yScaleType="linear"
/>
<Connect(SpecInstance)
<BarSeries
areaSeriesStyle={
Object {
"point": Object {

View file

@ -33,6 +33,9 @@ import {
RecursivePartial,
AxisStyle,
ScaleType,
AreaSeriesProps,
BarSeriesProps,
LineSeriesProps,
} from '@elastic/charts';
import { I18nProvider } from '@kbn/i18n-react';
import type {
@ -83,10 +86,7 @@ declare global {
}
}
type InferPropType<T> = T extends React.FunctionComponent<infer P> ? P : T;
type SeriesSpec = InferPropType<typeof LineSeries> &
InferPropType<typeof BarSeries> &
InferPropType<typeof AreaSeries>;
type SeriesSpec = LineSeriesProps & BarSeriesProps & AreaSeriesProps;
export type XYChartRenderProps = XYChartProps & {
chartsThemeService: ChartsPluginSetup['theme'];

View file

@ -18,7 +18,7 @@ import {
RecursivePartial,
AxisStyle,
PartialTheme,
BarSeriesSpec,
BarSeriesProps,
} from '@elastic/charts';
import { i18n } from '@kbn/i18n';
import { euiLightVars as euiVars } from '@kbn/ui-theme';
@ -100,13 +100,18 @@ export const FeatureImportanceSummaryPanel: FC<FeatureImportanceSummaryPanelProp
services: { docLinks },
} = useMlKibana();
const [plotData, barSeriesSpec, showLegend, chartHeight] = useMemo(() => {
let sortedData: Array<{
featureName: string;
meanImportance: number;
className?: FeatureImportanceClassName;
}> = [];
let _barSeriesSpec: Partial<BarSeriesSpec> = {
interface Datum {
featureName: string;
meanImportance: number;
className?: FeatureImportanceClassName;
}
type PlotData = Datum[];
type SeriesProps = Omit<BarSeriesProps, 'id' | 'xScaleType' | 'yScaleType' | 'data'>;
const [plotData, barSeriesSpec, showLegend, chartHeight] = useMemo<
[plotData: PlotData, barSeriesSpec: SeriesProps, showLegend?: boolean, chartHeight?: number]
>(() => {
let sortedData: PlotData = [];
let _barSeriesSpec: SeriesProps = {
xAccessor: 'featureName',
yAccessors: ['meanImportance'],
name: i18n.translate(
@ -122,7 +127,7 @@ export const FeatureImportanceSummaryPanel: FC<FeatureImportanceSummaryPanelProp
| 'regression'
| '' = '';
if (totalFeatureImportance.length < 1) {
return [sortedData, _barSeriesSpec];
return [sortedData, _barSeriesSpec, undefined, undefined];
}
if (isClassificationTotalFeatureImportance(totalFeatureImportance[0])) {

View file

@ -28,6 +28,7 @@ import {
HeatmapBrushEvent,
Position,
ScaleType,
PartialTheme,
} from '@elastic/charts';
import moment from 'moment';
@ -276,66 +277,59 @@ export const SwimlaneContainer: FC<SwimlaneProps> = ({
const showBrush = !!onCellsSelection;
const swimLaneConfig = useMemo<HeatmapSpec['config']>(() => {
const themeOverrides = useMemo<PartialTheme>(() => {
if (!showSwimlane) return {};
const config: HeatmapSpec['config'] = {
grid: {
cellHeight: {
min: CELL_HEIGHT,
max: CELL_HEIGHT,
const theme: PartialTheme = {
heatmap: {
grid: {
cellHeight: {
min: CELL_HEIGHT,
max: CELL_HEIGHT,
},
stroke: {
width: 1,
color: euiTheme.euiBorderColor,
},
},
stroke: {
width: 1,
color: euiTheme.euiBorderColor,
cell: {
maxWidth: 'fill',
maxHeight: 'fill',
label: {
visible: false,
},
border: {
stroke: euiTheme.euiBorderColor,
strokeWidth: 0,
},
},
},
cell: {
maxWidth: 'fill',
maxHeight: 'fill',
label: {
visible: false,
yAxisLabel: {
visible: showYAxis,
width: Y_AXIS_LABEL_WIDTH,
textColor: euiTheme.euiTextSubduedColor,
padding: Y_AXIS_LABEL_PADDING,
fontSize: parseInt(euiTheme.euiFontSizeXS, 10),
},
border: {
stroke: euiTheme.euiBorderColor,
strokeWidth: 0,
xAxisLabel: {
visible: showTimeline,
textColor: euiTheme.euiTextSubduedColor,
fontSize: parseInt(euiTheme.euiFontSizeXS, 10),
// Required to calculate where the swimlane ends
width: X_AXIS_RIGHT_OVERFLOW * 2,
},
},
yAxisLabel: {
visible: showYAxis,
width: Y_AXIS_LABEL_WIDTH,
textColor: euiTheme.euiTextSubduedColor,
padding: Y_AXIS_LABEL_PADDING,
formatter: (laneLabel: string) => {
return laneLabel === '' ? EMPTY_FIELD_VALUE_LABEL : laneLabel;
brushMask: {
visible: showBrush,
fill: isDarkTheme ? 'rgb(30,31,35,80%)' : 'rgb(247,247,247,50%)',
},
fontSize: parseInt(euiTheme.euiFontSizeXS, 10),
},
xAxisLabel: {
visible: showTimeline,
textColor: euiTheme.euiTextSubduedColor,
formatter: (v: number) => {
timeBuckets.setInterval(`${swimlaneData.interval}s`);
const scaledDateFormat = timeBuckets.getScaledDateFormat();
return moment(v).format(scaledDateFormat);
brushArea: {
visible: showBrush,
stroke: isDarkTheme ? 'rgb(255, 255, 255)' : 'rgb(105, 112, 125)',
},
fontSize: parseInt(euiTheme.euiFontSizeXS, 10),
// Required to calculate where the swimlane ends
width: X_AXIS_RIGHT_OVERFLOW * 2,
...(showLegend ? { maxLegendHeight: LEGEND_HEIGHT } : {}),
},
brushMask: {
visible: showBrush,
fill: isDarkTheme ? 'rgb(30,31,35,80%)' : 'rgb(247,247,247,50%)',
},
brushArea: {
visible: showBrush,
stroke: isDarkTheme ? 'rgb(255, 255, 255)' : 'rgb(105, 112, 125)',
},
...(showLegend ? { maxLegendHeight: LEGEND_HEIGHT } : {}),
timeZone: 'UTC',
};
return config;
return theme;
}, [
showSwimlane,
swimlaneType,
@ -427,6 +421,7 @@ export const SwimlaneContainer: FC<SwimlaneProps> = ({
{showSwimlane && !isLoading && (
<Chart className={'mlSwimLaneContainer'}>
<Settings
theme={themeOverrides}
onElementClick={onElementClick}
showLegend={showLegend}
legendPosition={Position.Top}
@ -438,6 +433,7 @@ export const SwimlaneContainer: FC<SwimlaneProps> = ({
<Heatmap
id={id}
timeZone="UTC"
colorScale={{
type: 'bands',
bands: [
@ -485,7 +481,14 @@ export const SwimlaneContainer: FC<SwimlaneProps> = ({
},
}}
ySortPredicate="dataIndex"
config={swimLaneConfig}
yAxisLabelFormatter={(laneLabel) => {
return laneLabel === '' ? EMPTY_FIELD_VALUE_LABEL : String(laneLabel);
}}
xAxisLabelFormatter={(v) => {
timeBuckets.setInterval(`${swimlaneData.interval}s`);
const scaledDateFormat = timeBuckets.getScaledDateFormat();
return moment(v).format(scaledDateFormat);
}}
/>
</Chart>
)}

View file

@ -5,34 +5,34 @@
* 2.0.
*/
import { PartialTheme } from '@elastic/charts';
import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme';
import { useTheme } from './use_theme';
export function useChartTheme() {
export function useChartTheme(): PartialTheme[] {
const theme = useTheme();
const baseChartTheme = theme.darkMode
? EUI_CHARTS_THEME_DARK.theme
: EUI_CHARTS_THEME_LIGHT.theme;
return {
...baseChartTheme,
chartMargins: {
left: 10,
right: 10,
top: 10,
bottom: 10,
return [
{
chartMargins: {
left: 10,
right: 10,
top: 10,
bottom: 10,
},
background: {
color: 'transparent',
},
lineSeriesStyle: {
point: { visible: false },
},
areaSeriesStyle: {
point: { visible: false },
},
},
background: {
...baseChartTheme.background,
color: 'transparent',
},
lineSeriesStyle: {
...baseChartTheme.lineSeriesStyle,
point: { visible: false },
},
areaSeriesStyle: {
...baseChartTheme.areaSeriesStyle,
point: { visible: false },
},
};
baseChartTheme,
];
}

View file

@ -13,7 +13,7 @@ import {
Rendering,
Rotation,
ScaleType,
SettingsSpecProps,
SettingsProps,
TickFormatter,
Position,
BrushEndListener,
@ -52,7 +52,7 @@ export interface ChartSeriesConfigs {
tickSize?: number | undefined;
};
yAxisTitle?: string | undefined;
settings?: Partial<SettingsSpecProps>;
settings?: SettingsProps;
}
export interface ChartSeriesData {

View file

@ -9,7 +9,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui';
import React, { useContext } from 'react';
import { i18n } from '@kbn/i18n';
import styled from 'styled-components';
import { Chart, Datum, Partition, Settings, PartitionLayout } from '@elastic/charts';
import { Chart, Datum, Partition, Settings, PartitionLayout, PartialTheme } from '@elastic/charts';
import { DonutChartLegend } from './donut_chart_legend';
import { UptimeThemeContext } from '../../../contexts';
@ -28,6 +28,19 @@ export const GreenCheckIcon = styled(EuiIcon)`
position: absolute;
`;
const themeOverrides: PartialTheme = {
chartMargins: { top: 0, bottom: 0, left: 0, right: 0 },
partition: {
linkLabel: {
maximumSection: Infinity,
},
idealFontSizeJump: 1.1,
outerSizeRatio: 0.9,
emptySizeRatio: 0.4,
circlePadding: 4,
},
};
export const DonutChart = ({ height, down, up }: DonutChartProps) => {
const {
colors: { danger, gray },
@ -44,15 +57,18 @@ export const DonutChart = ({ height, down, up }: DonutChartProps) => {
'Pie chart showing the current status. {down} of {total} monitors are down.',
values: { down, total: up + down },
})}
{...chartTheme}
>
<Settings />
<Settings
theme={[themeOverrides, chartTheme.theme ?? {}]}
baseTheme={chartTheme.baseTheme}
/>
<Partition
id="spec_1"
data={[
{ value: down, label: 'Down' },
{ value: up, label: 'Up' },
]}
layout={PartitionLayout.sunburst}
valueAccessor={(d: Datum) => d.value as number}
layers={[
{
@ -65,17 +81,6 @@ export const DonutChart = ({ height, down, up }: DonutChartProps) => {
},
},
]}
config={{
partitionLayout: PartitionLayout.sunburst,
linkLabel: {
maximumSection: Infinity,
},
margin: { top: 0, bottom: 0, left: 0, right: 0 },
idealFontSizeJump: 1.1,
outerSizeRatio: 0.9,
emptySizeRatio: 0.4,
circlePadding: 4,
}}
/>
</Chart>
{down === 0 && <GreenCheckIcon className="greenCheckIcon" type="checkInCircleFilled" />}

View file

@ -1431,10 +1431,10 @@
dependencies:
object-hash "^1.3.0"
"@elastic/charts@40.2.0":
version "40.2.0"
resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-40.2.0.tgz#2e329ce4f495731f478cbaf2f8f3b89b5167a65b"
integrity sha512-N0t7YK58Kce/s9LEgaocrD75NYuFMwrcI1QNIPcwZ9IAOHY8/9yRHD5Ipoz0caGibAgOE8OunGkpyPY/NHKB5Q==
"@elastic/charts@43.1.1":
version "43.1.1"
resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-43.1.1.tgz#2a9cd4bbde9397b86a45d8aa604a1950ae0997c0"
integrity sha512-lYTdwpARIDXD15iC4cujKplBhGXb3zriBATp0wFsqgT9XE9TMOzlQ9dgylWQ+2x6OlataZLrOMnWXiFQ3uJqqQ==
dependencies:
"@popperjs/core" "^2.4.0"
chroma-js "^2.1.0"