Skip to content

[ENHANCEMENT] Improve canvas user experience with new tooltips and legend display - #858

Open
adrianSepiol wants to merge 12 commits into
perses:mainfrom
adrianSepiol:enhancement/canvas-improvements
Open

adrianSepiol wants to merge 12 commits into
perses:mainfrom
adrianSepiol:enhancement/canvas-improvements

Conversation

@adrianSepiol

@adrianSepiol adrianSepiol commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Description

Addresses the improvements described in perses/perses#4486.

Adds a tooltip inside the canvas to make navigation easier for users. The issue where the preview doesn't show the
whole display is not solved in code, but the new tooltip shows a shortcut to "fit everything in view," which should
help mitigate it.

Screenshots

New tooltip for canvas shortcuts (legend with long values also visible):

Screenshot 2026-10-05 at 15 07 47

New tooltip for edge thicknes section:

Screenshot 2026-10-05 at 15 08 44

Checklist

  • Pull request has a descriptive title and context useful to a reviewer.
  • Pull request title follows the [<catalog_entry>] <commit message> naming convention using one of the
    following catalog_entry values: FEATURE, ENHANCEMENT, BUGFIX, BREAKINGCHANGE, DOC,IGNORE.
  • All commits have DCO signoffs.

UI Changes

  • Changes that impact the UI include screenshots and/or screencasts of the relevant changes.
  • Code follows the UI guidelines.

…d shortcuts tooltip

Signed-off-by: Adrian Sepiół <a.sepiol@sap.com>
Signed-off-by: Adrian Sepiół <a.sepiol@sap.com>
Signed-off-by: Adrian Sepiół <a.sepiol@sap.com>
Signed-off-by: Adrian Sepiół <a.sepiol@sap.com>
@adrianSepiol
adrianSepiol requested a review from a team as a code owner October 5, 2026 13:18
@adrianSepiol
adrianSepiol requested review from jgbernalp and removed request for a team October 5, 2026 13:18
@adrianSepiol adrianSepiol changed the title [ENHANCEMENT] Improve canvas user experience with new tooltips [ENHANCEMENT] Improve canvas user experience with new tooltips and legend display Oct 5, 2026
Signed-off-by: Adrian Sepiół <a.sepiol@sap.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Tooltip accessibility and unrecoverable legend truncation need correction before approval.

Review effort: Balanced
Findings: 3 Medium severity

Open (3)
What changed in this PR

Improves the Canvas plugin usability requested in Perses issue #4486.

Changes:

  • Centralizes mouse shortcuts and adds resolver tests.
  • Adds canvas shortcut and edge-thickness tooltips.
  • Adjusts legend positioning and truncates long labels.
File Description
canvas/​src/​utils/​shortcuts.ts Defines canvas shortcuts and event resolution.
canvas/​src/​utils/​shortcuts.test.ts Tests shortcut resolution.
canvas/​src/​hooks/​useZoom.ts Uses centralized shortcuts for zoom and pan.
canvas/​src/​components/​shared/​CanvasShortcutsTooltip.tsx Displays canvas shortcut guidance.
canvas/​src/​components/​settings/​GlobalSettingsEditor.tsx Adds edge-thickness help.
canvas/​src/​components/​panel/​ThresholdLegend.tsx Constrains long legend labels.
canvas/​src/​components/​panel/​CanvasPanel.tsx Adds shortcut help and updates legend placement.
canvas/​src/​components/​editor/​EditorCanvas.tsx Centralizes double-click shortcut handling.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread canvas/src/components/panel/ThresholdLegend.tsx Outdated
Comment thread canvas/src/components/settings/GlobalSettingsEditor.tsx Outdated
Comment thread canvas/src/components/shared/CanvasShortcutsTooltip.tsx Outdated
adrianSepiol and others added 2 commits October 6, 2026 14:58
…for truncated legend labels'

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Adrian <56292595+adrianSepiol@users.noreply.github.com>
…tcutsTooltip and GlobalSettingsEditor

Signed-off-by: Adrian Sepiół <a.sepiol@sap.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The new tooltip controls contain accessibility issues, including misleading labeling and insufficient dark-theme contrast.

1 open finding
3 resolved since last review
Previously missed (2)

In code that hasn't changed since last review

Medium severity Use theme-aware color for information icon contrast

canvas/​src/​components/​settings/​GlobalSettingsEditor.tsx:87

Hard-coding grey[700] makes this information icon low-contrast against the default dark-theme surface. Use a semantic, theme-aware foreground color so the icon remains visible in both light and dark modes.

Low severity Document required fitView/resetView and useZoom implementation

canvas/​src/​utils/​shortcuts.ts:16

This comment overstates the single-source guarantee: adding a definition here only makes it resolvable and visible in the tooltip; the behavior still has to be implemented in the fitView/resetView switches and accepted by useZoom for zoom/pan. Document that requirement so future entries are not advertised without functioning.

🧠 Review effort: Balanced

Comment thread canvas/src/components/shared/CanvasShortcutsTooltip.tsx Outdated
…ortcuts file

Signed-off-by: Adrian Sepiół <a.sepiol@sap.com>

@Nexucis Nexucis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🤖 AI-generated review. This review was produced by an AI assistant (GitHub Copilot) at the request of the maintainer posting it. Please treat it as input to discuss, not as a final decision.

Thanks for this. Putting all the mouse shortcuts in one table (utils/shortcuts.ts / resolveMouseShortcut) is a nice improvement: the d3 zoom filter, both double-click handlers and the tooltip text now come from the same place. The behavior matches what was there before, and the wheel-zoom "grabbing" cursor flicker is fixed along the way.

Validation (PR head 80761ab): tsc --noEmit ✅, vitest run ✅ (14 files, 155 tests), oxlint on the changed files ⚠️ 4 new react-perf warnings compared to main (details in the inline comments).

Main points

  1. The shortcuts tooltip is missing from the editor. It's only mounted in CanvasPanel. The editor canvas uses the same gestures, plus box-select and Delete/Backspace, so users first discover them there. Could the component be reused in the editor's toolbar row?
  2. Could this be a panel header action instead of an overlay? Right now the button is drawn on top of the content of every canvas panel, which adds a wrapper Box, magic numbers for the legend offset, and covers whatever node sits in the top-right corner. Other plugins already put this kind of button in the panel header via the plugin's actions (BarChart.ts, TracingGanttChart.ts): actions: [{ component: CanvasShortcutsAction, location: 'header' }].
  3. The edge-thickness tooltip should explain what issue item 3 asks for: the width follows the latest value of the edge's query, and the edge must use thickness mode Threshold (see inline).
  4. Tests: resolveMouseShortcut is well covered. There are no tests yet for CanvasShortcutsTooltip (accessible name, rows shown on hover/focus, ⌘ vs Ctrl), the legend truncation and its <title>, or the useZoom isPanning change.

Out of scope (maybe a follow-up issue)

strokeWidthFromThresholds keeps the last step in array order that matches, but edgeThresholdWidths is stored in the order the user edits it, not sorted by value. Setting a higher step's width before a lower one can therefore pick the wrong width.


const LEGEND_MARGIN = 8;
const BTN_MARGIN = 4;
const BTN_HEIGHT = 32;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

BTN_HEIGHT = 32 doesn't match the button that's actually rendered: a size="small" IconButton with padding: '2px' and a fontSize="small" icon is about 24px tall. If the overlay stays, compute the offset from the same constants used to style the button. If the button moves to a panel header action (see the summary), this constant and the shifted legendY aren't needed anymore.

? BTN_MARGIN + BTN_HEIGHT + BTN_MARGIN
: height - ((spec.thresholds?.steps?.length ?? 0) + 1) * 18 - 24;

const shortcutsBtnSx = useMemo(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: useMemo(() => …, []) for a static object, while PANEL_WRAPPER_SX just above is a module constant. Make this a module-level constant too, for consistency.

) : null}
</svg>
<Box sx={shortcutsBtnSx}>
<CanvasShortcutsTooltip />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This overlay shows on every canvas panel in view mode and can cover nodes placed in the top-right corner. A static tooltip doesn't need ZoomContext, so it could be registered as a header action in Canvas.tsx (actions: [{ component, location: 'header' }], like barchart/src/BarChart.ts). The same component could then be reused in the editor (EditorItemsPanel).

const NO_SELECT_STYLE = { userSelect: 'none' } as const;

// Approximate character width for font-size 11px sans-serif.
const CHAR_WIDTH_APPROX = 6.5;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A fixed 6.5px per character is fragile here: the <text> doesn't set fontFamily, so it inherits the theme font, and the limit works out to 11 characters including the ≥ prefix. Labels that end in a unit (e.g. ≥ 1.23 MB/s) lose the unit first. Options:

  • measure once with getComputedTextLength() (or size the box to the longest label, with a max width)
  • or at least set fontFamily explicitly so the estimate holds.

truncateLabel could also run inside the existing rows useMemo.

<InfoTooltip
id="edge-thickness-info-tooltip"
title="Edge thickness"
description="Defines stroke widths used when edges have thickness mode set to threshold. The default width applies when no threshold step matches the query value."

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggestion for the text, closer to what issue item 3 asks to explain and to what PanelEdgeLayer.resolveEdgeStyle does:

Suggested change
description="Defines stroke widths used when edges have thickness mode set to threshold. The default width applies when no threshold step matches the query value."
description="When an edge has a query and its thickness mode is set to Threshold, its width follows the latest value of that query: the width of the highest matching threshold step is used. The default width applies otherwise (fixed mode without a custom width, no data, or no matching step)."

Comment thread canvas/src/utils/shortcuts.ts Outdated

function detectMac(): boolean {
if (typeof navigator === 'undefined') return false;
const p = (navigator.platform ?? navigator.userAgent).toLowerCase();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

navigator.platform is deprecated and returns "" (not null/undefined) in some environments, so ?? never falls back to userAgent. Suggest:

Suggested change
const p = (navigator.platform ?? navigator.userAgent).toLowerCase();
const p = ((navigator as Navigator & { userAgentData?: { platform?: string } }).userAgentData?.platform || navigator.platform || navigator.userAgent).toLowerCase();

Comment thread canvas/src/utils/shortcuts.ts Outdated
}

export interface MouseShortcutDefinition {
mouseEvent: string;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: narrow the type to 'wheel' | 'mousedown' | 'dblclick', and make SHORTCUTS readonly (as const satisfies readonly MouseShortcut[]). A short comment would also help: adding an entry shows it in the tooltip, but it only works if useZoom accepts it (zoom/pan) or the double-click switch statements handle it.

Comment thread canvas/src/utils/shortcuts.test.ts Outdated
expect(resolveMouseShortcut({ type: 'wheel', ctrlKey: true, metaKey: false })?.id).toBe('zoom');
});

it('returns zoom on wheel + metaKey (pinch-to-zoom)', () => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Trackpad pinch-to-zoom sends a wheel event with ctrlKey: true, not metaKey. Rename this test, or move the pinch note to the ctrlKey case.

Comment thread canvas/src/hooks/useZoom.ts Outdated
return event instanceof MouseEvent && event.button === 1;
});
zoomBehavior.on('start', () => setIsPanning(true));
zoomBehavior.on('start', ({ sourceEvent }: { sourceEvent: Event }) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice: wheel zoom no longer sets isPanning. Small typing nit: sourceEvent is null for programmatic zoomBehavior.transform calls (fitView/resetPan), so the type should be Event | null. The instanceof check already handles it at runtime.

const boundingBox = nodeBoundingBox(displayNodes);
if (boundingBox) {
fitView(boundingBox, width, height);
switch (resolveMouseShortcut(event)?.id) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The editor uses the same shortcuts (plus box-select and Delete/Backspace), but no CanvasShortcutsTooltip is rendered for it. Consider adding it to EditorItemsPanel's toolbar row so editors can find these interactions too.

@ibakshay

ibakshay commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

@adrianSepiol I did a manual testing now and the functionality is working as expected! Thank you for the PR! 🚀

Signed-off-by: Adrian Sepiół <a.sepiol@sap.com>
… split view/editor tooltips, and optimized hot-path resolvers

Signed-off-by: Adrian Sepiół <a.sepiol@sap.com>
Signed-off-by: Adrian Sepiół <a.sepiol@sap.com>
Signed-off-by: Adrian Sepiół <a.sepiol@sap.com>
@adrianSepiol

Copy link
Copy Markdown
Contributor Author

Moved tooltip to actions:

Screenshot 2026-10-08 at 17 19 37

Added tooltip to editor:

Screenshot 2026-10-08 at 17 19 57

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants