Skip to content

Commit fb792b4

Browse files
authored
Plot tooltip for truncated titles (#4858)
* Add a tooltip to plots with long titles that are cut by Vega * Add tooltip inside zoomed in plot and correctly make it interactive * Add tooltips for plot titles and axis that get truncated * Self-review * Add tests * Fix tests * Fix lint error * Fix story linting
1 parent ee444db commit fb792b4

File tree

20 files changed

+350
-87
lines changed

20 files changed

+350
-87
lines changed

extension/src/plots/model/collect.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import get from 'lodash.get'
22
import { TopLevelSpec } from 'vega-lite'
3-
import { VisualizationSpec } from 'react-vega'
43
import { createSpec, CustomPlotsOrderValue, getFullValuePath } from './custom'
54
import {
65
ColorScale,
@@ -23,7 +22,8 @@ import { TemplateOrder } from '../paths/collect'
2322
import {
2423
extendVegaSpec,
2524
isMultiViewPlot,
26-
truncateVerticalTitle
25+
SpecWithTitles,
26+
truncateVegaSpecTitles
2727
} from '../vega/util'
2828
import { definedAndNonEmpty } from '../../util/array'
2929
import {
@@ -139,22 +139,22 @@ const getCustomPlotData = (
139139
const completeColorScale = fillColorScale(experiments, colorScale, valueIds)
140140

141141
const [{ param: paramVal, metric: metricVal }] = values
142-
const yTitle = truncateVerticalTitle(metric, nbItemsPerRow, height) as string
143142

144143
const spec = createSpec(
145-
yTitle,
146144
metric,
147145
param,
148146
typeof metricVal,
149147
typeof paramVal,
150148
completeColorScale
151149
)
152150

151+
const updatedSpec = truncateVegaSpecTitles(spec, nbItemsPerRow, height)
152+
153153
return {
154154
id: getCustomPlotId(metric, param),
155155
metric,
156156
param,
157-
spec,
157+
spec: updatedSpec,
158158
values
159159
} as CustomPlotData
160160
}
@@ -522,7 +522,7 @@ const collectTemplatePlot = (
522522
multiSourceEncoding: MultiSourceEncoding
523523
) => {
524524
const isMultiView = isMultiViewPlot(
525-
JSON.parse(template) as TopLevelSpec | VisualizationSpec
525+
JSON.parse(template) as TopLevelSpec | SpecWithTitles
526526
)
527527
const multiSourceEncodingUpdate = multiSourceEncoding[path] || {}
528528
const { datapoints, revisions } = transformRevisionData(
@@ -545,7 +545,7 @@ const collectTemplatePlot = (
545545
...multiSourceEncodingUpdate,
546546
color: revisionColors
547547
}
548-
) as VisualizationSpec
548+
) as SpecWithTitles
549549

550550
acc.push({
551551
content,
@@ -638,7 +638,7 @@ export const collectSelectedTemplatePlotRawData = ({
638638
multiSourceEncodingUpdate: { strokeDash: StrokeDashEncoding }
639639
}) => {
640640
const isMultiView = isMultiViewPlot(
641-
JSON.parse(template) as TopLevelSpec | VisualizationSpec
641+
JSON.parse(template) as TopLevelSpec | SpecWithTitles
642642
)
643643
const { datapoints } = transformRevisionData(
644644
path,

extension/src/plots/model/custom.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { VisualizationSpec } from 'react-vega'
1+
import { TopLevelSpec } from 'vega-lite'
22
import { getCustomPlotId } from './collect'
33
import { Column, ColumnType } from '../../experiments/webview/contract'
44
import { FILE_SEPARATOR } from '../../experiments/columns/paths'
@@ -56,7 +56,6 @@ const getSpecDataType = (type: string) =>
5656
type === 'number' ? 'quantitative' : 'nominal'
5757

5858
export const createSpec = (
59-
title: string,
6059
metric: string,
6160
param: string,
6261
metricType: string,
@@ -89,7 +88,7 @@ export const createSpec = (
8988
scale: {
9089
zero: false
9190
},
92-
title,
91+
title: metric,
9392
type: getSpecDataType(metricType)
9493
}
9594
},
@@ -120,4 +119,4 @@ export const createSpec = (
120119
}
121120
],
122121
width: 'container'
123-
}) as VisualizationSpec
122+
}) as TopLevelSpec

extension/src/plots/paths/collect.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { join, sep } from 'path'
2-
import { VisualizationSpec } from 'react-vega'
32
import isEqual from 'lodash.isequal'
43
import {
54
collectEncodingElements,
@@ -16,6 +15,7 @@ import { Shape, StrokeDash } from '../multiSource/constants'
1615
import { EXPERIMENT_WORKSPACE_ID, PlotsOutput } from '../../cli/dvc/contract'
1716
import { REVISIONS } from '../../test/fixtures/plotsDiff'
1817
import { FIELD_SEPARATOR } from '../../cli/dvc/constants'
18+
import { SpecWithTitles } from '../vega/util'
1919

2020
const plotsDiffFixturePaths: PlotPath[] = [
2121
{
@@ -112,7 +112,7 @@ describe('collectPaths', () => {
112112
data: {
113113
[remainingPath]: [
114114
{
115-
content: {},
115+
content: {} as SpecWithTitles,
116116
datapoints: {
117117
[fetchedRevs[0]]: [
118118
{
@@ -212,15 +212,15 @@ describe('collectPaths', () => {
212212
data: {
213213
[join('logs', 'scalars', 'acc.tsv')]: [
214214
{
215-
content: {},
215+
content: {} as SpecWithTitles,
216216
datapoints: { [EXPERIMENT_WORKSPACE_ID]: [{}] },
217217
revisions,
218218
type: PlotsType.VEGA
219219
}
220220
],
221221
[join('logs', 'scalars', 'loss.tsv')]: [
222222
{
223-
content: {},
223+
content: {} as SpecWithTitles,
224224
datapoints: { [EXPERIMENT_WORKSPACE_ID]: [{}] },
225225
revisions,
226226
type: PlotsType.VEGA
@@ -237,15 +237,15 @@ describe('collectPaths', () => {
237237
{
238238
content: {
239239
facet: { field: 'rev', type: 'nominal' }
240-
} as VisualizationSpec,
240+
} as SpecWithTitles,
241241
datapoints: { [EXPERIMENT_WORKSPACE_ID]: [{}] },
242242
revisions,
243243
type: PlotsType.VEGA
244244
}
245245
],
246246
[join(`dvc.yaml${FIELD_SEPARATOR}logs`, 'acc.tsv')]: [
247247
{
248-
content: {},
248+
content: {} as SpecWithTitles,
249249
datapoints: { [EXPERIMENT_WORKSPACE_ID]: [{}] },
250250
revisions,
251251
type: PlotsType.VEGA
@@ -258,7 +258,7 @@ describe('collectPaths', () => {
258258
'acc.tsv'
259259
)]: [
260260
{
261-
content: {},
261+
content: {} as SpecWithTitles,
262262
datapoints: { [EXPERIMENT_WORKSPACE_ID]: [{}] },
263263
revisions,
264264
type: PlotsType.VEGA

extension/src/plots/paths/model.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { PlotsType, TemplatePlotGroup } from '../webview/contract'
77
import { EXPERIMENT_WORKSPACE_ID } from '../../cli/dvc/contract'
88
import { ErrorsModel } from '../errors/model'
99
import { REVISIONS } from '../../test/fixtures/plotsDiff'
10+
import { SpecWithTitles } from '../vega/util'
1011

1112
describe('PathsModel', () => {
1213
const mockDvcRoot = 'test'
@@ -107,7 +108,7 @@ describe('PathsModel', () => {
107108
data: {
108109
[previousPlotPath]: [
109110
{
110-
content: {},
111+
content: {} as SpecWithTitles,
111112
datapoints: {
112113
[commitBeforePlots]: [
113114
{

extension/src/plots/util.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { sep } from 'path'
22
import { ensurePlotsDataPathsOsSep } from './util'
33
import { PlotsType } from './webview/contract'
4+
import { SpecWithTitles } from './vega/util'
45
import { FIELD_SEPARATOR } from '../cli/dvc/constants'
56
import { PlotsOutput } from '../cli/dvc/contract'
67

@@ -18,7 +19,7 @@ const getOutput = (slash = sep): PlotsOutput => {
1819
],
1920
[joinWithSep([`dvc.yaml${FIELD_SEPARATOR}logs`, 'acc.tsv'], slash)]: [
2021
{
21-
content: {},
22+
content: {} as SpecWithTitles,
2223
datapoints: { main: [{}] },
2324
revisions: ['main'],
2425
type: PlotsType.VEGA

extension/src/plots/vega/util.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ describe('extendVegaSpec', () => {
150150
it('should truncate all titles from the left to 50 characters for large plots', () => {
151151
const spec = withLongTemplatePlotTitle()
152152
const updatedSpec = extendVegaSpec(spec, 1, DEFAULT_PLOT_HEIGHT)
153+
delete updatedSpec.titles
153154

154155
const truncatedTitle = '…-many-many-characters-at-least-seventy-characters'
155156
const truncatedHorizontalTitle =
@@ -180,6 +181,7 @@ describe('extendVegaSpec', () => {
180181
DEFAULT_NB_ITEMS_PER_ROW,
181182
DEFAULT_PLOT_HEIGHT
182183
)
184+
delete updatedSpec.titles
183185

184186
const truncatedTitle = '…-many-many-characters-at-least-seventy-characters'
185187
const truncatedHorizontalTitle =
@@ -206,6 +208,7 @@ describe('extendVegaSpec', () => {
206208
it('should truncate all titles from the left to 30 characters for small plots', () => {
207209
const spec = withLongTemplatePlotTitle()
208210
const updatedSpec = extendVegaSpec(spec, 3, DEFAULT_PLOT_HEIGHT)
211+
delete updatedSpec.titles
209212

210213
const truncatedTitle = '…s-at-least-seventy-characters'
211214
const truncatedHorizontalTitle = '…at-least-seventy-characters-x'
@@ -236,6 +239,7 @@ describe('extendVegaSpec', () => {
236239
})
237240

238241
const updatedSpec = extendVegaSpec(spec, 3, DEFAULT_PLOT_HEIGHT)
242+
delete updatedSpec.titles
239243

240244
const truncatedTitle = '…ghijklmnopqrstuvwyz1234567890'
241245

@@ -253,6 +257,7 @@ describe('extendVegaSpec', () => {
253257
const spec = withLongTemplatePlotTitle([repeatedTitle, repeatedTitle])
254258

255259
const updatedSpec = extendVegaSpec(spec, 3, DEFAULT_PLOT_HEIGHT)
260+
delete updatedSpec.titles
256261

257262
const truncatedTitle = '…ghijklmnopqrstuvwyz1234567890'
258263

@@ -273,6 +278,7 @@ describe('extendVegaSpec', () => {
273278
})
274279

275280
const updatedSpec = extendVegaSpec(spec, 3, DEFAULT_PLOT_HEIGHT)
281+
delete updatedSpec.titles
276282

277283
const truncatedTitle = '…ghijklmnopqrstuvwyz1234567890'
278284

@@ -305,6 +311,7 @@ describe('extendVegaSpec', () => {
305311
const updatedSpecCopy = cloneDeep(updatedSpec)
306312
delete updatedSpecCopy.layer[1].layer[0].encoding.shape
307313
delete updatedSpecCopy.encoding
314+
delete updatedSpecCopy.titles
308315

309316
expect(updatedSpecCopy).toStrictEqual(multiSourceTemplate)
310317
})

0 commit comments

Comments
 (0)