diff --git a/webview/src/plots/components/App.test.tsx b/webview/src/plots/components/App.test.tsx index c9f73ab1e8..82165a56cc 100644 --- a/webview/src/plots/components/App.test.tsx +++ b/webview/src/plots/components/App.test.tsx @@ -34,6 +34,7 @@ import { act } from 'react-dom/test-utils' import { App } from './App' import { Plots } from './Plots' import { NewSectionBlock } from './templatePlots/TemplatePlots' +import { SectionDescription } from './PlotsContainer' import { vsCodeApi } from '../../shared/api' import { createBubbledEvent, dragAndDrop, dragEnter } from '../../test/dragDrop' import { DragEnterDirection } from '../../shared/components/dragDrop/util' @@ -1056,6 +1057,33 @@ describe('App', () => { expect(screen.getByTestId('modal')).toBeInTheDocument() }) + it('should show a tooltip with the meaning of each plot section', () => { + renderAppWithData({ + checkpoint: checkpointPlotsFixture, + comparison: comparisonTableFixture, + sectionCollapsed: DEFAULT_SECTION_COLLAPSED, + template: complexTemplatePlotsFixture + }) + + const [templateInfo, comparisonInfo, checkpointInfo] = + screen.getAllByTestId('info-tooltip-toggle') + + fireEvent.mouseEnter(templateInfo, { bubbles: true }) + expect( + screen.getByText(SectionDescription[Section.TEMPLATE_PLOTS]) + ).toBeInTheDocument() + + fireEvent.mouseEnter(comparisonInfo, { bubbles: true }) + expect( + screen.getByText(SectionDescription[Section.COMPARISON_TABLE]) + ).toBeInTheDocument() + + fireEvent.mouseEnter(checkpointInfo, { bubbles: true }) + expect( + screen.getByText(SectionDescription[Section.CHECKPOINT_PLOTS]) + ).toBeInTheDocument() + }) + describe('Virtualization', () => { const changeSize = async (size: string, buttonPosition: number) => { const sizePickerButton = diff --git a/webview/src/plots/components/PlotsContainer.tsx b/webview/src/plots/components/PlotsContainer.tsx index f0f1741ef9..4d3575964f 100644 --- a/webview/src/plots/components/PlotsContainer.tsx +++ b/webview/src/plots/components/PlotsContainer.tsx @@ -15,6 +15,7 @@ import { AllIcons, Icon } from '../../shared/components/Icon' import { IconMenu } from '../../shared/components/iconMenu/IconMenu' import { IconMenuItemProps } from '../../shared/components/iconMenu/IconMenuItem' import { sendMessage } from '../../shared/vscode' +import Tooltip from '../../shared/components/tooltip/Tooltip' export interface PlotsContainerProps { sectionCollapsed: SectionCollapsed @@ -31,6 +32,24 @@ export type BasicContainerProps = Pick< 'onRename' | 'onResize' | 'sectionCollapsed' > +export const SectionDescription = { + [Section.CHECKPOINT_PLOTS]: + 'Linear plots based on data from the experiments table.', + [Section.COMPARISON_TABLE]: + 'A table used to display image plots side by side.', + [Section.TEMPLATE_PLOTS]: + 'JSON, YAML, CSV or TSV files visualized using Vega pre-defined or custom Vega-Lite templates.' +} + +const InfoIcon = () => ( + +) + export const PlotsContainer: React.FC = ({ sectionCollapsed, sectionKey, @@ -96,6 +115,13 @@ export const PlotsContainer: React.FC = ({ onRename(sectionKey, title) } + const tooltipContent = ( +
+ + {SectionDescription[sectionKey]} +
+ ) + return (
@@ -126,6 +152,14 @@ export const PlotsContainer: React.FC = ({ ) : ( sectionTitle )} + +
+ +
+
{open && ( diff --git a/webview/src/plots/components/styles.module.scss b/webview/src/plots/components/styles.module.scss index 830745668f..ab1b2115fa 100644 --- a/webview/src/plots/components/styles.module.scss +++ b/webview/src/plots/components/styles.module.scss @@ -17,6 +17,8 @@ $gap: 20px; margin: 14px 10px; font-weight: bold; font-size: 1.25rem; + display: flex; + align-items: center; } } @@ -321,6 +323,30 @@ $gap: 20px; } } +.infoTooltipToggle { + display: flex; + align-items: center; +} + +.infoIcon { + fill: $accent-color; + margin-left: 6px; +} + +.infoTooltip { + max-width: 220px; + padding: 12px 8px; + white-space: normal; + display: flex; + gap: 4px; + font-size: 0.8125rem; + + svg { + min-width: 16px; + min-height: 16px; + } +} + :global(.has-actions) { summary { background: $fg-color !important; diff --git a/webview/src/shared/components/Icon.tsx b/webview/src/shared/components/Icon.tsx index 898423eea8..c2d1da24db 100644 --- a/webview/src/shared/components/Icon.tsx +++ b/webview/src/shared/components/Icon.tsx @@ -10,6 +10,7 @@ import { Ellipsis, GraphLine, Gripper, + Info, Lines, Pencil, Refresh, @@ -27,6 +28,7 @@ export const AllIcons = { ELLIPSIS: Ellipsis, GRAPH_LINE: GraphLine, GRIPPER: Gripper, + INFO: Info, LINES: Lines, PENCIL: Pencil, REFRESH: Refresh, diff --git a/webview/src/shared/components/icons/Info.tsx b/webview/src/shared/components/icons/Info.tsx new file mode 100644 index 0000000000..b955dbd8ad --- /dev/null +++ b/webview/src/shared/components/icons/Info.tsx @@ -0,0 +1,21 @@ +import * as React from 'react' +import { SVGProps } from 'react' + +const SvgInfo = (props: SVGProps) => ( + + + +) + +export default SvgInfo diff --git a/webview/src/shared/components/icons/index.ts b/webview/src/shared/components/icons/index.ts index bfb653d3e4..f68b45dff2 100644 --- a/webview/src/shared/components/icons/index.ts +++ b/webview/src/shared/components/icons/index.ts @@ -10,6 +10,7 @@ export { default as DownArrow } from './DownArrow' export { default as Ellipsis } from './Ellipsis' export { default as GraphLine } from './GraphLine' export { default as Gripper } from './Gripper' +export { default as Info } from './Info' export { default as Lines } from './Lines' export { default as Pencil } from './Pencil' export { default as Pin } from './Pin'