;
+
+/** @internal */
+export function measureOneBoxWidth(measure: TextMeasure, fontSize: number, box: Box) {
+ return measure(fontSize, [box])[0].width;
+}
+
+/** @internal */
+export function cutToLength(s: string, maxLength: number) {
+ return s.length <= maxLength ? s : `${s.slice(0, Math.max(0, maxLength - 1))}…`; // ellipsis is one char
+}
+
+/** @internal */
+export function fitText(
+ measure: TextMeasure,
+ desiredText: string,
+ allottedWidth: number,
+ fontSize: number,
+ font: Font,
+) {
+ const desiredLength = desiredText.length;
+ const response = (v: number) => measure(fontSize, [{ ...font, text: desiredText.slice(0, Math.max(0, v)) }])[0].width;
+ const visibleLength = monotonicHillClimb(response, desiredLength, allottedWidth, integerSnap);
+ const text = visibleLength < 2 && desiredLength >= 2 ? '' : cutToLength(desiredText, visibleLength);
+ const { width, emHeightAscent, emHeightDescent } = measure(fontSize, [{ ...font, text }])[0];
+ return {
+ width,
+ verticalOffset: -(emHeightDescent + emHeightAscent) / 2, // meaning, `middle`
+ text,
+ };
+}
diff --git a/src/components/__snapshots__/chart.test.tsx.snap b/src/components/__snapshots__/chart.test.tsx.snap
index ab35a43e06..a354f9c450 100644
--- a/src/components/__snapshots__/chart.test.tsx.snap
+++ b/src/components/__snapshots__/chart.test.tsx.snap
@@ -67,7 +67,7 @@ exports[`Chart should render the legend name test 1`] = `
-
+
@@ -100,7 +100,7 @@ exports[`Chart should render the legend name test 1`] = `
-
+
diff --git a/src/components/brush/brush.tsx b/src/components/brush/brush.tsx
index 410ea621c4..e89ef7d8ff 100644
--- a/src/components/brush/brush.tsx
+++ b/src/components/brush/brush.tsx
@@ -42,6 +42,7 @@ interface StateProps {
isBrushing: boolean | undefined;
isBrushAvailable: boolean | undefined;
brushArea: Dimensions | null;
+ zIndex: number;
}
const DEFAULT_FILL_COLOR: RgbObject = {
@@ -129,7 +130,7 @@ class BrushToolComponent extends React.Component {
}
render() {
- const { initialized, isBrushAvailable, isBrushing, projectionContainer } = this.props;
+ const { initialized, isBrushAvailable, isBrushing, projectionContainer, zIndex } = this.props;
if (!initialized || !isBrushAvailable || !isBrushing) {
this.ctx = null;
return null;
@@ -144,6 +145,7 @@ class BrushToolComponent extends React.Component {
style={{
width,
height,
+ zIndex,
}}
/>
);
@@ -169,6 +171,7 @@ const mapStateToProps = (state: GlobalChartState): StateProps => {
isBrushing: false,
isBrushAvailable: false,
brushArea: null,
+ zIndex: 0,
};
}
return {
@@ -178,6 +181,7 @@ const mapStateToProps = (state: GlobalChartState): StateProps => {
isBrushAvailable: getInternalIsBrushingAvailableSelector(state),
isBrushing: getInternalIsBrushingSelector(state),
brushArea: getInternalBrushAreaSelector(state),
+ zIndex: state.zIndex,
};
};
diff --git a/src/index.ts b/src/index.ts
index 62d049c7b5..f48f76e7e6 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -32,7 +32,6 @@ export { SeriesIdentifier } from './common/series_id';
export { XYChartSeriesIdentifier, DataSeriesDatum, FilledValues } from './chart_types/xy_chart/utils/series';
export { AnnotationTooltipFormatter, CustomAnnotationTooltip } from './chart_types/xy_chart/annotations/types';
export { GeometryValue, BandedAccessorType } from './utils/geometry';
-export { LegendStrategy } from './chart_types/partition_chart/state/selectors/get_highlighted_shapes';
export { LegendPath, LegendPathElement } from './state/actions/legend';
export { CategoryKey } from './common/category';
export {
@@ -89,3 +88,4 @@ export {
export { DataGenerator } from './utils/data_generators/data_generator';
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';
diff --git a/src/specs/settings.tsx b/src/specs/settings.tsx
index 654b0b9cfe..1d648d32fa 100644
--- a/src/specs/settings.tsx
+++ b/src/specs/settings.tsx
@@ -22,7 +22,7 @@ import React, { ComponentType, ReactChild } from 'react';
import { Spec } from '.';
import { Cell } from '../chart_types/heatmap/layout/types/viewmodel_types';
import { PrimitiveValue } from '../chart_types/partition_chart/layout/utils/group_by_rollup';
-import { LegendStrategy } from '../chart_types/partition_chart/state/selectors/get_highlighted_shapes';
+import { LegendStrategy } from '../chart_types/partition_chart/layout/utils/highlighted_geoms';
import { XYChartSeriesIdentifier } from '../chart_types/xy_chart/utils/series';
import { DomainRange } from '../chart_types/xy_chart/utils/specs';
import { SeriesIdentifier } from '../common/series_id';
diff --git a/src/state/chart_state.ts b/src/state/chart_state.ts
index e2fe1200ef..8e0233e0d5 100644
--- a/src/state/chart_state.ts
+++ b/src/state/chart_state.ts
@@ -25,6 +25,7 @@ import { HeatmapState } from '../chart_types/heatmap/state/chart_state';
import { PrimitiveValue } from '../chart_types/partition_chart/layout/utils/group_by_rollup';
import { PartitionState } from '../chart_types/partition_chart/state/chart_state';
import { XYAxisChartState } from '../chart_types/xy_chart/state/chart_state';
+import { CategoryKey } from '../common/category';
import { LegendItem, LegendItemExtraValues } from '../common/legend';
import { SeriesIdentifier, SeriesKey } from '../common/series_id';
import { TooltipAnchorPosition, TooltipInfo } from '../components/tooltip/types';
@@ -36,7 +37,7 @@ import { Point } from '../utils/point';
import { StateActions } from './actions';
import { CHART_RENDERED } from './actions/chart';
import { UPDATE_PARENT_DIMENSION } from './actions/chart_settings';
-import { SET_PERSISTED_COLOR, SET_TEMPORARY_COLOR, CLEAR_TEMPORARY_COLORS } from './actions/colors';
+import { CLEAR_TEMPORARY_COLORS, SET_PERSISTED_COLOR, SET_TEMPORARY_COLOR } from './actions/colors';
import { DOMElement } from './actions/dom_element';
import { EXTERNAL_POINTER_EVENT } from './actions/events';
import { LegendPath } from './actions/legend';
@@ -186,6 +187,7 @@ export interface InteractionsState {
highlightedLegendPath: LegendPath;
deselectedDataSeries: SeriesIdentifier[];
hoveredDOMElement: DOMElement | null;
+ drilldown: CategoryKey[];
}
/** @internal */
@@ -275,6 +277,7 @@ export const getInitialState = (chartId: string): GlobalChartState => ({
highlightedLegendPath: [],
deselectedDataSeries: [],
hoveredDOMElement: null,
+ drilldown: [],
},
externalEvents: {
pointer: null,
@@ -391,7 +394,7 @@ export const chartStoreReducer = (chartId: string) => {
return getInternalIsInitializedSelector(state) === InitStatus.Initialized
? {
...state,
- interactions: interactionsReducer(state.interactions, action, getLegendItemsSelector(state)),
+ interactions: interactionsReducer(state, action, getLegendItemsSelector(state)),
}
: state;
}
@@ -410,17 +413,14 @@ function chartTypeFromSpecs(specs: SpecList): ChartTypes | null {
return nonGlobalTypes[0];
}
+const constructors: Record InternalChartState | null> = {
+ [ChartTypes.Goal]: () => new GoalState(),
+ [ChartTypes.Partition]: () => new PartitionState(),
+ [ChartTypes.XYAxis]: () => new XYAxisChartState(),
+ [ChartTypes.Heatmap]: () => new HeatmapState(),
+ [ChartTypes.Global]: () => null,
+}; // with no default, TS signals if a new chart type isn't added here too
+
function newInternalState(chartType: ChartTypes | null): InternalChartState | null {
- switch (chartType) {
- case ChartTypes.Goal:
- return new GoalState();
- case ChartTypes.Partition:
- return new PartitionState();
- case ChartTypes.XYAxis:
- return new XYAxisChartState();
- case ChartTypes.Heatmap:
- return new HeatmapState();
- default:
- return null;
- }
+ return chartType ? constructors[chartType]() : null;
}
diff --git a/src/state/reducers/interactions.ts b/src/state/reducers/interactions.ts
index 5f26b693c9..c31580e225 100644
--- a/src/state/reducers/interactions.ts
+++ b/src/state/reducers/interactions.ts
@@ -17,9 +17,12 @@
* under the License.
*/
+import { ChartTypes } from '../../chart_types';
+import { getPickedShapesLayerValues } from '../../chart_types/partition_chart/state/selectors/picked_shapes';
import { getSeriesIndex } from '../../chart_types/xy_chart/utils/series';
import { LegendItem } from '../../common/legend';
import { SeriesIdentifier } from '../../common/series_id';
+import { LayerValue } from '../../specs';
import { getDelta } from '../../utils/point';
import { DOMElementActions, ON_DOM_ELEMENT_ENTER, ON_DOM_ELEMENT_LEAVE } from '../actions/dom_element';
import { KeyActions, ON_KEY_UP } from '../actions/key';
@@ -31,7 +34,7 @@ import {
ToggleDeselectSeriesAction,
} from '../actions/legend';
import { MouseActions, ON_MOUSE_DOWN, ON_MOUSE_UP, ON_POINTER_MOVE } from '../actions/mouse';
-import { InteractionsState } from '../chart_state';
+import { GlobalChartState, InteractionsState } from '../chart_state';
import { getInitialPointerState } from '../utils';
/**
@@ -46,10 +49,11 @@ const DRAG_DETECTION_PIXEL_DELTA = 4;
/** @internal */
export function interactionsReducer(
- state: InteractionsState,
+ globalState: GlobalChartState,
action: LegendActions | MouseActions | KeyActions | DOMElementActions,
legendItems: LegendItem[],
): InteractionsState {
+ const { interactions: state } = globalState;
switch (action.type) {
case ON_KEY_UP:
if (action.key === 'Escape') {
@@ -81,6 +85,7 @@ export function interactionsReducer(
case ON_MOUSE_DOWN:
return {
...state,
+ drilldown: getDrilldownData(globalState),
pointer: {
...state.pointer,
dragging: false,
@@ -169,7 +174,10 @@ export function interactionsReducer(
}
}
-/** @internal */
+/**
+ * Helper functions that currently depend on chart type eg. xy or partition
+ */
+
function toggleDeselectedDataSeries(
{ legendItemId: id, negate }: ToggleDeselectSeriesAction,
deselectedDataSeries: SeriesIdentifier[],
@@ -194,3 +202,11 @@ function toggleDeselectedDataSeries(
}
return [...deselectedDataSeries, id];
}
+
+function getDrilldownData(globalState: GlobalChartState) {
+ if (globalState.chartType !== ChartTypes.Partition) {
+ return [];
+ }
+ const layerValues: LayerValue[] = getPickedShapesLayerValues(globalState)[0];
+ return layerValues ? layerValues[layerValues.length - 1].path.map((n) => n.value) : [];
+}
diff --git a/src/utils/common.ts b/src/utils/common.ts
index 121a26562b..302f327106 100644
--- a/src/utils/common.ts
+++ b/src/utils/common.ts
@@ -426,7 +426,10 @@ export type ValueAccessor = (d: Datum) => number;
export type LabelAccessor = (value: PrimitiveValue) => string;
export type ShowAccessor = (value: PrimitiveValue) => boolean;
-/** @internal */
+/**
+ * Returns planar distance bewtween two points
+ * @internal
+ */
export function getDistance(a: Point, b: Point): number {
return Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));
}
diff --git a/stories/grids/3_lines.tsx b/stories/grids/3_lines.tsx
new file mode 100644
index 0000000000..fc0974bdb4
--- /dev/null
+++ b/stories/grids/3_lines.tsx
@@ -0,0 +1,97 @@
+/*
+ * 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 { array, boolean, color, number } from '@storybook/addon-knobs';
+import { startCase } from 'lodash';
+import React from 'react';
+
+import {
+ Axis,
+ LineSeries,
+ Chart,
+ Position,
+ ScaleType,
+ Settings,
+ TooltipType,
+ PartialTheme,
+ StrokeStyle,
+ StrokeDashArray,
+} from '../../src';
+import { SeededDataGenerator } from '../../src/mocks/utils';
+import { getTooltipTypeKnob } from '../utils/knobs';
+
+const dg = new SeededDataGenerator();
+const data = dg.generateBasicSeries(20);
+
+type LineProps = StrokeStyle & StrokeDashArray;
+
+const getLineStyles = ({ stroke, strokeWidth, dash }: Partial = {}, group?: string): LineProps => ({
+ stroke: color('Stroke', stroke ?? '#ccc', group),
+ strokeWidth: number('Stroke width', strokeWidth ?? 2, { min: 1, max: 6, range: true, step: 1 }, group),
+ dash: (
+ array(
+ 'Dash',
+ (dash ?? []).map((n) => `${n}`),
+ ',',
+ group,
+ ) ?? []
+ ).map((s) => parseInt(s, 10)),
+});
+
+const getAxisKnobs = (position: Position) => {
+ const title = `${startCase(position)} axis`;
+ const visible = boolean('Show gridline', true, title);
+ return {
+ id: position,
+ position,
+ title,
+ tickFormat: (n: number) => n.toFixed(1),
+ gridLine: {
+ visible,
+ opacity: number('Opacity', 0.2, { min: 0, max: 1, range: true, step: 0.1 }, title),
+ ...getLineStyles(
+ {
+ dash: position === Position.Left ? [4, 4] : undefined,
+ },
+ title,
+ ),
+ },
+ };
+};
+
+export const Example = () => {
+ const theme: PartialTheme = {
+ crosshair: {
+ line: getLineStyles({ stroke: 'red' }, 'Crosshair line'),
+ crossLine: getLineStyles({ stroke: 'red', dash: [4, 4] }, 'Crosshair cross line'),
+ },
+ };
+ return (
+
+
+
+
+
+
+ );
+};
diff --git a/stories/grids/grids.stories.tsx b/stories/grids/grids.stories.tsx
index a5f87767d1..a67b867c92 100644
--- a/stories/grids/grids.stories.tsx
+++ b/stories/grids/grids.stories.tsx
@@ -28,3 +28,4 @@ export default {
export { Example as basic } from './1_basic';
export { Example as multipleAxesWithTheSamePosition } from './2_multiple_axes';
+export { Example as lines } from './3_lines';
diff --git a/stories/icicle/01_unix_icicle.tsx b/stories/icicle/01_unix_icicle.tsx
index d4bbecdf3b..8c4fde3696 100644
--- a/stories/icicle/01_unix_icicle.tsx
+++ b/stories/icicle/01_unix_icicle.tsx
@@ -36,7 +36,7 @@ export const Example = () => {
valueAccessor={(d: Datum) => d.value as number}
valueFormatter={() => ''}
layers={getLayerSpec(color)}
- config={{ ...config, partitionLayout: PartitionLayout.icicle }}
+ config={{ ...config, partitionLayout: PartitionLayout.icicle, drilldown: true }}
/>
);
diff --git a/stories/icicle/02_unix_flame.tsx b/stories/icicle/02_unix_flame.tsx
index ed7c1d0ecc..3b117218af 100644
--- a/stories/icicle/02_unix_flame.tsx
+++ b/stories/icicle/02_unix_flame.tsx
@@ -24,7 +24,7 @@ import { STORYBOOK_LIGHT_THEME } from '../shared';
import { config, getFlatData, getLayerSpec, maxDepth } from '../utils/hierarchical_input_utils';
import { plasma18 as palette } from '../utils/utils';
-const color = palette.slice().reverse();
+const color = [...palette].reverse();
export const Example = () => {
return (
@@ -32,13 +32,10 @@ export const Example = () => {
{
- // eslint-disable-next-line no-console
- console.log(e);
- }}
/>
{
valueAccessor={(d: Datum) => d.value as number}
valueFormatter={() => ''}
layers={getLayerSpec(color)}
- config={{ ...config, partitionLayout: PartitionLayout.flame }}
+ config={{ ...config, partitionLayout: PartitionLayout.flame, drilldown: true }}
/>
);
diff --git a/stories/interactions/3_line_point_clicks.tsx b/stories/interactions/3_line_point_clicks.tsx
index 7f0b8abb67..4787ebd0de 100644
--- a/stories/interactions/3_line_point_clicks.tsx
+++ b/stories/interactions/3_line_point_clicks.tsx
@@ -35,7 +35,7 @@ export const Example = () => (
Number(d).toFixed(2)} />
(
{ x: 3, y: 6 },
]}
/>
+
);
diff --git a/stories/stylings/9_custom_series_colors_function.tsx b/stories/stylings/9_custom_series_colors_function.tsx
index 280a6d0c3d..baab617a74 100644
--- a/stories/stylings/9_custom_series_colors_function.tsx
+++ b/stories/stylings/9_custom_series_colors_function.tsx
@@ -39,7 +39,7 @@ export const Example = () => {
const lineColor = color('linelineSeriesColor', '#ff0');
const lineSeriesColorAccessor: SeriesColorAccessor = ({ specId, yAccessor, splitAccessors }) => {
- if (specId === 'lines' && yAccessor === 'y1' && splitAccessors.size === 0) {
+ if (specId === 'lines' && yAccessor === 'y' && splitAccessors.size === 0) {
return lineColor;
}
return null;
diff --git a/stories/utils/knobs.ts b/stories/utils/knobs.ts
index a9ff4ceb1b..3f83a277da 100644
--- a/stories/utils/knobs.ts
+++ b/stories/utils/knobs.ts
@@ -48,7 +48,7 @@ export const getChartRotationKnob = () =>
export const getTooltipTypeKnob = (
name = 'tooltip type',
- defaultValue = TooltipType.VerticalCursor,
+ defaultValue: TooltipType = TooltipType.VerticalCursor,
groupId?: string,
) =>
select(