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

Add plots ribbon to show and remove experiments #1785

Merged
merged 11 commits into from
May 31, 2022
11 changes: 11 additions & 0 deletions extension/src/plots/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ export class Plots extends BaseRepository<TPlotsData> {
return this.selectExperimentsFromWebview()
case MessageFromWebviewType.REFRESH_REVISION:
return this.attemptToRefreshData(message.payload)
case MessageFromWebviewType.TOGGLE_EXPERIMENT:
return this.setExperimentStatus(message.payload)
default:
Logger.error(`Unexpected message: ${JSON.stringify(message)}`)
}
Expand Down Expand Up @@ -353,6 +355,15 @@ export class Plots extends BaseRepository<TPlotsData> {
)
}

private setExperimentStatus(id: string) {
this.experiments?.toggleExperimentStatus(id)
sendTelemetryEvent(
EventName.VIEWS_EXPERIMENTS_TABLE_EXPERIMENT_TOGGLE,
sroy3 marked this conversation as resolved.
Show resolved Hide resolved
undefined,
undefined
)
}

private attemptToRefreshData(revision: string) {
Toast.infoWithOptions(`Attempting to refresh ${revision} plots data.`)
this.plots?.setupManualRefresh(revision)
Expand Down
7 changes: 4 additions & 3 deletions extension/src/plots/model/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import {
CheckpointPlotData,
ComparisonPlots,
ComparisonRevision,
Revision,
ComparisonRevisionData,
DEFAULT_SECTION_COLLAPSED,
DEFAULT_SECTION_NAMES,
Expand Down Expand Up @@ -187,13 +187,14 @@ export class PlotsModel extends ModelWithPersistence {
}

public getSelectedRevisionDetails() {
return reorderObjectList<ComparisonRevision>(
return reorderObjectList<Revision>(
this.comparisonOrder,
this.experiments
.getSelectedRevisions()
.map(({ label: revision, displayColor, logicalGroupName }) => ({
.map(({ label: revision, displayColor, logicalGroupName, id }) => ({
displayColor,
group: logicalGroupName,
id,
revision
})),
'revision'
Expand Down
4 changes: 2 additions & 2 deletions extension/src/plots/vega/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
RepeatMapping
} from 'vega-lite/build/src/spec/repeat'
import { TopLevelUnitSpec } from 'vega-lite/build/src/spec/unit'
import { ColorScale, ComparisonRevision } from '../webview/contract'
import { ColorScale, Revision } from '../webview/contract'

const COMMIT_FIELD = 'rev'

Expand Down Expand Up @@ -84,7 +84,7 @@ export const isMultiViewByCommitPlot = (
): boolean => !template || getFacetField(template) === COMMIT_FIELD

export const getColorScale = (
revisions: ComparisonRevision[]
revisions: Revision[]
): ColorScale | undefined => {
const acc: ColorScale = { domain: [], range: [] }

Expand Down
5 changes: 3 additions & 2 deletions extension/src/plots/webview/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,15 @@ export type ComparisonPlots = {
revisions: ComparisonRevisionData
}[]

export type ComparisonRevision = {
export type Revision = {
id?: string
revision: string
group?: string
displayColor: Color
}

export interface PlotsComparisonData {
revisions: ComparisonRevision[]
revisions: Revision[]
plots: ComparisonPlots
sectionName: string
size: PlotSize
Expand Down
2 changes: 2 additions & 0 deletions extension/src/telemetry/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const EventName = Object.assign(

VIEWS_PLOTS_CLOSED: 'views.plots.closed',
VIEWS_PLOTS_CREATED: 'views.plots.created',
VIEWS_PLOTS_EXPERIMENT_TOGGLE: 'views.plots.toggleExperimentStatus',
VIEWS_PLOTS_FOCUS_CHANGED: 'views.plots.focusChanged',
VIEWS_PLOTS_MANUAL_REFRESH: 'views.plots.manualRefresh',
VIEWS_PLOTS_METRICS_SELECTED: 'views.plots.metricsSelected',
Expand Down Expand Up @@ -207,6 +208,7 @@ export interface IEventNamePropertyMapping {
[EventName.VIEWS_PLOTS_SECTION_TOGGLE]: Partial<SectionCollapsed>
[EventName.VIEWS_PLOTS_SELECT_EXPERIMENTS]: undefined
[EventName.VIEWS_PLOTS_SELECT_PLOTS]: undefined
[EventName.VIEWS_PLOTS_EXPERIMENT_TOGGLE]: undefined
[EventName.VIEWS_REORDER_PLOTS_METRICS]: undefined
[EventName.VIEWS_REORDER_PLOTS_TEMPLATES]: undefined

Expand Down
60 changes: 32 additions & 28 deletions webview/src/plots/components/Plots.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { GetStarted } from './GetStarted'
import { CheckpointPlotsWrapper } from './checkpointPlots/CheckpointPlotsWrapper'
import { TemplatePlotsWrapper } from './templatePlots/TemplatePlotsWrapper'
import { ComparisonTableWrapper } from './comparisonTable/ComparisonTableWrapper'
import { Ribbon } from './ribbon/Ribbon'
import { PlotsWebviewState } from '../hooks/useAppReducer'
import { EmptyState } from '../../shared/components/emptyState/EmptyState'
import { Modal } from '../../shared/components/modal/Modal'
Expand Down Expand Up @@ -113,33 +114,36 @@ const PlotsContent = ({ state }: PlotsProps) => {
) => section?.size || PlotSize.REGULAR

return (
<DragDropProvider>
<PlotsSizeProvider
sizes={{
[Section.CHECKPOINT_PLOTS]: currentSizeOrRegular(checkpointPlots),
[Section.TEMPLATE_PLOTS]: currentSizeOrRegular(templatePlots),
[Section.COMPARISON_TABLE]: currentSizeOrRegular(comparisonTable)
}}
>
{templatePlots && (
<TemplatePlotsWrapper
templatePlots={templatePlots}
{...wrapperProps}
/>
)}
{comparisonTable && (
<ComparisonTableWrapper
comparisonTable={comparisonTable}
{...wrapperProps}
/>
)}
{checkpointPlots && (
<CheckpointPlotsWrapper
checkpointPlots={checkpointPlots}
{...wrapperProps}
/>
)}
</PlotsSizeProvider>
<>
<Ribbon revisions={comparisonTable?.revisions || []} />
<DragDropProvider>
<PlotsSizeProvider
sizes={{
[Section.CHECKPOINT_PLOTS]: currentSizeOrRegular(checkpointPlots),
[Section.TEMPLATE_PLOTS]: currentSizeOrRegular(templatePlots),
[Section.COMPARISON_TABLE]: currentSizeOrRegular(comparisonTable)
}}
>
{templatePlots && (
<TemplatePlotsWrapper
templatePlots={templatePlots}
{...wrapperProps}
/>
)}
{comparisonTable && (
<ComparisonTableWrapper
comparisonTable={comparisonTable}
{...wrapperProps}
/>
)}
{checkpointPlots && (
<CheckpointPlotsWrapper
checkpointPlots={checkpointPlots}
{...wrapperProps}
/>
)}
</PlotsSizeProvider>
</DragDropProvider>

{zoomedInPlot && (
<Modal onClose={handleModalClose}>
Expand All @@ -160,7 +164,7 @@ const PlotsContent = ({ state }: PlotsProps) => {
</div>
</Modal>
)}
</DragDropProvider>
</>
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import { MessageFromWebviewType } from 'dvc/src/webview/contract'
import comparisonTableFixture from 'dvc/src/test/fixtures/plotsDiff/comparison'
import React from 'react'
import { ComparisonRevision } from 'dvc/src/plots/webview/contract'
import { Revision } from 'dvc/src/plots/webview/contract'
import { ComparisonTable, ComparisonTableProps } from './ComparisonTable'
import {
createBubbledEvent,
Expand Down Expand Up @@ -205,7 +205,7 @@ describe('ComparisonTable', () => {
const newRevisions = [
...basicProps.revisions,
{ displayColor: '#000000', revision: newRevName }
] as ComparisonRevision[]
] as Revision[]

rerender(<ComparisonTable {...basicProps} revisions={newRevisions} />)
const headers = getHeaders().map(header => header.textContent)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
ComparisonPlots,
ComparisonRevision,
Revision,
PlotsComparisonData
} from 'dvc/src/plots/webview/contract'
import { reorderObjectList } from 'dvc/src/util/array'
Expand Down Expand Up @@ -40,7 +40,7 @@ export const ComparisonTable: React.FC<ComparisonTableProps> = ({
useEffect(
() =>
setColumns(() => {
const acc: ComparisonRevision[] = []
const acc: Revision[] = []

for (const column of revisions) {
if (isPinned(column)) {
Expand All @@ -59,11 +59,7 @@ export const ComparisonTable: React.FC<ComparisonTableProps> = ({
useEffect(() => setComparisonPlots(plots), [plots])

const setColumnsOrder = (order: string[]) => {
const newOrder = reorderObjectList<ComparisonRevision>(
order,
columns,
'revision'
)
const newOrder = reorderObjectList<Revision>(order, columns, 'revision')
setColumns(newOrder)
sendMessage({
payload: newOrder.map(({ revision }) => revision),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from 'react'
import { ComparisonRevision } from 'dvc/src/plots/webview/contract'
import { Revision } from 'dvc/src/plots/webview/contract'
import cx from 'classnames'
import styles from './styles.module.scss'
import { DropTarget } from './DropTarget'
import { ComparisonTableHeader } from './ComparisonTableHeader'
import { DragDropContainer } from '../../../shared/components/dragDrop/DragDropContainer'

export type ComparisonTableColumn = ComparisonRevision
export type ComparisonTableColumn = Revision

interface ComparisonTableHeadProps {
columns: ComparisonTableColumn[]
Expand Down
30 changes: 30 additions & 0 deletions webview/src/plots/components/ribbon/Ribbon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Revision } from 'dvc/src/plots/webview/contract'
import { MessageFromWebviewType } from 'dvc/src/webview/contract'
import React from 'react'
import styles from './styles.module.scss'
import { RibbonBlock } from './RibbonBlock'
import { sendMessage } from '../../../shared/vscode'

interface RibbonProps {
revisions: Revision[]
}

export const Ribbon: React.FC<RibbonProps> = ({ revisions }) => {
const removeRevision = (revision: string) => {
sendMessage({
payload: revision,
type: MessageFromWebviewType.TOGGLE_EXPERIMENT
})
}
return (
<div className={styles.list}>
{revisions.map(revision => (
<RibbonBlock
revision={revision}
key={revision.revision}
onClear={() => removeRevision(revision.id || '')}
/>
))}
</div>
)
}
63 changes: 63 additions & 0 deletions webview/src/plots/components/ribbon/RibbonBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Revision } from 'dvc/src/plots/webview/contract'
import React, { useRef, useState, useEffect } from 'react'
import styles from './styles.module.scss'
import { AllIcons, Icon } from '../../../shared/components/Icon'
import Tooltip from '../../../shared/components/tooltip/Tooltip'

interface RibbonBlockProps {
revision: Revision
onClear: () => void
}

enum CopyTooltip {
NORMAL = 'Copy',
COPIED = 'Copied'
}

export const RibbonBlock: React.FC<RibbonBlockProps> = ({
revision,
onClear
}) => {
const [copyTooltip, setCopyTooltip] = useState(CopyTooltip.NORMAL)
const copyTooltipTimeout = useRef(0)

useEffect(() => {
return () => {
clearTimeout(copyTooltipTimeout.current)
}
}, [])

const copyExp = async (exp: string) => {
try {
await navigator.clipboard.writeText(exp)
Copy link
Member

Choose a reason for hiding this comment

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

[Q] Do we want to add the copy icon on hover as we do in the experiments table?

image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll try to see if it fits. If it does, I'll open another PR for it.

setCopyTooltip(CopyTooltip.COPIED)
copyTooltipTimeout.current = window.setTimeout(() => {
setCopyTooltip(CopyTooltip.NORMAL)
}, 2000)
} catch {
setCopyTooltip(CopyTooltip.NORMAL)
}
}
const exp = revision.group?.replace(/[[\]]/g, '') || revision.revision

return (
<div
className={styles.block}
style={{ borderColor: revision.displayColor }}
>
<Tooltip content={<>{copyTooltip}</>} hideOnClick={false}>
<button className={styles.label} onClick={() => copyExp(exp)}>
<span>{exp}</span>
{revision.group && (
<span className={styles.subtitle}>{revision.revision}</span>
)}
</button>
</Tooltip>
<Tooltip content="Clear" placement="bottom">
<button className={styles.clearButton} onClick={onClear}>
sroy3 marked this conversation as resolved.
Show resolved Hide resolved
<Icon icon={AllIcons.CLOSE} width={12} height={12} />
</button>
</Tooltip>
</div>
)
}
Loading