fix(TooltipHandler): initialize the div when tooltip is shown#591
fix(TooltipHandler): initialize the div when tooltip is shown#591tbouffard merged 5 commits intomaxGraph:mainfrom
Conversation
WalkthroughThe changes made to the Changes
Assessment against linked issues
Possibly related PRs
Suggested labels
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (3)
packages/core/src/view/handler/TooltipHandler.ts (3)
44-74: LGTM! Consider adding error handling for document.body check.The initialization logic has been properly moved out of the constructor, aligning with the PR objectives. The event listeners are well-structured and handle edge cases appropriately.
Consider adding error handling for cases where
document.bodyis null:init() { if (document.body != null) { this.div = document.createElement('div'); // ... rest of the implementation + } else { + console.warn('TooltipHandler: Document body not available for tooltip initialization'); } }
302-317: Consider improving null safety in the show method.While the initialization flow is correct, the code assumes
init()always succeeds and uses null assertions (!) extensively. This could be made more robust.Consider this safer implementation:
if (this.div == null) { this.init(); } -this.div!.style.zIndex = String(this.zIndex); -this.div!.style.left = `${x + origin.x}px`; -this.div!.style.top = `${y + TOOLTIP_VERTICAL_OFFSET + origin.y}px`; +if (this.div) { + this.div.style.zIndex = String(this.zIndex); + this.div.style.left = `${x + origin.x}px`; + this.div.style.top = `${y + TOOLTIP_VERTICAL_OFFSET + origin.y}px`; if (!isNode(tip)) { - this.div!.innerHTML = (tip as string).replace(/\n/g, '<br>'); + this.div.innerHTML = (tip as string).replace(/\n/g, '<br>'); } else { - this.div!.innerHTML = ''; - this.div!.appendChild(tip as HTMLElement); + this.div.innerHTML = ''; + this.div.appendChild(tip as HTMLElement); } - this.div!.style.visibility = ''; - fit(this.div!); + this.div.style.visibility = ''; + fit(this.div); +} else { + console.warn('TooltipHandler: Failed to initialize tooltip div'); +}
199-199: Fix indentation for better readability.Add two spaces of indentation to align with the code style.
- (me.isSource(state.shape) || !me.isSource(state.text))))) + (me.isSource(state.shape) || !me.isSource(state.text)))))🧰 Tools
🪛 eslint
[error] 199-199: Insert
··(prettier/prettier)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
packages/core/src/view/handler/TooltipHandler.ts(7 hunks)
🧰 Additional context used
🪛 eslint
packages/core/src/view/handler/TooltipHandler.ts
[error] 199-199: Insert ··
(prettier/prettier)
🔇 Additional comments (4)
packages/core/src/view/handler/TooltipHandler.ts (4)
86-86: LGTM! Property type correctly updated for lazy initialization.
The div property type has been properly updated to be optional and nullable, which aligns with the lazy initialization pattern.
239-240: LGTM! Null check properly added for lazy initialization.
The reset method now correctly handles the case where the div hasn't been initialized yet.
327-336: LGTM! Proper cleanup implementation.
The destroy method properly handles cleanup with appropriate null checks and correct order of operations.
Line range hint 1-341: Verify the tooltip initialization behavior.
The implementation successfully achieves the PR objective of lazy tooltip initialization. Let's verify that no other code paths create the tooltip div prematurely.
✅ Verification successful
Tooltip initialization is correctly implemented and isolated
The verification confirms that:
- The tooltip div is only created in the
init()method ofTooltipHandler - All div accesses are properly guarded with null checks
- The tooltip handler is properly registered as a plugin and used consistently across the codebase
- No other code paths create the tooltip div prematurely
The implementation successfully achieves lazy initialization of the tooltip div, and the changes are safe and consistent with the codebase.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for any other potential tooltip div creation paths
# and verify that the tooltip div is only created in the init method
# Search for div creation
echo "Checking for div creation patterns:"
rg "createElement.*div" --type ts
# Search for mxTooltip class usage
echo "Checking for mxTooltip class usage:"
rg "className.*mxTooltip" --type ts
# Search for tooltip initialization patterns
echo "Checking for tooltip initialization patterns:"
rg "new TooltipHandler" --type ts -A 5
Length of output: 6617
Script:
#!/bin/bash
# Let's verify the tooltip initialization with a more focused search
# Check for TooltipHandler instantiation and usage
echo "Checking TooltipHandler instantiation and usage:"
rg "TooltipHandler" --type ts -B 2 -A 2
# Check for any direct div access in TooltipHandler
echo "Checking div access patterns in TooltipHandler:"
ast-grep --pattern 'this.div' packages/core/src/view/handler/TooltipHandler.ts
# Check for any tooltip-related plugin registration
echo "Checking tooltip plugin registration:"
rg "TooltipHandler.pluginId" --type ts
Length of output: 12075
8bcf665 to
3834a52
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (3)
packages/core/src/view/handler/TooltipHandler.ts (3)
44-74: Consider handling document.body availability more robustlyThe implementation correctly moves tooltip creation to a lazy-loaded approach. However, there's a potential edge case where document.body might not be available when init() is called.
Consider using a more robust approach:
init() { - if (document.body != null) { + if (!document.body) { + // Wait for document.body to be available + setTimeout(() => this.init(), 0); + return; + } this.div = document.createElement('div'); // ... rest of the implementation }
302-317: Consider more robust null handlingWhile the initialization check is good, the subsequent non-null assertions (
this.div!) could be handled more elegantly to maintain type safety.Consider refactoring to avoid multiple non-null assertions:
show(tip: HTMLElement | string | null, x: number, y: number) { if (!this.destroyed && tip && tip !== '') { const origin = getScrollOrigin(); if (this.div == null) { this.init(); } + if (this.div == null) { + console.warn('Failed to initialize tooltip div'); + return; + } - this.div!.style.zIndex = String(this.zIndex); - this.div!.style.left = `${x + origin.x}px`; + const div = this.div; // Create a local reference + div.style.zIndex = String(this.zIndex); + div.style.left = `${x + origin.x}px`; // ... rest of the method using div instead of this.div! } }
Line range hint
1-341: Consider adding tests for lazy initializationThe changes successfully implement lazy loading of the tooltip div, but we should ensure this behavior is covered by tests.
Would you like me to help create test cases that verify:
- Tooltip div is not created on initialization
- Tooltip div is created only when show() is called
- Proper cleanup in various scenarios
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
packages/core/src/view/handler/TooltipHandler.ts(6 hunks)
🔇 Additional comments (3)
packages/core/src/view/handler/TooltipHandler.ts (3)
86-86: LGTM: Type change supports lazy initialization
The type change to div?: HTMLElement | null properly reflects the new lazy initialization pattern where div may not exist until needed.
239-240: LGTM: Improved visibility check
The condition (!this.div || this.div.style.visibility == 'hidden') correctly handles both initial state and subsequent updates.
289-292: LGTM: Proper null checking
The null check before accessing div properties prevents potential runtime errors when the tooltip hasn't been initialized.
|
Hi this looks promising, I should be able to review this PR this week. |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
packages/core/src/view/handler/TooltipHandler.ts (2)
44-74: Ensure event cleanup for 'mouseleave' on containerThe lazy creation of the tooltip
divis well done to only instantiate when needed. However, consider removing the'mouseleave'listener from the graph container upon destruction. This helps prevent memory leaks if the container remains in the DOM after the tooltip handler has been destroyed.
289-292: Avoid clearing innerHTML if other child nodes must persistClearing
.innerHTMLis generally fine for a pure text tooltip. However, if the tooltip could at times contain other nodes, ensure that removing them is indeed the desired behavior. Using.textContent = ''might be safer for text-only contexts.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/core/src/view/handler/TooltipHandler.ts(6 hunks)packages/html/.storybook/preview.ts(1 hunks)
🔇 Additional comments (10)
packages/html/.storybook/preview.ts (1)
30-32: Good addition of.mxTooltipfor cleanupExtending the selector to include
.mxTooltipensures that tooltip elements are now correctly removed between stories, preventing leftover DOM elements. This aligns well with the refactor in the tooltip handler that lazily creates.mxTooltip.packages/core/src/view/handler/TooltipHandler.ts (9)
86-86: Optional property fordivimproves state managementMaking
divoptional and nullable is a neat change, allowing you to conditionally initialize it and avoid potential null references. This also supports the lazy creation approach ininit().
240-240: Visibility check prevents redundant show callsThe condition
(!this.div || this.div.style.visibility == 'hidden')is a good safeguard to avoid re-showing an already visible tooltip. Confirm that this logic covers scenarios where the tooltip content needs to be updated while currently visible. If dynamic content updates are needed, consider removing or adjusting the visibility check.
302-304: Initialization on demand aligns with lazy loading goalsAuto-initializing the tooltip on show ensures minimal DOM footprint at startup. This matches the PR objective of deferring
mxTooltipcreation until necessary.
310-313: Efficient node-based tooltip insertionHandling the node-based tooltip separately is a nice approach to support rich content. This keeps the design flexible and works well with the new lazy creation logic.
316-317:fitcall ensures tooltip stays in viewportNice step to adjust position after insertion. This helps avoid tooltips cropped by the browser window.
326-326: Cleaner approach to reset the timer inonDestroyClearing the pending timeout thread prevents lingering events after destruction and is consistent with best practices for resource cleanup.
328-330: Double-checkInternalEvent.releaseusageReleasing the tooltip element before removing it is good. Ensure there are no references held by other event listeners or outside code that might become stale after this call.
332-332: Ensuring complete removal from DOMRemoving the
divfrom its parent is correct if it’s guaranteed that the parent is in the normal DOM, not in a user-provided container. Confirm that dynamic container changes do not lead to unexpected side effects.
337-337: Nullingthis.divfinalizes cleanupClearing the reference ensures the tooltip handler cannot accidentally attempt operations on an outdated element. This aligns with the overall PR goal of preventing leftover DOM usage.
tbouffard
left a comment
There was a problem hiding this comment.
LGTM
Tested with ts-example and with Storybook by switching from to Boundary
- events: use tooltip
- boundary: not using the tooltip, no new tooltip, tooltip created by the Events story is cleaned by the storybook script
PR Checklist
maxGraph, and you are assigned to the issue.packages/core/_tests_or a new or altered Storybook story inpackages/html/stories(an existing story may also demonstrate the change).I have added or edited necessary documentation,or no docs changes are needed.Overview
Current maxGraph Tooltip Handler code was not functioning same as mxGraph since we were creating the div inside constructor and not when
showfunction was called like in maxGraph. This change fixes that by moving the logic from constructor to init function which is called ifshowfunction is called and div was null at that moment.Tooltip functionality was tested on:
Notes
I did not provide any screenshots since it is pointless to screenshot the DOM that doesn't include mxTooltip.
Summary by CodeRabbit
New Features
Bug Fixes