Skip to content

Commit 3972b04

Browse files
authored
Merge pull request #633 from adobe/highlightingRefactor
refactor: add hovered item opacity controls to line
2 parents d1d1900 + 2f8a765 commit 3972b04

14 files changed

+66
-47
lines changed

packages/vega-spec-builder/src/chartTooltip/chartTooltipUtils.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ describe('addHoveredItemOpacityRules()', () => {
186186
expect(opacityRules[0].test).toContain(HOVERED_ITEM);
187187
expect(opacityRules[1]).toBe(DEFAULT_OPACITY_RULE);
188188

189-
opacityRules = [DEFAULT_OPACITY_RULE, { description: 'Hover', test: 'this is a test' }];
189+
opacityRules = [DEFAULT_OPACITY_RULE, { description: 'Hover', test: `this is a test ${HOVERED_ITEM}` }];
190190
addHoveredItemOpacityRules(opacityRules, getDefautltMarkOptions());
191191
expect(opacityRules).toHaveLength(3);
192192
expect(opacityRules[0]).toBe(DEFAULT_OPACITY_RULE);

packages/vega-spec-builder/src/chartTooltip/chartTooltipUtils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,10 @@ export const addHighlightMarkOpacityRules = (
219219
}
220220
};
221221

222-
export const addHoveredItemOpacityRules = (opacityRules: ({ test?: string, description?: string } & NumericValueRef)[], markOptions: TooltipParentOptions) => {
222+
export const addHoveredItemOpacityRules = (opacityRules: ({ test?: string } & NumericValueRef)[], markOptions: TooltipParentOptions) => {
223223
const { name: markName } = markOptions;
224224
// find the index of the first hover rule
225-
const startIndex = opacityRules.findIndex((rule) => rule.description?.startsWith(`Hover`)) + 1;
225+
const startIndex = opacityRules.findIndex((rule) => rule.test?.includes(HOVERED_ITEM)) + 1;
226226

227227
const hoveredItemSignal = `${markName}_${HOVERED_ITEM}`;
228228

packages/vega-spec-builder/src/line/lineMarkUtils.test.ts

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ import {
1313
COLOR_SCALE,
1414
DEFAULT_OPACITY_RULE,
1515
DEFAULT_TRANSFORMED_TIME_DIMENSION,
16+
HIGHLIGHT_CONTRAST_RATIO,
1617
HIGHLIGHTED_SERIES,
18+
HOVERED_ITEM,
1719
SELECTED_SERIES,
1820
SERIES_ID,
1921
} from '@spectrum-charts/constants';
@@ -121,11 +123,8 @@ describe('getLineOpacity()', () => {
121123
chartTooltips: [{}],
122124
});
123125
expect(opacityRule).toEqual([
124-
{ test: `isValid(${HIGHLIGHTED_SERIES}) && ${HIGHLIGHTED_SERIES} !== datum.${SERIES_ID}`, value: 0.2 },
125-
{
126-
test: `length(data('line0_highlightedData')) > 0 && indexof(pluck(data('line0_highlightedData'), '${SERIES_ID}'), datum.${SERIES_ID}) === -1`,
127-
value: 0.2,
128-
},
126+
{ test: `isValid(line0_${HOVERED_ITEM})`, signal: `line0_${HOVERED_ITEM}.${SERIES_ID} === datum.${SERIES_ID} ? 1 : 1 / ${HIGHLIGHT_CONTRAST_RATIO}`},
127+
{ test: `isValid(${HIGHLIGHTED_SERIES}) && ${HIGHLIGHTED_SERIES} !== datum.${SERIES_ID}`, value: 1 / HIGHLIGHT_CONTRAST_RATIO },
129128
{ value: 1 },
130129
]);
131130
});
@@ -138,12 +137,9 @@ describe('getLineOpacity()', () => {
138137
chartPopovers: [{}],
139138
});
140139
expect(opacityRule).toEqual([
141-
{ test: `isValid(${HIGHLIGHTED_SERIES}) && ${HIGHLIGHTED_SERIES} !== datum.${SERIES_ID}`, value: 0.2 },
142-
{
143-
test: `length(data('line0_highlightedData')) > 0 && indexof(pluck(data('line0_highlightedData'), '${SERIES_ID}'), datum.${SERIES_ID}) === -1`,
144-
value: 0.2,
145-
},
146-
{ test: `isValid(${SELECTED_SERIES}) && ${SELECTED_SERIES} !== datum.${SERIES_ID}`, value: 0.2 },
140+
{ signal: `line0_hoveredItem.${SERIES_ID} === datum.${SERIES_ID} ? 1 : 1 / ${HIGHLIGHT_CONTRAST_RATIO}`, test: 'isValid(line0_hoveredItem)' },
141+
{ test: `isValid(${HIGHLIGHTED_SERIES}) && ${HIGHLIGHTED_SERIES} !== datum.${SERIES_ID}`, value: 1 / HIGHLIGHT_CONTRAST_RATIO },
142+
{ test: `isValid(${SELECTED_SERIES}) && ${SELECTED_SERIES} !== datum.${SERIES_ID}`, value: 1 / HIGHLIGHT_CONTRAST_RATIO },
147143
{ value: 1 },
148144
]);
149145
});
@@ -164,10 +160,7 @@ describe('getLineOpacity()', () => {
164160
chartTooltips: [{}],
165161
isHighlightedByGroup: true,
166162
});
167-
expect(opacityRule).toHaveLength(4);
168-
expect(opacityRule[0]).toHaveProperty(
169-
'test',
170-
`indexof(pluck(data('line0_highlightedData'), '${SERIES_ID}'), datum.${SERIES_ID}) !== -1`
171-
);
163+
expect(opacityRule).toHaveLength(3);
164+
expect(opacityRule[0]).toHaveProperty('test', `length(data('line0_highlightedData'))`);
172165
});
173166
});

packages/vega-spec-builder/src/line/lineMarkUtils.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
DEFAULT_OPACITY_RULE,
1717
HIGHLIGHTED_SERIES,
1818
HIGHLIGHT_CONTRAST_RATIO,
19+
HOVERED_ITEM,
1920
SELECTED_SERIES,
2021
SERIES_ID,
2122
} from '@spectrum-charts/constants';
@@ -104,8 +105,13 @@ export const getLineOpacity = ({
104105

105106
if (isHighlightedByGroup) {
106107
strokeOpacityRules.push({
107-
test: `indexof(pluck(data('${interactiveMarkName}_highlightedData'), '${SERIES_ID}'), datum.${SERIES_ID}) !== -1`,
108-
value: 1,
108+
test: `length(data('${interactiveMarkName}_highlightedData'))`,
109+
signal: `indexof(pluck(data('${interactiveMarkName}_highlightedData'), '${SERIES_ID}'), datum.${SERIES_ID}) !== -1 ? 1 : 1 / ${HIGHLIGHT_CONTRAST_RATIO}`,
110+
})
111+
} else {
112+
strokeOpacityRules.push({
113+
test: `isValid(${interactiveMarkName}_${HOVERED_ITEM})`,
114+
signal: `${interactiveMarkName}_${HOVERED_ITEM}.${SERIES_ID} === datum.${SERIES_ID} ? 1 : 1 / ${HIGHLIGHT_CONTRAST_RATIO}`,
109115
});
110116
}
111117

@@ -115,10 +121,6 @@ export const getLineOpacity = ({
115121
test: `isValid(${HIGHLIGHTED_SERIES}) && ${HIGHLIGHTED_SERIES} !== datum.${SERIES_ID}`,
116122
value: 1 / HIGHLIGHT_CONTRAST_RATIO,
117123
},
118-
{
119-
test: `length(data('${interactiveMarkName}_highlightedData')) > 0 && indexof(pluck(data('${interactiveMarkName}_highlightedData'), '${SERIES_ID}'), datum.${SERIES_ID}) === -1`,
120-
value: 1 / HIGHLIGHT_CONTRAST_RATIO,
121-
}
122124
);
123125

124126
if (popoverMarkName) {

packages/vega-spec-builder/src/line/lineSpecBuilder.test.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
FILTERED_TABLE,
2424
HIGHLIGHTED_ITEM,
2525
HIGHLIGHTED_SERIES,
26+
HOVERED_ITEM,
2627
LINEAR_PADDING,
2728
MARK_ID,
2829
SERIES_ID,
@@ -557,11 +558,13 @@ describe('lineSpecBuilder', () => {
557558
...defaultLineOptions,
558559
metricRanges: [{ metricEnd: 'end', metricStart: 'start', displayOnHover: true }],
559560
});
560-
expect(signals).toHaveLength(defaultSignals.length);
561+
expect(signals).toHaveLength(defaultSignals.length + 1);
561562
expect(signals[0]).toHaveProperty('name', HIGHLIGHTED_ITEM);
562563
expect(signals[0].on).toHaveLength(2);
563564
expect(signals[2]).toHaveProperty('name', HIGHLIGHTED_SERIES);
564565
expect(signals[2].on).toHaveLength(2);
566+
expect(signals.at(-1)).toHaveProperty('name', `${defaultLineOptions.name}_${HOVERED_ITEM}`);
567+
expect(signals.at(-1)?.on).toHaveLength(2);
565568
});
566569

567570
test('adds hover signals when displayPointMark is not undefined', () => {
@@ -574,11 +577,13 @@ describe('lineSpecBuilder', () => {
574577
staticPoint: 'staticPoint',
575578
metricRanges: [{ metricEnd: 'end', metricStart: 'start', displayOnHover: true }],
576579
});
577-
expect(signals).toHaveLength(defaultSignals.length);
580+
expect(signals).toHaveLength(defaultSignals.length + 1);
578581
expect(signals[0]).toHaveProperty('name', HIGHLIGHTED_ITEM);
579582
expect(signals[0].on).toHaveLength(2);
580583
expect(signals[2]).toHaveProperty('name', HIGHLIGHTED_SERIES);
581584
expect(signals[2].on).toHaveLength(2);
585+
expect(signals.at(-1)).toHaveProperty('name', `${defaultLineOptions.name}_${HOVERED_ITEM}`);
586+
expect(signals.at(-1)?.on).toHaveLength(2);
582587
});
583588

584589
test('hover signals with interactionMode item', () => {
@@ -587,11 +592,13 @@ describe('lineSpecBuilder', () => {
587592
interactionMode: 'item',
588593
chartTooltips: [{}],
589594
});
590-
expect(signals).toHaveLength(defaultSignals.length);
595+
expect(signals).toHaveLength(defaultSignals.length + 1);
591596
expect(signals[0]).toHaveProperty('name', HIGHLIGHTED_ITEM);
592597
expect(signals[0].on).toHaveLength(8);
593598
expect(signals[2]).toHaveProperty('name', HIGHLIGHTED_SERIES);
594599
expect(signals[2].on).toHaveLength(8);
600+
expect(signals.at(-1)).toHaveProperty('name', `${defaultLineOptions.name}_${HOVERED_ITEM}`);
601+
expect(signals.at(-1)?.on).toHaveLength(2);
595602
});
596603
});
597604
});

packages/vega-spec-builder/src/line/lineSpecBuilder.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import {
3535
getMetricRanges,
3636
} from '../metricRange/metricRangeUtils';
3737
import { addContinuousDimensionScale, addFieldToFacetScaleDomain, addMetricScale } from '../scale/scaleSpecBuilder';
38-
import { addHighlightedItemSignalEvents, addHighlightedSeriesSignalEvents } from '../signal/signalSpecBuilder';
38+
import { addHighlightedItemSignalEvents, addHighlightedSeriesSignalEvents, addHoveredItemSignal } from '../signal/signalSpecBuilder';
3939
import { getFacetsFromOptions } from '../specUtils';
4040
import { addTrendlineData, getTrendlineMarks, getTrendlineScales, setTrendlineSignals } from '../trendline';
4141
import { ColorScheme, HighlightedItem, LineOptions, LineSpecOptions, ScSpec } from '../types';
@@ -141,6 +141,7 @@ export const addSignals = produce<Signal[], [LineSpecOptions]>((signals, options
141141
if (!isInteractive(options)) return;
142142
addHighlightedItemSignalEvents(signals, `${name}_voronoi`, idKey, 2);
143143
addHighlightedSeriesSignalEvents(signals, `${name}_voronoi`, 2);
144+
addHoveredItemSignal(signals, name, `${name}_voronoi`, 2);
144145
addHoverSignals(signals, options);
145146
addTooltipSignals(signals, options);
146147
});

packages/vega-spec-builder/src/scatter/scatterMarkUtils.test.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { GroupMark } from 'vega';
1414
import {
1515
DEFAULT_OPACITY_RULE,
1616
HIGHLIGHTED_ITEM,
17+
HOVERED_ITEM,
1718
MARK_ID,
1819
SELECTED_ITEM,
1920
SYMBOL_SIZE_SCALE,
@@ -52,28 +53,29 @@ describe('getOpacity()', () => {
5253
});
5354
test('should include hover rules if tooltip exists', () => {
5455
const opacity = getOpacity({ ...defaultScatterOptions, chartTooltips: [{}] });
55-
expect(opacity).toHaveLength(3);
56-
expect(opacity[0]).toHaveProperty(
56+
expect(opacity).toHaveLength(4);
57+
expect(opacity[0]).toHaveProperty('test', `isValid(scatter0_${HOVERED_ITEM})`);
58+
expect(opacity[1]).toHaveProperty(
5759
'test',
5860
`isArray(${HIGHLIGHTED_ITEM}) && length(${HIGHLIGHTED_ITEM}) > 0 && indexof(${HIGHLIGHTED_ITEM}, datum.${MARK_ID}) === -1`
5961
);
60-
expect(opacity[1]).toHaveProperty(
62+
expect(opacity[2]).toHaveProperty(
6163
'test',
6264
`!isArray(${HIGHLIGHTED_ITEM}) && isValid(${HIGHLIGHTED_ITEM}) && ${HIGHLIGHTED_ITEM} !== datum.${MARK_ID}`
6365
);
6466
});
6567
test('should include select rule if popover exists', () => {
6668
const opacity = getOpacity({ ...defaultScatterOptions, chartPopovers: [{}] });
67-
expect(opacity).toHaveLength(4);
68-
expect(opacity[0]).toHaveProperty(
69+
expect(opacity).toHaveLength(5);
70+
expect(opacity[1]).toHaveProperty(
6971
'test',
7072
`isArray(${HIGHLIGHTED_ITEM}) && length(${HIGHLIGHTED_ITEM}) > 0 && indexof(${HIGHLIGHTED_ITEM}, datum.${MARK_ID}) === -1`
7173
);
72-
expect(opacity[1]).toHaveProperty(
74+
expect(opacity[2]).toHaveProperty(
7375
'test',
7476
`!isArray(${HIGHLIGHTED_ITEM}) && isValid(${HIGHLIGHTED_ITEM}) && ${HIGHLIGHTED_ITEM} !== datum.${MARK_ID}`
7577
);
76-
expect(opacity[2]).toHaveProperty('test', `isValid(${SELECTED_ITEM}) && ${SELECTED_ITEM} !== datum.${MARK_ID}`);
78+
expect(opacity[3]).toHaveProperty('test', `isValid(${SELECTED_ITEM}) && ${SELECTED_ITEM} !== datum.${MARK_ID}`);
7779
});
7880
});
7981

packages/vega-spec-builder/src/scatter/scatterMarkUtils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
} from '@spectrum-charts/constants';
2121
import { spectrumColors } from '@spectrum-charts/themes';
2222

23-
import { addHighlightMarkOpacityRules } from '../chartTooltip/chartTooltipUtils';
23+
import { addHighlightMarkOpacityRules, addHoveredItemOpacityRules } from '../chartTooltip/chartTooltipUtils';
2424
import {
2525
getColorProductionRule,
2626
getLineWidthProductionRule,
@@ -118,6 +118,7 @@ export const getOpacity = (scatterOptions: ScatterSpecOptions): ({ test?: string
118118

119119
const rules: ({ test?: string } & NumericValueRef)[] = [];
120120
addHighlightMarkOpacityRules(rules, scatterOptions);
121+
addHoveredItemOpacityRules(rules, scatterOptions);
121122
if (hasPopover(scatterOptions)) {
122123
rules.push({
123124
test: `isValid(${SELECTED_ITEM}) && ${SELECTED_ITEM} !== datum.${idKey}`,

packages/vega-spec-builder/src/scatter/scatterSpecBuilder.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ describe('addSignals()', () => {
8686
...defaultScatterOptions,
8787
chartTooltips: [{}],
8888
});
89-
expect(signals).toHaveLength(defaultSignals.length);
89+
expect(signals).toHaveLength(defaultSignals.length + 1);
9090
expect(signals[0].name).toBe(HIGHLIGHTED_ITEM);
9191
expect(signals[0].on).toHaveLength(2);
9292
});
@@ -95,7 +95,7 @@ describe('addSignals()', () => {
9595
...defaultScatterOptions,
9696
trendlines: [{ displayOnHover: true }],
9797
});
98-
expect(signals).toHaveLength(defaultSignals.length);
98+
expect(signals).toHaveLength(defaultSignals.length + 1);
9999
expect(signals[0].name).toBe(HIGHLIGHTED_ITEM);
100100
expect(signals[0].on).toHaveLength(2);
101101
});

packages/vega-spec-builder/src/scatter/scatterSpecBuilder.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import { addTimeTransform, getFilteredTooltipData, getTableData } from '../data/
3333
import { getInteractiveMarkName, hasPopover, isInteractive } from '../marks/markUtils';
3434
import { addContinuousDimensionScale, addFieldToFacetScaleDomain, addMetricScale } from '../scale/scaleSpecBuilder';
3535
import { setScatterPathScales } from '../scatterPath';
36-
import { addHighlightedItemSignalEvents } from '../signal/signalSpecBuilder';
36+
import { addHighlightedItemSignalEvents, addHoveredItemSignal } from '../signal/signalSpecBuilder';
3737
import { addTrendlineData, getTrendlineScales, setTrendlineSignals } from '../trendline';
3838
import { ColorScheme, HighlightedItem, ScSpec, ScatterOptions, ScatterSpecOptions } from '../types';
3939
import { addScatterMarks } from './scatterMarkUtils';
@@ -136,13 +136,14 @@ export const addData = produce<Data[], [ScatterSpecOptions]>((data, scatterOptio
136136
* @param scatterOptions ScatterSpecOptions
137137
*/
138138
export const addSignals = produce<Signal[], [ScatterSpecOptions]>((signals, scatterOptions) => {
139-
const { idKey, name } = scatterOptions;
139+
const { idKey, name: scatterName } = scatterOptions;
140140
// trendline signals
141141
setTrendlineSignals(signals, scatterOptions);
142142

143143
if (!isInteractive(scatterOptions)) return;
144144
// interactive signals
145-
addHighlightedItemSignalEvents(signals, `${name}_voronoi`, idKey, 2);
145+
addHighlightedItemSignalEvents(signals, `${scatterName}_voronoi`, idKey, 2);
146+
addHoveredItemSignal(signals, scatterName, `${scatterName}_voronoi`, 2);
146147
addTooltipSignals(signals, scatterOptions);
147148
});
148149

0 commit comments

Comments
 (0)