Skip to content

Commit 24dd71e

Browse files
authored
Truncate all long titles inside of plots (#2365)
* Truncate axis titles * Truncate all titles * Self review * Change tests
1 parent 62f0650 commit 24dd71e

File tree

4 files changed

+100
-9
lines changed

4 files changed

+100
-9
lines changed

webview/src/plots/components/App.test.tsx

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1939,6 +1939,12 @@ describe('App', () => {
19391939
describe('Title truncation', () => {
19401940
const longTitle =
19411941
'we-need-a-very-very-very-long-title-to-test-with-many-many-many-characters-at-least-seventy-characters'
1942+
const longAxisTitleHorizontal = `${longTitle}-x`
1943+
const longAxisTitleVertical = `${longTitle}-y`
1944+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1945+
const layers = (templatePlotsFixture.plots[0].entries[0].content as any)
1946+
.layer
1947+
19421948
const withLongTemplatePlotTitle = (title: Text | Title = longTitle) => ({
19431949
...templatePlotsFixture,
19441950
plots: [
@@ -1949,6 +1955,23 @@ describe('App', () => {
19491955
...templatePlotsFixture.plots[0].entries[0],
19501956
content: {
19511957
...templatePlotsFixture.plots[0].entries[0].content,
1958+
layer: [
1959+
{
1960+
...layers[0],
1961+
encoding: {
1962+
...layers[0].encoding,
1963+
x: {
1964+
...layers[0].encoding.x,
1965+
title: longAxisTitleHorizontal
1966+
},
1967+
y: {
1968+
...layers[0].encoding.y,
1969+
title: longAxisTitleVertical
1970+
}
1971+
}
1972+
},
1973+
...layers.slice(1)
1974+
],
19521975
title
19531976
},
19541977
id: longTitle
@@ -1958,27 +1981,37 @@ describe('App', () => {
19581981
} as TemplatePlotSection
19591982
]
19601983
})
1961-
it('should truncate the title of the plot from the left to 50 characters for large plots', async () => {
1984+
it('should truncate all titles from the left to 50 characters for large plots', async () => {
19621985
await renderAppAndChangeSize(
19631986
{
19641987
template: withLongTemplatePlotTitle()
19651988
},
19661989
'Large',
19671990
Section.TEMPLATE_PLOTS
19681991
)
1992+
19691993
const longTitlePlot = screen.getByTestId(`plot_${longTitle}`)
19701994

19711995
await waitForVega(longTitlePlot)
19721996

19731997
const truncatedTitle =
19741998
'…-many-many-characters-at-least-seventy-characters'
1999+
const truncatedHorizontalTitle =
2000+
'…any-many-characters-at-least-seventy-characters-x'
2001+
const truncatedVerticalTitle = '…racters-at-least-seventy-characters-y'
19752002

19762003
expect(
19772004
within(longTitlePlot).getByText(truncatedTitle)
19782005
).toBeInTheDocument()
2006+
expect(
2007+
within(longTitlePlot).getByText(truncatedHorizontalTitle)
2008+
).toBeInTheDocument()
2009+
expect(
2010+
within(longTitlePlot).getByText(truncatedVerticalTitle)
2011+
).toBeInTheDocument()
19792012
})
19802013

1981-
it('should truncate the title of the plot from the left to 50 characters for regular plots', async () => {
2014+
it('should truncate all titles from the left to 50 characters for regular plots', async () => {
19822015
await renderAppAndChangeSize(
19832016
{
19842017
template: withLongTemplatePlotTitle()
@@ -1991,13 +2024,22 @@ describe('App', () => {
19912024
await waitForVega(longTitlePlot)
19922025
const truncatedTitle =
19932026
'…-many-many-characters-at-least-seventy-characters'
2027+
const truncatedHorizontalTitle =
2028+
'…any-many-characters-at-least-seventy-characters-x'
2029+
const truncatedVerticalTitle = '…racters-at-least-seventy-characters-y'
19942030

19952031
expect(
19962032
within(longTitlePlot).getByText(truncatedTitle)
19972033
).toBeInTheDocument()
2034+
expect(
2035+
within(longTitlePlot).getByText(truncatedHorizontalTitle)
2036+
).toBeInTheDocument()
2037+
expect(
2038+
within(longTitlePlot).getByText(truncatedVerticalTitle)
2039+
).toBeInTheDocument()
19982040
})
19992041

2000-
it('should truncate the title of the plot from the left to 30 characters for large plots', async () => {
2042+
it('should truncate all titles from the left to 30 characters for large plots', async () => {
20012043
await renderAppAndChangeSize(
20022044
{
20032045
template: withLongTemplatePlotTitle()
@@ -2009,10 +2051,18 @@ describe('App', () => {
20092051

20102052
await waitForVega(longTitlePlot)
20112053
const truncatedTitle = '…s-at-least-seventy-characters'
2054+
const truncatedHorizontalTitle = '…at-least-seventy-characters-x'
2055+
const truncatedVerticalTitle = '…t-seventy-characters-y'
20122056

20132057
expect(
20142058
within(longTitlePlot).getByText(truncatedTitle)
20152059
).toBeInTheDocument()
2060+
expect(
2061+
within(longTitlePlot).getByText(truncatedHorizontalTitle)
2062+
).toBeInTheDocument()
2063+
expect(
2064+
within(longTitlePlot).getByText(truncatedVerticalTitle)
2065+
).toBeInTheDocument()
20162066
})
20172067

20182068
it('should truncate the title and the subtitle', async () => {

webview/src/plots/components/ZoomablePlot.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import { PlotSize } from 'dvc/src/plots/webview/contract'
77
import { setZoomedInPlot } from './webviewSlice'
88
import styles from './styles.module.scss'
99
import { config } from './constants'
10-
import { truncateTitle } from './util'
10+
import { truncateTitles } from './util'
1111
import { GripIcon } from '../../shared/components/dragDrop/GripIcon'
12+
import { Any } from '../../util/objects'
1213

1314
interface ZoomablePlotProps {
1415
spec: VisualizationSpec
@@ -42,10 +43,7 @@ export const ZoomablePlot: React.FC<ZoomablePlotProps> = ({
4243
data,
4344
'data-testid': `${id}-vega`,
4445
renderer: 'svg' as unknown as Renderers,
45-
spec: {
46-
...spec,
47-
title: truncateTitle(spec.title, TitleLimit[size])
48-
}
46+
spec: truncateTitles(spec as Any, TitleLimit[size]) as VisualizationSpec
4947
} as VegaLiteProps
5048
currentPlotProps.current = plotProps
5149

webview/src/plots/components/util.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { PlotSize } from 'dvc/src/plots/webview/contract'
22
import { ExprRef, hasOwnProperty, SignalRef, Title, truncate, Text } from 'vega'
33
import { TitleParams } from 'vega-lite/build/src/title'
4+
import { Any, Obj } from '../../util/objects'
45

56
const MaxItemsBeforeVirtualization = {
67
[PlotSize.LARGE]: 10,
@@ -86,3 +87,36 @@ export const truncateTitle = (
8687
}
8788
return titleCopy
8889
}
90+
91+
const isEndValue = (valueType: string) =>
92+
['string', 'number', 'boolean'].includes(valueType)
93+
94+
// eslint-disable-next-line sonarjs/cognitive-complexity
95+
export const truncateTitles = (spec: Any, size: number, vertical?: boolean) => {
96+
if (spec && typeof spec === 'object') {
97+
const specCopy: Obj = {}
98+
99+
for (const [key, value] of Object.entries(spec)) {
100+
const valueType = typeof value
101+
if (key === 'y') {
102+
vertical = true
103+
}
104+
if (key === 'title') {
105+
specCopy[key] = truncateTitle(
106+
value as unknown as Title,
107+
size * (vertical ? 0.75 : 1)
108+
)
109+
} else if (isEndValue(valueType)) {
110+
specCopy[key] = value
111+
} else if (Array.isArray(value)) {
112+
specCopy[key] = value.map(val =>
113+
isEndValue(typeof val) ? val : truncateTitles(val, size, vertical)
114+
)
115+
} else if (typeof value === 'object') {
116+
specCopy[key] = truncateTitles(value as Any, size, vertical)
117+
}
118+
}
119+
return specCopy
120+
}
121+
return spec
122+
}

webview/src/util/objects.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
import isEqual from 'lodash.isequal'
22

3-
export type BaseType = string | number | boolean | Object | undefined | null
3+
export type BaseType =
4+
| string
5+
| number
6+
| boolean
7+
| object
8+
| Obj
9+
| undefined
10+
| null
411

512
export type Any = BaseType | BaseType[]
613

14+
export type Obj = { [key: string]: Any }
15+
716
export const keepReferenceIfEqual = (old: BaseType, recent: BaseType) =>
817
isEqual(old, recent) ? old : recent

0 commit comments

Comments
 (0)