Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show "Custom" section in "Get Started" screen #3523

Merged
merged 15 commits into from
Apr 3, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions extension/src/plots/model/collect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@ describe('collectCustomPlots', () => {
expect(data).toStrictEqual(expectedOutput)
})

it('should return only custom plots if there no selected revisions', () => {
const expectedOutput: CustomPlotData[] = customPlotsFixture.plots.filter(
plot => plot.type !== CustomPlotType.CHECKPOINT
it('should return checkpoint plots with empty values if there no selected revisions', () => {
const expectedOutput: CustomPlotData[] = customPlotsFixture.plots.map(
plot =>
plot.type === CustomPlotType.CHECKPOINT ? { ...plot, values: [] } : plot
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels odd:

image

I don't know

  1. Which plot is which
  2. What type of plot should be inside (trends or metrics vs params).

If there are no experiments at all then we end up with this:

image

Seems inconsistent.

At the very least I think the text inside of the empty box needs to mention the plot title/select a checkpoint experiment to display but I think they should be hidden with some message to say "select a checkpoint experiment to display trend plots"

Note: Chances of users seeing these screens are minimal due to the use of checkpoint experiments but I think we should attempt to get it right.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, need to consider when there are no custom plots added but we do have data available:

image

An update will definitely be needed after #3567. Probably want to add a button.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the very least I think the text inside of the empty box needs to mention the plot title/select a checkpoint experiment to display but I think they should be hidden with some message to say "select a checkpoint experiment to display trend plots"

A message makes sense! Not sure where it could go though 🤔. Will start experimenting with some ideas :)

Also, need to consider when there are no custom plots added but we do have data available:

What's the issue with the current setup? Text says "No Plots Added" and you can add a plot with the plus button 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the issue with the current setup? Text says "No Plots Added" and you can add a plot with the plus button

The screen shouldn't be split. The custom section should be hidden and there should be two buttons, one to add an experiment and one to add a custom plot.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are 3 things that can currently cause an "empty" plots screen:

  1. No plots in the project
  2. No plots selected
  3. No experiments selected

I'm about to introduce a global error too which gives 4.

I think we need to distinguish between the user having added custom plots or not.

From those we end up with the following combinations:

project has plots plots selected experiments selected project has custom plots project has global error welcome screen content buttons to add/create show or hide custom plots section
N/A N/A N/A N Y Error message custom plots hide
N N/A N/A N N Info on adding plots to the project custom plot hide
Y N N N N There are no selected plots or experiments plots, experiments, custom plots hide
Y N Y N N There are no selected plots or custom plots plots, custom plots hide
Y Y N N N The are no selected experiments or custom plots experiments, custom plots hide
N/A N/A N/A Y Y Error message custom plots show
N N/A N/A Y N Info on adding plots to the project none show
Y N N Y N There are no selected plots or experiments plots, experiments show
Y N Y Y N There are no selected plots plots show
Y Y N Y N There are no experiments selected experiments show
Y Y Y Y N Normal webview N/A show

I added suggested welcome screen content, buttons and whether I think we should show or hide the custom plots section. IMO the section should be hidden if there is no other data sent the webview and a button should be added to create new custom plots.

I even think we should do this when there is a global CLI error.

The alternative is that we throw away the welcome screen concept in favour of improving the empty state of the existing sections. We then only have to deal with the project having no data at all.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another alternative is to simplify the welcome screen into as few states as possible.

project has plots plots selected experiments selected project has custom plots project has global error welcome screen content buttons to select/create show or hide custom plots section
N N/A N/A N N Info on adding plots to the project custom plot hide
Y N/A N/A N N There are no plots to show experiments, plots, custom plots hide
N N/A N/A Y N Info on adding plots to the project none show
Y N/A N/A Y N There are no plots to show experiments, plots, experiments show

We can deal with global errors separately but that would give us 6 states instead of 11. Seems pretty reasonable to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification! Updated the logic.

)
const data = collectCustomPlots({
...defaultFuncArgs,
Expand Down
2 changes: 1 addition & 1 deletion extension/src/plots/model/collect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ export const collectCustomPlots = ({
nbItemsPerRow: number
}): CustomPlotData[] => {
const plots = []
const shouldSkipCheckpointPlots = !hasCheckpoints || !selectedRevisions
const shouldSkipCheckpointPlots = !hasCheckpoints

for (const value of plotsOrderValues) {
if (shouldSkipCheckpointPlots && isCheckpointValue(value.type)) {
Expand Down
8 changes: 5 additions & 3 deletions extension/src/test/suite/experiments/model/tree.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,16 @@ suite('Experiments Tree Test Suite', () => {

expect(
messageSpy,
'when there are no experiments selected we dont send checkpoint type plots'
'when there are no experiments selected we send checkpoint plots with empty values'
).to.be.calledWithMatch({
comparison: null,
custom: {
...customPlotsFixture,
colors: undefined,
plots: customPlotsFixture.plots.filter(
plot => plot.type !== CustomPlotType.CHECKPOINT
plots: customPlotsFixture.plots.map(plot =>
plot.type === CustomPlotType.CHECKPOINT
? { ...plot, values: [] }
: plot
)
},
hasPlots: false,
Expand Down
85 changes: 44 additions & 41 deletions webview/src/plots/components/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -334,14 +334,14 @@ describe('App', () => {
})
})

it('should render an empty state given a message with only custom plots data', () => {
it('should render get started along with custom plots given a message with only custom plots data', () => {
renderAppWithOptionalData({
custom: customPlotsFixture
})

expect(screen.queryByText('Loading Plots...')).not.toBeInTheDocument()
const addExperimentsButton = screen.queryByText('Add Experiments')

expect(screen.getByText('Custom')).toBeInTheDocument()
expect(addExperimentsButton).toBeInTheDocument()
})

Expand All @@ -358,19 +358,40 @@ describe('App', () => {

it('should render custom with "No Plots Added" message when there are no plots added', () => {
renderAppWithOptionalData({
comparison: comparisonTableFixture,
custom: {
...customPlotsFixture,
plots: []
}
})

expect(screen.queryByText('Loading Plots...')).not.toBeInTheDocument()
expect(screen.queryByText('No Plots to Display')).not.toBeInTheDocument()
expect(screen.getByText('Custom')).toBeInTheDocument()
expect(screen.getByText('No Plots to Display')).toBeInTheDocument()
expect(screen.getByText('No Plots Added')).toBeInTheDocument()
})

it('should render custom with checkpoint plot placeholders when there are no selected experiments', () => {
const checkpointPlot = customPlotsFixture.plots[2]
renderAppWithOptionalData({
custom: {
...customPlotsFixture,
plots: [
{
...checkpointPlot,
values: []
}
]
}
})

expect(screen.queryByText('Loading Plots...')).not.toBeInTheDocument()
expect(screen.queryByText('No Plots to Display')).not.toBeInTheDocument()
expect(screen.getByText('Custom')).toBeInTheDocument()
expect(
screen.getByText('There are no selected experiments.')
).toBeInTheDocument()
})

it('should render the comparison table when given a message with comparison plots data', () => {
const expectedSectionName = 'Images'

Expand Down Expand Up @@ -404,24 +425,18 @@ describe('App', () => {
expect(emptyState).toBeInTheDocument()
})

it('should remove custom plots given a message showing custom plots as null', async () => {
const emptyStateText = 'No Plots to Display'

it('should remove custom plots given a message showing custom plots as null', () => {
renderAppWithOptionalData({
comparison: comparisonTableFixture,
custom: customPlotsFixture,
template: templatePlotsFixture
custom: customPlotsFixture
})

expect(screen.queryByText(emptyStateText)).not.toBeInTheDocument()
expect(screen.getByText('Custom')).toBeInTheDocument()

sendSetDataMessage({
custom: null
})

const emptyState = await screen.findByText(emptyStateText)

expect(emptyState).toBeInTheDocument()
expect(screen.queryByText('Custom')).not.toBeInTheDocument()
})

it('should remove all sections from the document if there is no data provided', () => {
Expand All @@ -440,7 +455,6 @@ describe('App', () => {

it('should toggle the custom plots section in state when its header is clicked', async () => {
renderAppWithOptionalData({
comparison: comparisonTableFixture,
custom: customPlotsFixture
})

Expand Down Expand Up @@ -475,7 +489,6 @@ describe('App', () => {

it('should not toggle the custom plots section when its header is clicked and its title is selected', async () => {
renderAppWithOptionalData({
comparison: comparisonTableFixture,
custom: customPlotsFixture
})

Expand Down Expand Up @@ -542,7 +555,6 @@ describe('App', () => {

it('should not toggle the custom plots section when its header is clicked and the content of its tooltip is selected', async () => {
renderAppWithOptionalData({
comparison: comparisonTableFixture,
custom: customPlotsFixture
})

Expand Down Expand Up @@ -597,12 +609,11 @@ describe('App', () => {

it('should display a slider to pick the number of items per row if there are items and the action is available', () => {
const store = renderAppWithOptionalData({
comparison: comparisonTableFixture,
custom: customPlotsFixture
})
setWrapperSize(store)

expect(screen.getAllByTestId('size-sliders')[1]).toBeInTheDocument()
expect(screen.getByTestId('size-sliders')).toBeInTheDocument()
})

it('should not display a slider to pick the number of items per row if there are no items', () => {
Expand All @@ -614,7 +625,6 @@ describe('App', () => {

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', () => {
const store = renderAppWithOptionalData({
comparison: comparisonTableFixture,
custom: customPlotsFixture
})
setWrapperSize(store, 400)
Expand All @@ -637,13 +647,12 @@ describe('App', () => {

it('should display both size sliders for custom plots', () => {
const store = renderAppWithOptionalData({
custom: customPlotsFixture,
template: templatePlotsFixture
custom: customPlotsFixture
})
setWrapperSize(store)

const plotResizers = within(
screen.getAllByTestId('size-sliders')[1]
screen.getByTestId('size-sliders')
).getAllByRole('slider')

expect(plotResizers.length).toBe(2)
Expand All @@ -664,14 +673,13 @@ describe('App', () => {

it('should send a message to the extension with the selected size when changing the width of plots', () => {
const store = renderAppWithOptionalData({
comparison: comparisonTableFixture,
custom: customPlotsFixture
})
setWrapperSize(store)

const plotResizer = within(
screen.getAllByTestId('size-sliders')[1]
).getAllByRole('slider')[0]
const plotResizer = within(screen.getByTestId('size-sliders')).getAllByRole(
'slider'
)[0]

fireEvent.change(plotResizer, { target: { value: -3 } })
expect(mockPostMessage).toHaveBeenCalledWith({
Expand All @@ -686,14 +694,13 @@ describe('App', () => {

it('should send a message to the extension with the selected size when changing the height of plots', () => {
const store = renderAppWithOptionalData({
comparison: comparisonTableFixture,
custom: customPlotsFixture
})
setWrapperSize(store)

const plotResizer = within(
screen.getAllByTestId('size-sliders')[1]
).getAllByRole('slider')[1]
const plotResizer = within(screen.getByTestId('size-sliders')).getAllByRole(
'slider'
)[1]

fireEvent.change(plotResizer, { target: { value: 3 } })
expect(mockPostMessage).toHaveBeenCalledWith({
Expand Down Expand Up @@ -735,7 +742,6 @@ describe('App', () => {

it('should send a message to the extension when the custom plots are reordered', () => {
renderAppWithOptionalData({
comparison: comparisonTableFixture,
custom: customPlotsFixture
})

Expand Down Expand Up @@ -770,7 +776,6 @@ describe('App', () => {

it('should add a custom plot if a user creates a custom plot', () => {
renderAppWithOptionalData({
comparison: comparisonTableFixture,
custom: {
...customPlotsFixture,
plots: customPlotsFixture.plots.slice(0, 3)
Expand Down Expand Up @@ -801,7 +806,6 @@ describe('App', () => {

it('should remove a custom plot if a user deletes a custom plot', () => {
renderAppWithOptionalData({
comparison: comparisonTableFixture,
custom: customPlotsFixture
})

Expand Down Expand Up @@ -1315,7 +1319,6 @@ describe('App', () => {

it('should open a modal with the plot zoomed in when clicking a custom plot', () => {
renderAppWithOptionalData({
comparison: comparisonTableFixture,
custom: customPlotsFixture
julieg18 marked this conversation as resolved.
Show resolved Hide resolved
})

Expand Down Expand Up @@ -1452,7 +1455,7 @@ describe('App', () => {
describe('Large plots', () => {
it('should wrap the custom plots in a big grid (virtualize them) when there are more than eight large plots', async () => {
await renderAppAndChangeSize(
{ comparison: comparisonTableFixture, custom: createCustomPlots(9) },
{ custom: createCustomPlots(9) },
1,
PlotsSection.CUSTOM_PLOTS
)
Expand All @@ -1470,7 +1473,7 @@ describe('App', () => {

it('should not wrap the custom plots in a big grid (virtualize them) when there are eight or fewer large plots', async () => {
await renderAppAndChangeSize(
{ comparison: comparisonTableFixture, custom: createCustomPlots(8) },
{ custom: createCustomPlots(8) },
1,
PlotsSection.CUSTOM_PLOTS
)
Expand Down Expand Up @@ -1579,7 +1582,7 @@ describe('App', () => {
describe('Regular plots', () => {
it('should wrap the custom plots in a big grid (virtualize them) when there are more than fourteen regular plots', async () => {
await renderAppAndChangeSize(
{ comparison: comparisonTableFixture, custom: createCustomPlots(15) },
{ custom: createCustomPlots(15) },
DEFAULT_NB_ITEMS_PER_ROW,
PlotsSection.CUSTOM_PLOTS
)
Expand All @@ -1589,7 +1592,7 @@ describe('App', () => {

it('should not wrap the custom plots in a big grid (virtualize them) when there are fourteen regular plots', async () => {
await renderAppAndChangeSize(
{ comparison: comparisonTableFixture, custom: createCustomPlots(14) },
{ custom: createCustomPlots(14) },
DEFAULT_NB_ITEMS_PER_ROW,
PlotsSection.CUSTOM_PLOTS
)
Expand Down Expand Up @@ -1676,7 +1679,7 @@ describe('App', () => {
describe('Smaller plots', () => {
it('should wrap the custom plots in a big grid (virtualize them) when there are more than twenty small plots', async () => {
await renderAppAndChangeSize(
{ comparison: comparisonTableFixture, custom: createCustomPlots(21) },
{ custom: createCustomPlots(21) },
4,
PlotsSection.CUSTOM_PLOTS
)
Expand All @@ -1686,7 +1689,7 @@ describe('App', () => {

it('should not wrap the custom plots in a big grid (virtualize them) when there are twenty or fewer small plots', async () => {
await renderAppAndChangeSize(
{ comparison: comparisonTableFixture, custom: createCustomPlots(20) },
{ custom: createCustomPlots(20) },
4,
PlotsSection.CUSTOM_PLOTS
)
Expand Down
2 changes: 1 addition & 1 deletion webview/src/plots/components/GetStarted.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const AddPlots: React.FC<AddPlotsProps> = ({
hasUnselectedPlots
}: AddPlotsProps) => (
<div>
<p>No Plots to Display.</p>
<p>There are no selected plots or experiments.</p>
<div>
<StartButton
onClick={() =>
Expand Down
41 changes: 27 additions & 14 deletions webview/src/plots/components/Plots.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { TemplatePlotsWrapper } from './templatePlots/TemplatePlotsWrapper'
import { ComparisonTableWrapper } from './comparisonTable/ComparisonTableWrapper'
import { Ribbon } from './ribbon/Ribbon'
import { setMaxNbPlotsPerRow, setZoomedInPlot } from './webviewSlice'
import styles from './styles.module.scss'
import { EmptyState } from '../../shared/components/emptyState/EmptyState'
import { Modal } from '../../shared/components/modal/Modal'
import { WebviewWrapper } from '../../shared/components/webviewWrapper/WebviewWrapper'
Expand All @@ -24,6 +25,7 @@ const PlotsContent = () => {
const hasTemplateData = useSelector(
(state: PlotsState) => state.template.hasData
)
const hasCustomData = useSelector((state: PlotsState) => state.custom.hasData)
const wrapperRef = createRef<HTMLDivElement>()

useLayoutEffect(() => {
Expand All @@ -47,13 +49,32 @@ const PlotsContent = () => {
return <EmptyState>Loading Plots...</EmptyState>
}

const modal = zoomedInPlot?.plot && (
<Modal
onClose={() => {
dispatch(setZoomedInPlot(undefined))
}}
>
<ZoomedInPlot props={zoomedInPlot.plot} />
</Modal>
)

if (!hasComparisonData && !hasTemplateData) {
return (
<GetStarted
addItems={<AddPlots hasUnselectedPlots={hasUnselectedPlots} />}
showEmpty={!hasPlots}
welcome={<Welcome />}
/>
<div className={styles.getStartedWrapper}>
<GetStarted
addItems={<AddPlots hasUnselectedPlots={hasUnselectedPlots} />}
showEmpty={!hasPlots}
welcome={<Welcome />}
isFullScreen={!hasCustomData}
/>
{hasCustomData && (
<>
<CustomPlotsWrapper />
{modal}
</>
)}
</div>
)
}

Expand All @@ -64,15 +85,7 @@ const PlotsContent = () => {
<ComparisonTableWrapper />
<CustomPlotsWrapper />

{zoomedInPlot?.plot && (
<Modal
onClose={() => {
dispatch(setZoomedInPlot(undefined))
}}
>
<ZoomedInPlot props={zoomedInPlot.plot} />
</Modal>
)}
{modal}
</div>
)
}
Expand Down
Loading