Skip to content

Commit 4aa0958

Browse files
authored
Update plots empty states to reflect dynamic nature of available plots (#2932)
1 parent 77ab4c6 commit 4aa0958

File tree

10 files changed

+73
-60
lines changed

10 files changed

+73
-60
lines changed

extension/src/plots/paths/model.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import {
88
} from './collect'
99
import { PathSelectionModel, Status } from '../../path/selection/model'
1010
import { PersistenceKey } from '../../persistence/constants'
11-
import { performSimpleOrderedUpdate } from '../../util/array'
11+
import {
12+
definedAndNonEmpty,
13+
performSimpleOrderedUpdate
14+
} from '../../util/array'
1215
import { MultiSourceEncoding } from '../multiSource/collect'
1316
import { isDvcError } from '../../cli/dvc/reader'
1417
import { PlotsOutputOrError } from '../../cli/dvc/contract'
@@ -85,14 +88,21 @@ export class PathsModel extends PathSelectionModel<PlotPath> {
8588
.map(element => ({ ...element, selected: !!this.status[element.path] }))
8689
}
8790

88-
public getSelected() {
89-
return (
90-
this.data.filter(
91-
element =>
92-
this.status[element.path] !== Status.UNSELECTED &&
93-
this.hasRevisions(element)
94-
) || []
91+
public getHasUnselectedPlots() {
92+
const revisionPaths = this.data.filter(element =>
93+
this.hasRevisions(element)
9594
)
95+
96+
if (!definedAndNonEmpty(revisionPaths)) {
97+
return false
98+
}
99+
100+
for (const { path } of revisionPaths) {
101+
if (this.status[path] === Status.UNSELECTED) {
102+
return true
103+
}
104+
}
105+
return false
96106
}
97107

98108
public getTemplateOrder(): TemplateOrder {

extension/src/plots/webview/contract.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@ export type ComparisonPlot = {
126126
export enum PlotsDataKeys {
127127
COMPARISON = 'comparison',
128128
CHECKPOINT = 'checkpoint',
129+
HAS_UNSELECTED_PLOTS = 'hasUnselectedPlots',
129130
HAS_PLOTS = 'hasPlots',
130-
HAS_SELECTED_PLOTS = 'hasSelectedPlots',
131131
SELECTED_REVISIONS = 'selectedRevisions',
132132
TEMPLATE = 'template',
133133
SECTION_COLLAPSED = 'sectionCollapsed'
@@ -138,7 +138,7 @@ export type PlotsData =
138138
[PlotsDataKeys.COMPARISON]?: PlotsComparisonData | null
139139
[PlotsDataKeys.CHECKPOINT]?: CheckpointPlotsData | null
140140
[PlotsDataKeys.HAS_PLOTS]?: boolean
141-
[PlotsDataKeys.HAS_SELECTED_PLOTS]?: boolean
141+
[PlotsDataKeys.HAS_UNSELECTED_PLOTS]?: boolean
142142
[PlotsDataKeys.SELECTED_REVISIONS]?: Revision[]
143143
[PlotsDataKeys.TEMPLATE]?: TemplatePlotsData | null
144144
[PlotsDataKeys.SECTION_COLLAPSED]?: SectionCollapsed

extension/src/plots/webview/messages.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import { PlotsModel } from '../model'
2121
import { PathsModel } from '../paths/model'
2222
import { BaseWebview } from '../../webview'
2323
import { getModifiedTime } from '../../fileSystem'
24-
import { definedAndNonEmpty } from '../../util/array'
2524

2625
export class WebviewMessages {
2726
private readonly paths: PathsModel
@@ -56,7 +55,7 @@ export class WebviewMessages {
5655
checkpoint: this.getCheckpointPlots(),
5756
comparison: this.getComparisonPlots(overrideComparison),
5857
hasPlots: !!this.paths.hasPaths(),
59-
hasSelectedPlots: definedAndNonEmpty(this.paths.getSelected()),
58+
hasUnselectedPlots: this.paths.getHasUnselectedPlots(),
6059
sectionCollapsed: this.plots.getSectionCollapsed(),
6160
selectedRevisions: overrideRevisions,
6261
template: this.getTemplatePlots(overrideRevisions)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,7 @@ suite('Plots Test Suite', () => {
675675
checkpoint: checkpointPlotsFixture,
676676
comparison: comparisonPlotsFixture,
677677
hasPlots: true,
678-
hasSelectedPlots: true,
678+
hasUnselectedPlots: false,
679679
sectionCollapsed: DEFAULT_SECTION_COLLAPSED,
680680
selectedRevisions: plotsRevisionsFixture,
681681
template: templatePlotsFixture

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ describe('App', () => {
244244
renderAppWithOptionalData({
245245
checkpoint: null
246246
})
247-
const emptyState = await screen.findByText('No Plots to Display.')
247+
const emptyState = await screen.findByText('No Plots Detected.')
248248

249249
expect(emptyState).toBeInTheDocument()
250250
})
@@ -271,7 +271,7 @@ describe('App', () => {
271271
size: PlotSizeNumber.REGULAR
272272
},
273273
hasPlots: true,
274-
hasSelectedPlots: true,
274+
hasUnselectedPlots: false,
275275
sectionCollapsed: DEFAULT_SECTION_COLLAPSED,
276276
selectedRevisions: [
277277
{
@@ -289,21 +289,29 @@ describe('App', () => {
289289
expect(loading).toHaveLength(3)
290290
})
291291

292-
it('should only render the Add Plots get started button when there are experiments but no plots are selected', async () => {
292+
it('should render the Add Plots and Add Experiments get started button when there are experiments which have plots that are all unselected', async () => {
293293
renderAppWithOptionalData({
294294
checkpoint: null,
295295
hasPlots: true,
296-
hasSelectedPlots: false,
296+
hasUnselectedPlots: true,
297297
selectedRevisions: [{} as Revision]
298298
})
299-
const addExperimentsButton = screen.queryByText('Add Experiments')
299+
const addExperimentsButton = await screen.findByText('Add Experiments')
300300
const addPlotsButton = await screen.findByText('Add Plots')
301301

302-
expect(addExperimentsButton).not.toBeInTheDocument()
302+
expect(addExperimentsButton).toBeInTheDocument()
303303
expect(addPlotsButton).toBeInTheDocument()
304304

305305
mockPostMessage.mockReset()
306306

307+
fireEvent.click(addExperimentsButton)
308+
309+
expect(mockPostMessage).toHaveBeenCalledWith({
310+
type: MessageFromWebviewType.SELECT_EXPERIMENTS
311+
})
312+
313+
mockPostMessage.mockReset()
314+
307315
fireEvent.click(addPlotsButton)
308316

309317
expect(mockPostMessage).toHaveBeenCalledWith({
@@ -312,11 +320,11 @@ describe('App', () => {
312320
mockPostMessage.mockReset()
313321
})
314322

315-
it('should only render the Add Experiments get started button when no plots or experiments are selected', async () => {
323+
it('should render only the Add Experiments get started button when no experiments are selected', async () => {
316324
renderAppWithOptionalData({
317325
checkpoint: null,
318326
hasPlots: true,
319-
hasSelectedPlots: false,
327+
hasUnselectedPlots: false,
320328
selectedRevisions: undefined
321329
})
322330
const addExperimentsButton = await screen.findByText('Add Experiments')

webview/src/plots/components/App.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
import {
2828
initialize,
2929
updateHasPlots,
30-
updateHasSelectedPlots,
30+
updateHasUnselectedPlots,
3131
updateSelectedRevisions
3232
} from './webviewSlice'
3333
import { PlotsDispatch } from '../store'
@@ -71,8 +71,8 @@ export const feedStore = (
7171
case PlotsDataKeys.HAS_PLOTS:
7272
dispatch(updateHasPlots(!!data.data[key]))
7373
continue
74-
case PlotsDataKeys.HAS_SELECTED_PLOTS:
75-
dispatch(updateHasSelectedPlots(!!data.data[key]))
74+
case PlotsDataKeys.HAS_UNSELECTED_PLOTS:
75+
dispatch(updateHasUnselectedPlots(!!data.data[key]))
7676
continue
7777
case PlotsDataKeys.SELECTED_REVISIONS:
7878
dispatch(updateSelectedRevisions(data.data[key] as Revision[]))

webview/src/plots/components/GetStarted.tsx

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,37 @@ import { MessageFromWebviewType } from 'dvc/src/webview/contract'
33
import { sendMessage } from '../../shared/vscode'
44
import { StartButton } from '../../shared/components/button/StartButton'
55

6-
const NoPlotsText: React.FC = () => <p>No Plots to Display.</p>
7-
86
export type AddPlotsProps = {
9-
hasSelectedPlots: boolean
7+
hasUnselectedPlots: boolean
108
hasSelectedRevisions: boolean
119
}
1210

1311
export const AddPlots: React.FC<AddPlotsProps> = ({
14-
hasSelectedPlots,
15-
hasSelectedRevisions
12+
hasUnselectedPlots
1613
}: AddPlotsProps) => (
1714
<div>
18-
<NoPlotsText />
15+
<p>No Plots to Display.</p>
1916
<div>
20-
{hasSelectedRevisions && !hasSelectedPlots && (
17+
{
2118
<StartButton
2219
onClick={() =>
2320
sendMessage({
24-
type: MessageFromWebviewType.SELECT_PLOTS
21+
type: MessageFromWebviewType.SELECT_EXPERIMENTS
2522
})
2623
}
27-
text="Add Plots"
24+
text="Add Experiments"
2825
/>
29-
)}
30-
{!hasSelectedRevisions && (
26+
}
27+
{hasUnselectedPlots && (
3128
<StartButton
32-
isNested={!hasSelectedPlots}
29+
isNested={hasUnselectedPlots}
30+
appearance="secondary"
3331
onClick={() =>
3432
sendMessage({
35-
type: MessageFromWebviewType.SELECT_EXPERIMENTS
33+
type: MessageFromWebviewType.SELECT_PLOTS
3634
})
3735
}
38-
text="Add Experiments"
36+
text="Add Plots"
3937
/>
4038
)}
4139
</div>
@@ -44,7 +42,15 @@ export const AddPlots: React.FC<AddPlotsProps> = ({
4442

4543
export const Welcome: React.FC = () => (
4644
<div>
47-
<NoPlotsText />
45+
<p>No Plots Detected.</p>
46+
<StartButton
47+
onClick={() =>
48+
sendMessage({
49+
type: MessageFromWebviewType.SELECT_EXPERIMENTS
50+
})
51+
}
52+
text="Add Experiments"
53+
/>
4854
<p>
4955
Learn how to{' '}
5056
<a href="https://dvc.org/doc/studio/user-guide/views/visualize-experiments">

webview/src/plots/components/Plots.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const PlotsContent = () => {
1919
const {
2020
hasData,
2121
hasPlots,
22-
hasSelectedPlots,
22+
hasUnselectedPlots,
2323
selectedRevisions,
2424
zoomedInPlot
2525
} = useSelector((state: PlotsState) => state.webview)
@@ -57,7 +57,7 @@ const PlotsContent = () => {
5757
<GetStarted
5858
addItems={
5959
<AddPlots
60-
hasSelectedPlots={!!hasSelectedPlots}
60+
hasUnselectedPlots={hasUnselectedPlots}
6161
hasSelectedRevisions={!!selectedRevisions?.length}
6262
/>
6363
}

webview/src/plots/components/webviewSlice.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type ZoomedInPlotState = {
1010
export interface WebviewState {
1111
hasData: boolean
1212
hasPlots: boolean
13-
hasSelectedPlots: boolean
13+
hasUnselectedPlots: boolean
1414
selectedRevisions: Revision[]
1515
zoomedInPlot: ZoomedInPlotState | undefined
1616
maxPlotSize: number
@@ -19,7 +19,7 @@ export interface WebviewState {
1919
export const webviewInitialState: WebviewState = {
2020
hasData: false,
2121
hasPlots: false,
22-
hasSelectedPlots: false,
22+
hasUnselectedPlots: false,
2323
maxPlotSize: 375,
2424
selectedRevisions: [],
2525
zoomedInPlot: {
@@ -64,11 +64,11 @@ export const webviewSlice = createSlice({
6464
) => {
6565
state.hasPlots = action.payload
6666
},
67-
updateHasSelectedPlots: (
68-
state: { hasSelectedPlots: boolean },
67+
updateHasUnselectedPlots: (
68+
state: { hasUnselectedPlots: boolean },
6969
action: PayloadAction<boolean>
7070
) => {
71-
state.hasSelectedPlots = action.payload
71+
state.hasUnselectedPlots = action.payload
7272
},
7373
updateSelectedRevisions: (
7474
state: { selectedRevisions: Revision[] },
@@ -82,7 +82,7 @@ export const webviewSlice = createSlice({
8282
export const {
8383
initialize,
8484
updateHasPlots,
85-
updateHasSelectedPlots,
85+
updateHasUnselectedPlots,
8686
updateSelectedRevisions,
8787
setZoomedInPlot,
8888
setMaxPlotSize

webview/src/stories/Plots.stories.tsx

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export default {
6767
checkpoint: checkpointPlotsFixture,
6868
comparison: comparisonPlotsFixture,
6969
hasPlots: true,
70-
hasSelectedPlots: false,
70+
hasUnselectedPlots: false,
7171
sectionCollapsed: DEFAULT_SECTION_COLLAPSED,
7272
selectedRevisions: plotsRevisionsFixture,
7373
template: templatePlotsFixture
@@ -144,7 +144,7 @@ export const WithoutPlotsSelected = Template.bind({})
144144
WithoutPlotsSelected.args = {
145145
data: {
146146
hasPlots: true,
147-
hasSelectedPlots: false,
147+
hasUnselectedPlots: true,
148148
sectionCollapsed: DEFAULT_SECTION_COLLAPSED,
149149
selectedRevisions: plotsRevisionsFixture
150150
}
@@ -154,22 +154,12 @@ export const WithoutExperimentsSelected = Template.bind({})
154154
WithoutExperimentsSelected.args = {
155155
data: {
156156
hasPlots: true,
157-
hasSelectedPlots: true,
157+
hasUnselectedPlots: false,
158158
sectionCollapsed: DEFAULT_SECTION_COLLAPSED,
159159
selectedRevisions: []
160160
}
161161
}
162162

163-
export const WithoutAnySelected = Template.bind({})
164-
WithoutAnySelected.args = {
165-
data: {
166-
hasPlots: true,
167-
hasSelectedPlots: false,
168-
sectionCollapsed: DEFAULT_SECTION_COLLAPSED,
169-
selectedRevisions: undefined
170-
}
171-
}
172-
173163
export const WithoutData = Template.bind({})
174164
WithoutData.args = {
175165
data: undefined

0 commit comments

Comments
 (0)