Skip to content

Commit

Permalink
fix(tooltip): dual axes with mult-series bar (#6295)
Browse files Browse the repository at this point in the history
  • Loading branch information
pearmini authored Jun 18, 2024
1 parent c1121fc commit a5db4cc
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<div
xmlns="http://www.w3.org/1999/xhtml"
class="g2-tooltip"
style="pointer-events: none; position: absolute; visibility: visible; z-index: 8; transition: visibility 0.2s cubic-bezier(0.23, 1, 0.32, 1), left 0.4s cubic-bezier(0.23, 1, 0.32, 1), top 0.4s cubic-bezier(0.23, 1, 0.32, 1); background-color: rgba(255, 255, 255, 0.96); box-shadow: 0 6px 12px 0 rgba(0, 0, 0, 0.12); border-radius: 4px; color: rgba(0, 0, 0, 0.65); font-size: 12px; line-height: 20px; padding: 12px; min-width: 120px; max-width: 360px; font-family: Roboto-Regular; left: 210px; top: 310px;"
>
<div
class="g2-tooltip-title"
style="color: rgba(0, 0, 0, 0.45); overflow: hidden; white-space: nowrap; text-overflow: ellipsis;"
>
10:15
</div>
<ul
class="g2-tooltip-list"
style="margin: 0px; list-style-type: none; padding: 0px;"
>
<li
class="g2-tooltip-list-item"
data-index="0"
style="list-style-type: none; display: flex; line-height: 2em; align-items: center; justify-content: space-between; white-space: nowrap;"
>
<span
class="g2-tooltip-list-item-name"
style="display: flex; align-items: center; max-width: 216px;"
>
<span
class="g2-tooltip-list-item-marker"
style="background: rgb(213, 128, 255); width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-right: 4px;"
/>
<span
class="g2-tooltip-list-item-name-label"
title="mock"
style="flex: 1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;"
>
mock
</span>
</span>
<span
class="g2-tooltip-list-item-value"
title="4"
style="display: inline-block; float: right; flex: 1; text-align: right; min-width: 28px; margin-left: 30px; color: rgba(0, 0, 0, 0.85); overflow: hidden; white-space: nowrap; text-overflow: ellipsis;"
>
4
</span>
</li>
<li
class="g2-tooltip-list-item"
data-index="1"
style="list-style-type: none; display: flex; line-height: 2em; align-items: center; justify-content: space-between; white-space: nowrap;"
>
<span
class="g2-tooltip-list-item-name"
style="display: flex; align-items: center; max-width: 216px;"
>
<span
class="g2-tooltip-list-item-marker"
style="background: rgb(240, 136, 77); width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-right: 4px;"
/>
<span
class="g2-tooltip-list-item-name-label"
title="call"
style="flex: 1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;"
>
call
</span>
</span>
<span
class="g2-tooltip-list-item-value"
title="2"
style="display: inline-block; float: right; flex: 1; text-align: right; min-width: 28px; margin-left: 30px; color: rgba(0, 0, 0, 0.85); overflow: hidden; white-space: nowrap; text-overflow: ellipsis;"
>
2
</span>
</li>
<li
class="g2-tooltip-list-item"
data-index="2"
style="list-style-type: none; display: flex; line-height: 2em; align-items: center; justify-content: space-between; white-space: nowrap;"
>
<span
class="g2-tooltip-list-item-name"
style="display: flex; align-items: center; max-width: 216px;"
>
<span
class="g2-tooltip-list-item-marker"
style="background: rgb(23, 131, 255); width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-right: 4px;"
/>
<span
class="g2-tooltip-list-item-name-label"
title="waiting"
style="flex: 1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;"
>
waiting
</span>
</span>
<span
class="g2-tooltip-list-item-value"
title="6"
style="display: inline-block; float: right; flex: 1; text-align: right; min-width: 28px; margin-left: 30px; color: rgba(0, 0, 0, 0.85); overflow: hidden; white-space: nowrap; text-overflow: ellipsis;"
>
6
</span>
</li>
<li
class="g2-tooltip-list-item"
data-index="3"
style="list-style-type: none; display: flex; line-height: 2em; align-items: center; justify-content: space-between; white-space: nowrap;"
>
<span
class="g2-tooltip-list-item-name"
style="display: flex; align-items: center; max-width: 216px;"
>
<span
class="g2-tooltip-list-item-marker"
style="background: rgb(0, 201, 201); width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-right: 4px;"
/>
<span
class="g2-tooltip-list-item-name-label"
title="people"
style="flex: 1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;"
>
people
</span>
</span>
<span
class="g2-tooltip-list-item-value"
title="3"
style="display: inline-block; float: right; flex: 1; text-align: right; min-width: 28px; margin-left: 30px; color: rgba(0, 0, 0, 0.85); overflow: hidden; white-space: nowrap; text-overflow: ellipsis;"
>
3
</span>
</li>
</ul>
</div>
1 change: 1 addition & 0 deletions __tests__/plots/tooltip/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,4 @@ export { disastersPointSlider } from './disasters-point-slider';
export { alphabetText } from './alphabet-text';
export { mockLineFlex } from './mock-line-flex';
export { mockTooltipClosestTransposed } from './mock-tooltip-closest-transposed';
export { mockDualBarShareTooltip } from './mock-dual-bar-share-tooltip';
53 changes: 53 additions & 0 deletions __tests__/plots/tooltip/mock-dual-bar-share-tooltip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { G2Spec } from '../../../src';
import { seriesTooltipSteps } from './utils';

export async function mockDualBarShareTooltip(): Promise<G2Spec> {
return {
type: 'view',
data: [
{ time: '10:10', call: 4, waiting: 2, people: 2, mock: 3 },
{ time: '10:15', call: 2, waiting: 6, people: 3, mock: 4 },
{ time: '10:20', call: 13, waiting: 2, people: 5, mock: 1 },
{ time: '10:25', call: 9, waiting: 9, people: 1, mock: 2 },
{ time: '10:30', call: 5, waiting: 2, people: 3, mock: 5 },
{ time: '10:35', call: 8, waiting: 2, people: 1, mock: 3 },
{ time: '10:40', call: 13, waiting: 1, people: 2, mock: 2 },
],
children: [
{
type: 'interval',
encode: {
x: 'time',
y: 'waiting',
color: () => 'waiting',
series: () => 'waiting',
},
scale: { y: { nice: true } },
axis: { y: { title: null } },
},
{
type: 'interval',
encode: {
x: 'time',
y: 'people',
color: () => 'people',
series: () => 'people',
},
scale: { y: { key: '2' } },
axis: { y: { position: 'right', grid: null, title: null } },
},
{
type: 'line',
encode: { x: 'time', y: 'call', color: () => 'call' },
scale: { series: { independent: true } },
},
{
type: 'line',
encode: { x: 'time', y: 'mock', color: () => 'mock' },
scale: { y: { key: '2' }, series: { independent: true } },
},
],
};
}

mockDualBarShareTooltip.steps = seriesTooltipSteps([200, 300]);
71 changes: 45 additions & 26 deletions src/interaction/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,12 @@ export function seriesTooltip(
if (seriesX) seriesElements.push(element);
else if (title || items) itemElements.push(element);
}
const inInterval = (d) => d.markType === 'interval';
const isBar =
itemElements.length &&
itemElements.every(inInterval) &&
!isPolar(coordinate);
const xof = (d) => d.__data__.x;

// For band scale x, find the closest series element to focus,
// useful for interval + line mark.
Expand All @@ -616,14 +622,18 @@ export function seriesTooltip(
const { min, max } = d.getLocalBounds();
return sort([min[index], max[index]]);
};
// Sort itemElements by x or y.
itemElements.sort((a, b) => {
const [minA, maxA] = extent(a);
const [minB, maxB] = extent(b);
const midA = (minA + maxA) / 2;
const midB = (minB + maxB) / 2;
return transposed ? midB - midA : midA - midB;
});

// Sort itemElements for bisector search.
if (isBar) elements.sort((a, b) => xof(a) - xof(b));
else {
itemElements.sort((a, b) => {
const [minA, maxA] = extent(a);
const [minB, maxB] = extent(b);
const midA = (minA + maxA) / 2;
const midB = (minB + maxB) / 2;
return transposed ? midB - midA : midA - midB;
});
}

// Get sortedIndex and X for each series elements
const elementSortedX = new Map(
Expand Down Expand Up @@ -662,24 +672,33 @@ export function seriesTooltip(
return I[i];
};

const elementsByFocus = (focus, elements) => {
const index = transposed ? 1 : 0;
const x = focus[index];
const filtered = elements.filter((element) => {
const [min, max] = extent(element);
return x >= min && x <= max;
});
// If closet is true, always find at least one element.
if (!closest || filtered.length > 0) return filtered;

// Search the closet element to the focus.
const search = bisector((element) => {
const [min, max] = extent(element);
return (min + max) / 2;
}).center;
const i = search(elements, x);
return [elements[i]].filter(defined);
};
const elementsByFocus = isBar
? (focus, elements) => {
const search = bisector(xof).center;
const i = search(elements, abstractX(focus));
const find = elements[i];
const groups = group(elements, xof);
const selected = groups.get(xof(find));
return selected;
}
: (focus, elements) => {
const index = transposed ? 1 : 0;
const x = focus[index];
const filtered = elements.filter((element) => {
const [min, max] = extent(element);
return x >= min && x <= max;
});
// If closet is true, always find at least one element.
if (!closest || filtered.length > 0) return filtered;

// Search the closet element to the focus.
const search = bisector((element) => {
const [min, max] = extent(element);
return (min + max) / 2;
}).center;
const i = search(elements, x);
return [elements[i]].filter(defined);
};

const seriesData = (element, index) => {
const { __data__: data } = element;
Expand Down

0 comments on commit a5db4cc

Please sign in to comment.