diff --git a/__tests__/integration/snapshots/interaction/missing-area-tooltip-marker/step0.png b/__tests__/integration/snapshots/interaction/missing-area-tooltip-marker/step0.png new file mode 100644 index 0000000000..d396d19577 Binary files /dev/null and b/__tests__/integration/snapshots/interaction/missing-area-tooltip-marker/step0.png differ diff --git a/__tests__/integration/snapshots/interaction/missing-area-tooltip-marker/step1.png b/__tests__/integration/snapshots/interaction/missing-area-tooltip-marker/step1.png new file mode 100644 index 0000000000..30d8b770e7 Binary files /dev/null and b/__tests__/integration/snapshots/interaction/missing-area-tooltip-marker/step1.png differ diff --git a/__tests__/integration/snapshots/tooltip/aapl-area-missing-data-transpose/step0.html b/__tests__/integration/snapshots/tooltip/aapl-area-missing-data-transpose/step0.html index a821f242f8..69afb01d2b 100644 --- a/__tests__/integration/snapshots/tooltip/aapl-area-missing-data-transpose/step0.html +++ b/__tests__/integration/snapshots/tooltip/aapl-area-missing-data-transpose/step0.html @@ -24,7 +24,7 @@ > { y: 'Close', color: 'Symbol', key: 'Symbol', - title: (d) => new Date(d.Date).toUTCString(), }, state: { inactive: { opacity: '0.3' }, diff --git a/__tests__/plots/interaction/missing-area-tooltip-marker.ts b/__tests__/plots/interaction/missing-area-tooltip-marker.ts new file mode 100644 index 0000000000..1e03e1b38d --- /dev/null +++ b/__tests__/plots/interaction/missing-area-tooltip-marker.ts @@ -0,0 +1,55 @@ +import { G2Spec, PLOT_CLASS_NAME } from '../../../src'; +import { step } from './utils'; + +export function missingAreaTooltipMarker(): G2Spec { + return { + type: 'area', + data: { + type: 'fetch', + value: 'data/aapl.csv', + transform: [ + { + type: 'map', + callback: (d) => ({ + ...d, + close: d.date.getUTCMonth() <= 3 ? NaN : d.close, + }), + }, + ], + }, + encode: { + x: 'date', + y: 'close', + }, + style: { + connect: true, + connectFill: 'grey', + connectFillOpacity: 0.15, + }, + interaction: { + tooltip: { + body: false, + markerR: 10, + markerStrokeWidth: 5, + markerStroke: '#aaa', + }, + }, + }; +} + +missingAreaTooltipMarker.tooltip = true; + +missingAreaTooltipMarker.steps = ({ canvas }) => { + const { document } = canvas; + const [plot] = document.getElementsByClassName(PLOT_CLASS_NAME); + return [ + step(plot, 'pointermove', { + offsetX: 100, + offsetY: 350, + }), + step(plot, 'pointermove', { + offsetX: 176, + offsetY: 350, + }), + ]; +}; diff --git a/src/interaction/tooltip.ts b/src/interaction/tooltip.ts index 0bfbfda978..055989d1a8 100644 --- a/src/interaction/tooltip.ts +++ b/src/interaction/tooltip.ts @@ -236,6 +236,7 @@ function groupItems( scale, groupName, data = elements.map((d) => d['__data__']), + theme: Record = {}, ) { const key = (d) => (d instanceof Date ? +d : d); const T = unique( @@ -253,7 +254,7 @@ function groupItems( groupName !== undefined ? groupName : items.length <= 1 ? true : false; return definedItems.map( - ({ color = itemColorOf(element), name, ...item }) => { + ({ color = itemColorOf(element) || theme.color, name, ...item }) => { const name1 = useGroupName ? groupNameOf(scale, datum) || name : name || groupNameOf(scale, datum); @@ -347,30 +348,34 @@ function hideRuleY(root) { } } -// @todo Fill for composite g component. -function updateMarker(root, { data, style }) { +function updateMarker(root, { data, style, theme }) { if (root.markers) root.markers.forEach((d) => d.remove()); - const markers = data.map((d) => { - const [{ color, element }, point] = d; - const fill = - color || // encode value - element.style.fill || - element.style.stroke || - '#000'; - - const shape = new Circle({ - style: { - cx: point[0], - cy: point[1], - fill, - r: 4, - stroke: '#fff', - strokeWidth: 2, - ...style, - }, + const markers = data + .filter((d) => { + const [{ x, y }] = d; + return defined(x) && defined(y); + }) + .map((d) => { + const [{ color, element }, point] = d; + const fill = + color || // encode value + element.style.fill || + element.style.stroke || + theme.color; + + const shape = new Circle({ + style: { + cx: point[0], + cy: point[1], + fill, + r: 4, + stroke: '#fff', + strokeWidth: 2, + ...style, + }, + }); + return shape; }); - return shape; - }); for (const marker of markers) root.appendChild(marker); root.markers = markers; } @@ -432,6 +437,7 @@ export function seriesTooltip( enterable, mount, bounding, + theme, disableNative = false, marker = true, style: _style = {}, @@ -597,6 +603,7 @@ export function seriesTooltip( scale, groupName, selectedData, + theme, ); // Sort items and filter items. @@ -653,6 +660,7 @@ export function seriesTooltip( updateMarker(root, { data: filteredSeriesData, style: markerStyles, + theme, }); } @@ -760,6 +768,7 @@ export function tooltip( view, mount, bounding, + theme, shared = false, body = true, disableNative = false, @@ -782,7 +791,7 @@ export function tooltip( const data = group.length === 1 && !shared ? singleItem(group[0]) - : groupItems(group, scale, groupName); + : groupItems(group, scale, groupName, undefined, theme); // Sort items and sort. if (sortFunction) { @@ -904,7 +913,7 @@ export function Tooltip(options) { } = options; return (target, viewInstances, emitter) => { const { container, view } = target; - const { scale, markState, coordinate } = view; + const { scale, markState, coordinate, theme } = view; // Get default value from mark states. const defaultSeries = interactionKeyof(markState, 'seriesTooltip'); const defaultShowCrosshairs = interactionKeyof(markState, 'crosshairs'); @@ -915,6 +924,7 @@ export function Tooltip(options) { if (isSeries && hasSeries(markState) && !facet) { return seriesTooltip(plotArea, { ...rest, + theme, elements: selectG2Elements, scale, coordinate, @@ -943,6 +953,7 @@ export function Tooltip(options) { // @ts-ignore return seriesTooltip(plotArea.parentNode.parentNode, { ...rest, + theme, elements: () => elements, scale, coordinate, @@ -964,6 +975,7 @@ export function Tooltip(options) { item, emitter, view, + theme, shared, }); };