Skip to content

Commit 4c2d7d1

Browse files
authored
Resize comparison table with slider (#3480)
* Resize comparison tablw with slider * Add tests * Fix colliding headers for the comparison table
1 parent 66242bf commit 4c2d7d1

File tree

24 files changed

+211
-126
lines changed

24 files changed

+211
-126
lines changed

extension/src/persistence/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export enum PersistenceKey {
1111
PLOT_COMPARISON_PATHS_ORDER = 'plotComparisonPathsOrder',
1212
PLOT_HEIGHT = 'plotHeight',
1313
PLOT_METRIC_ORDER = 'plotMetricOrder:',
14-
PLOT_NB_ITEMS_PER_ROW = 'plotNbItemsPerRow:',
14+
PLOT_NB_ITEMS_PER_ROW_OR_WIDTH = 'plotNbItemsPerRowOrWidth:',
1515
PLOTS_CUSTOM_ORDER = 'plotCustomOrder:',
1616
PLOT_SECTION_COLLAPSED = 'plotSectionCollapsed:',
1717
PLOT_SELECTED_METRICS = 'plotSelectedMetrics:',

extension/src/persistence/util.test.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { resetPersistedState } from './util'
44
import { buildMockMemento } from '../test/util'
55
import {
66
DEFAULT_HEIGHT,
7-
DEFAULT_SECTION_NB_ITEMS_PER_ROW
7+
DEFAULT_SECTION_NB_ITEMS_PER_ROW_OR_WIDTH
88
} from '../plots/webview/contract'
99

1010
jest.mock('vscode')
@@ -31,8 +31,8 @@ describe('Persistence util', () => {
3131
it('should reset all values from all dvc roots', async () => {
3232
const persistedState = {
3333
[PersistenceKey.PLOT_HEIGHT + 'root1']: DEFAULT_HEIGHT,
34-
[PersistenceKey.PLOT_NB_ITEMS_PER_ROW + 'root2']:
35-
DEFAULT_SECTION_NB_ITEMS_PER_ROW
34+
[PersistenceKey.PLOT_NB_ITEMS_PER_ROW_OR_WIDTH + 'root2']:
35+
DEFAULT_SECTION_NB_ITEMS_PER_ROW_OR_WIDTH
3636
}
3737
const workspaceState = buildMockMemento(persistedState)
3838

@@ -42,7 +42,9 @@ describe('Persistence util', () => {
4242
workspaceState.get(PersistenceKey.PLOT_HEIGHT + 'root1')
4343
).toBeUndefined()
4444
expect(
45-
workspaceState.get(PersistenceKey.PLOT_NB_ITEMS_PER_ROW + 'root2')
45+
workspaceState.get(
46+
PersistenceKey.PLOT_NB_ITEMS_PER_ROW_OR_WIDTH + 'root2'
47+
)
4648
).toBeUndefined()
4749
})
4850
})

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

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { PlotsModel } from '.'
22
import {
33
DEFAULT_NB_ITEMS_PER_ROW,
44
DEFAULT_SECTION_COLLAPSED,
5-
DEFAULT_SECTION_NB_ITEMS_PER_ROW,
5+
DEFAULT_SECTION_NB_ITEMS_PER_ROW_OR_WIDTH,
66
PlotsSection
77
} from '../webview/contract'
88
import { buildMockMemento } from '../../test/util'
@@ -25,8 +25,8 @@ describe('plotsModel', () => {
2525
const memento = buildMockMemento({
2626
[PersistenceKey.PLOT_SELECTED_METRICS + exampleDvcRoot]:
2727
persistedSelectedMetrics,
28-
[PersistenceKey.PLOT_NB_ITEMS_PER_ROW + exampleDvcRoot]:
29-
DEFAULT_SECTION_NB_ITEMS_PER_ROW
28+
[PersistenceKey.PLOT_NB_ITEMS_PER_ROW_OR_WIDTH + exampleDvcRoot]:
29+
DEFAULT_SECTION_NB_ITEMS_PER_ROW_OR_WIDTH
3030
})
3131
const mockedGetSelectedRevisions = jest.fn()
3232
const mockedGetFirstThreeColumnOrder = jest.fn()
@@ -68,27 +68,27 @@ describe('plotsModel', () => {
6868
})
6969

7070
it('should change the plotSize when calling setPlotSize', () => {
71-
expect(model.getNbItemsPerRow(PlotsSection.CHECKPOINT_PLOTS)).toStrictEqual(
72-
DEFAULT_NB_ITEMS_PER_ROW
73-
)
71+
expect(
72+
model.getNbItemsPerRowOrWidth(PlotsSection.CHECKPOINT_PLOTS)
73+
).toStrictEqual(DEFAULT_NB_ITEMS_PER_ROW)
7474

75-
model.setNbItemsPerRow(PlotsSection.CHECKPOINT_PLOTS, 1)
75+
model.setNbItemsPerRowOrWidth(PlotsSection.CHECKPOINT_PLOTS, 1)
7676

77-
expect(model.getNbItemsPerRow(PlotsSection.CHECKPOINT_PLOTS)).toStrictEqual(
78-
1
79-
)
77+
expect(
78+
model.getNbItemsPerRowOrWidth(PlotsSection.CHECKPOINT_PLOTS)
79+
).toStrictEqual(1)
8080
})
8181

8282
it('should update the persisted plot size when calling setPlotSize', () => {
8383
const mementoUpdateSpy = jest.spyOn(memento, 'update')
8484

85-
model.setNbItemsPerRow(PlotsSection.CHECKPOINT_PLOTS, 2)
85+
model.setNbItemsPerRowOrWidth(PlotsSection.CHECKPOINT_PLOTS, 2)
8686

8787
expect(mementoUpdateSpy).toHaveBeenCalledTimes(1)
8888
expect(mementoUpdateSpy).toHaveBeenCalledWith(
89-
PersistenceKey.PLOT_NB_ITEMS_PER_ROW + exampleDvcRoot,
89+
PersistenceKey.PLOT_NB_ITEMS_PER_ROW_OR_WIDTH + exampleDvcRoot,
9090
{
91-
...DEFAULT_SECTION_NB_ITEMS_PER_ROW,
91+
...DEFAULT_SECTION_NB_ITEMS_PER_ROW_OR_WIDTH,
9292
[PlotsSection.CHECKPOINT_PLOTS]: 2
9393
}
9494
)

extension/src/plots/model/index.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
Revision,
2424
ComparisonRevisionData,
2525
DEFAULT_SECTION_COLLAPSED,
26-
DEFAULT_SECTION_NB_ITEMS_PER_ROW,
26+
DEFAULT_SECTION_NB_ITEMS_PER_ROW_OR_WIDTH,
2727
PlotsSection,
2828
SectionCollapsed,
2929
CustomPlotData,
@@ -56,7 +56,7 @@ export type CustomPlotsOrderValue = { metric: string; param: string }
5656
export class PlotsModel extends ModelWithPersistence {
5757
private readonly experiments: Experiments
5858

59-
private nbItemsPerRow: Record<PlotsSection, number>
59+
private nbItemsPerRowOrWidth: Record<PlotsSection, number>
6060
private height: Record<PlotsSection, PlotHeight>
6161
private customPlotsOrder: CustomPlotsOrderValue[]
6262
private sectionCollapsed: SectionCollapsed
@@ -85,9 +85,9 @@ export class PlotsModel extends ModelWithPersistence {
8585
super(dvcRoot, workspaceState)
8686
this.experiments = experiments
8787

88-
this.nbItemsPerRow = this.revive(
89-
PersistenceKey.PLOT_NB_ITEMS_PER_ROW,
90-
DEFAULT_SECTION_NB_ITEMS_PER_ROW
88+
this.nbItemsPerRowOrWidth = this.revive(
89+
PersistenceKey.PLOT_NB_ITEMS_PER_ROW_OR_WIDTH,
90+
DEFAULT_SECTION_NB_ITEMS_PER_ROW_OR_WIDTH
9191
)
9292
this.height = this.revive(PersistenceKey.PLOT_HEIGHT, DEFAULT_HEIGHT)
9393

@@ -183,7 +183,9 @@ export class PlotsModel extends ModelWithPersistence {
183183
return {
184184
colors,
185185
height: this.getHeight(PlotsSection.CHECKPOINT_PLOTS),
186-
nbItemsPerRow: this.getNbItemsPerRow(PlotsSection.CHECKPOINT_PLOTS),
186+
nbItemsPerRow: this.getNbItemsPerRowOrWidth(
187+
PlotsSection.CHECKPOINT_PLOTS
188+
),
187189
plots: this.getPlots(this.checkpointPlots, selectedExperiments),
188190
selectedMetrics: this.getSelectedMetrics()
189191
}
@@ -195,7 +197,7 @@ export class PlotsModel extends ModelWithPersistence {
195197
}
196198
return {
197199
height: this.getHeight(PlotsSection.CUSTOM_PLOTS),
198-
nbItemsPerRow: this.getNbItemsPerRow(PlotsSection.CUSTOM_PLOTS),
200+
nbItemsPerRow: this.getNbItemsPerRowOrWidth(PlotsSection.CUSTOM_PLOTS),
199201
plots: this.customPlots
200202
}
201203
}
@@ -403,14 +405,17 @@ export class PlotsModel extends ModelWithPersistence {
403405
this.persist(PersistenceKey.PLOT_METRIC_ORDER, this.metricOrder)
404406
}
405407

406-
public setNbItemsPerRow(section: PlotsSection, nbItemsPerRow: number) {
407-
this.nbItemsPerRow[section] = nbItemsPerRow
408-
this.persist(PersistenceKey.PLOT_NB_ITEMS_PER_ROW, this.nbItemsPerRow)
408+
public setNbItemsPerRowOrWidth(section: PlotsSection, nbItemsPerRow: number) {
409+
this.nbItemsPerRowOrWidth[section] = nbItemsPerRow
410+
this.persist(
411+
PersistenceKey.PLOT_NB_ITEMS_PER_ROW_OR_WIDTH,
412+
this.nbItemsPerRowOrWidth
413+
)
409414
}
410415

411-
public getNbItemsPerRow(section: PlotsSection) {
412-
if (this.nbItemsPerRow[section]) {
413-
return this.nbItemsPerRow[section]
416+
public getNbItemsPerRowOrWidth(section: PlotsSection) {
417+
if (this.nbItemsPerRowOrWidth[section]) {
418+
return this.nbItemsPerRowOrWidth[section]
414419
}
415420
return DEFAULT_NB_ITEMS_PER_ROW
416421
}
@@ -511,7 +516,7 @@ export class PlotsModel extends ModelWithPersistence {
511516
id,
512517
title: truncateVerticalTitle(
513518
id,
514-
this.getNbItemsPerRow(PlotsSection.CHECKPOINT_PLOTS),
519+
this.getNbItemsPerRowOrWidth(PlotsSection.CHECKPOINT_PLOTS),
515520
this.getHeight(PlotsSection.CHECKPOINT_PLOTS)
516521
) as string,
517522
values: values.filter(value =>
@@ -563,7 +568,7 @@ export class PlotsModel extends ModelWithPersistence {
563568
selectedRevisions.map(({ revision }) => revision),
564569
this.templates,
565570
this.revisionData,
566-
this.getNbItemsPerRow(PlotsSection.TEMPLATE_PLOTS),
571+
this.getNbItemsPerRowOrWidth(PlotsSection.TEMPLATE_PLOTS),
567572
this.getHeight(PlotsSection.TEMPLATE_PLOTS),
568573
this.getRevisionColors(selectedRevisions),
569574
this.multiSourceEncoding

extension/src/plots/webview/contract.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,19 @@ export enum PlotHeight {
1414

1515
export const DEFAULT_PLOT_HEIGHT = PlotHeight.SMALL
1616

17+
export const DEFAULT_PLOT_WIDTH = 2
18+
1719
export enum PlotsSection {
1820
CHECKPOINT_PLOTS = 'checkpoint-plots',
1921
TEMPLATE_PLOTS = 'template-plots',
2022
COMPARISON_TABLE = 'comparison-table',
2123
CUSTOM_PLOTS = 'custom-plots'
2224
}
2325

24-
export const DEFAULT_SECTION_NB_ITEMS_PER_ROW = {
26+
export const DEFAULT_SECTION_NB_ITEMS_PER_ROW_OR_WIDTH = {
2527
[PlotsSection.CHECKPOINT_PLOTS]: DEFAULT_NB_ITEMS_PER_ROW,
2628
[PlotsSection.TEMPLATE_PLOTS]: DEFAULT_NB_ITEMS_PER_ROW,
27-
[PlotsSection.COMPARISON_TABLE]: DEFAULT_NB_ITEMS_PER_ROW,
29+
[PlotsSection.COMPARISON_TABLE]: DEFAULT_PLOT_WIDTH,
2830
[PlotsSection.CUSTOM_PLOTS]: DEFAULT_NB_ITEMS_PER_ROW
2931
}
3032

@@ -69,7 +71,7 @@ export type Revision = {
6971

7072
export interface PlotsComparisonData {
7173
plots: ComparisonPlots
72-
nbItemsPerRow: number
74+
width: number
7375
height: PlotHeight
7476
revisions: Revision[]
7577
}

extension/src/plots/webview/messages.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export class WebviewMessages {
136136
nbItemsPerRow: number,
137137
height: PlotHeight
138138
) {
139-
this.plots.setNbItemsPerRow(section, nbItemsPerRow)
139+
this.plots.setNbItemsPerRowOrWidth(section, nbItemsPerRow)
140140
this.plots.setHeight(section, height)
141141
sendTelemetryEvent(
142142
EventName.VIEWS_PLOTS_SECTION_RESIZED,
@@ -372,7 +372,9 @@ export class WebviewMessages {
372372

373373
return {
374374
height: this.plots.getHeight(PlotsSection.TEMPLATE_PLOTS),
375-
nbItemsPerRow: this.plots.getNbItemsPerRow(PlotsSection.TEMPLATE_PLOTS),
375+
nbItemsPerRow: this.plots.getNbItemsPerRowOrWidth(
376+
PlotsSection.TEMPLATE_PLOTS
377+
),
376378
plots
377379
}
378380
}
@@ -389,11 +391,11 @@ export class WebviewMessages {
389391

390392
return {
391393
height: this.plots.getHeight(PlotsSection.COMPARISON_TABLE),
392-
nbItemsPerRow: this.plots.getNbItemsPerRow(PlotsSection.COMPARISON_TABLE),
393394
plots: comparison.map(({ path, revisions }) => {
394395
return { path, revisions: this.getRevisionsWithCorrectUrls(revisions) }
395396
}),
396-
revisions: overrideRevs || this.plots.getComparisonRevisions()
397+
revisions: overrideRevs || this.plots.getComparisonRevisions(),
398+
width: this.plots.getNbItemsPerRowOrWidth(PlotsSection.COMPARISON_TABLE)
397399
}
398400
}
399401

extension/src/test/fixtures/plotsDiff/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ import {
1313
TemplatePlots,
1414
Revision,
1515
PlotsComparisonData,
16+
DEFAULT_PLOT_HEIGHT,
1617
DEFAULT_NB_ITEMS_PER_ROW,
17-
DEFAULT_PLOT_HEIGHT
18+
DEFAULT_PLOT_WIDTH
1819
} from '../../../plots/webview/contract'
1920
import { join } from '../../util/path'
2021
import { copyOriginalColors } from '../../../experiments/model/status/colors'
@@ -706,7 +707,7 @@ export const getComparisonWebviewMessage = (
706707
return {
707708
revisions: getRevisions(),
708709
plots: plotAcc,
709-
nbItemsPerRow: DEFAULT_NB_ITEMS_PER_ROW,
710+
width: DEFAULT_PLOT_WIDTH,
710711
height: DEFAULT_PLOT_HEIGHT
711712
}
712713
}

extension/src/test/suite/plots/index.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,10 @@ suite('Plots Test Suite', () => {
242242
const mockSendTelemetryEvent = stub(Telemetry, 'sendTelemetryEvent')
243243
const mockMessageReceived = getMessageReceivedEmitter(webview)
244244

245-
const mockSetPlotSize = stub(plotsModel, 'setNbItemsPerRow').returns(
246-
undefined
247-
)
245+
const mockSetPlotSize = stub(
246+
plotsModel,
247+
'setNbItemsPerRowOrWidth'
248+
).returns(undefined)
248249

249250
mockMessageReceived.fire({
250251
payload: {

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

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ import {
2828
PlotsSection,
2929
TemplatePlotGroup,
3030
TemplatePlotsData,
31-
DEFAULT_NB_ITEMS_PER_ROW,
32-
DEFAULT_PLOT_HEIGHT
31+
DEFAULT_PLOT_HEIGHT,
32+
DEFAULT_NB_ITEMS_PER_ROW
3333
} from 'dvc/src/plots/webview/contract'
3434
import {
3535
MessageFromWebviewType,
@@ -264,7 +264,6 @@ describe('App', () => {
264264
checkpoint: null,
265265
comparison: {
266266
height: DEFAULT_PLOT_HEIGHT,
267-
nbItemsPerRow: DEFAULT_NB_ITEMS_PER_ROW,
268267
plots: [
269268
{
270269
path: 'training/plots/images/misclassified.jpg',
@@ -280,7 +279,8 @@ describe('App', () => {
280279
id: 'ad2b5ec854a447d00d9dfa9cdf88211a39a17813',
281280
revision: 'ad2b5ec'
282281
}
283-
]
282+
],
283+
width: DEFAULT_NB_ITEMS_PER_ROW
284284
},
285285
hasPlots: true,
286286
hasUnselectedPlots: false,
@@ -713,22 +713,66 @@ describe('App', () => {
713713
expect(screen.queryByTestId('size-sliders')).not.toBeInTheDocument()
714714
})
715715

716-
it('should not display a slider to pick the number of items per row if the action is unavailable', () => {
716+
it('should not display a slider to pick the number of items per row if the only width available for one item per row or less', () => {
717717
const store = renderAppWithOptionalData({
718-
comparison: comparisonTableFixture
718+
checkpoint: checkpointPlotsFixture
719719
})
720-
setWrapperSize(store)
720+
setWrapperSize(store, 400)
721721

722722
expect(screen.queryByTestId('size-sliders')).not.toBeInTheDocument()
723723
})
724724

725-
it('should not display a slider to pick the number of items per row if the only width available for one item per row or less', () => {
725+
it('should display both size sliders for checkpoint plots', () => {
726726
const store = renderAppWithOptionalData({
727727
checkpoint: checkpointPlotsFixture
728728
})
729-
setWrapperSize(store, 400)
729+
setWrapperSize(store)
730730

731-
expect(screen.queryByTestId('size-sliders')).not.toBeInTheDocument()
731+
const plotResizers = within(
732+
screen.getByTestId('size-sliders')
733+
).getAllByRole('slider')
734+
735+
expect(plotResizers.length).toBe(2)
736+
})
737+
738+
it('should display both size sliders for template plots', () => {
739+
const store = renderAppWithOptionalData({
740+
template: templatePlotsFixture
741+
})
742+
setWrapperSize(store)
743+
744+
const plotResizers = within(
745+
screen.getByTestId('size-sliders')
746+
).getAllByRole('slider')
747+
748+
expect(plotResizers.length).toBe(2)
749+
})
750+
751+
it('should display both size sliders for custom plots', () => {
752+
const store = renderAppWithOptionalData({
753+
custom: customPlotsFixture,
754+
template: templatePlotsFixture
755+
})
756+
setWrapperSize(store)
757+
758+
const plotResizers = within(
759+
screen.getAllByTestId('size-sliders')[1]
760+
).getAllByRole('slider')
761+
762+
expect(plotResizers.length).toBe(2)
763+
})
764+
765+
it('should not display the height slider for the comparison table', () => {
766+
const store = renderAppWithOptionalData({
767+
comparison: comparisonTableFixture
768+
})
769+
setWrapperSize(store)
770+
771+
const plotResizers = within(
772+
screen.getByTestId('size-sliders')
773+
).getAllByRole('slider')
774+
775+
expect(plotResizers.length).toBe(1)
732776
})
733777

734778
it('should send a message to the extension with the selected size when changing the width of plots', () => {

0 commit comments

Comments
 (0)