From d4c8b5fa16661801f9a9cbdf184af44487366fce Mon Sep 17 00:00:00 2001
From: IzStriker <44909896+IzStriker@users.noreply.github.com>
Date: Sun, 17 Dec 2023 20:55:16 +0000
Subject: [PATCH] [TS migration] Reafactor tooltips.js to use typescript
 (#2073)

---
 .../src/components/NotesButton.tsx            |  6 +-
 .../components/{tooltips.js => tooltips.tsx}  | 85 ++++++++++++++++---
 upcoming-release-notes/2073.md                |  6 ++
 3 files changed, 83 insertions(+), 14 deletions(-)
 rename packages/desktop-client/src/components/{tooltips.js => tooltips.tsx} (83%)
 create mode 100644 upcoming-release-notes/2073.md

diff --git a/packages/desktop-client/src/components/NotesButton.tsx b/packages/desktop-client/src/components/NotesButton.tsx
index 366f7c2ea..bbb1e14b6 100644
--- a/packages/desktop-client/src/components/NotesButton.tsx
+++ b/packages/desktop-client/src/components/NotesButton.tsx
@@ -15,7 +15,7 @@ import { remarkBreaks, sequentialNewlinesPlugin } from '../util/markdown';
 import Button from './common/Button';
 import Text from './common/Text';
 import View from './common/View';
-import { Tooltip, useTooltip } from './tooltips';
+import { Tooltip, type TooltipPosition, useTooltip } from './tooltips';
 
 const remarkPlugins = [sequentialNewlinesPlugin, remarkGfm, remarkBreaks];
 
@@ -86,7 +86,7 @@ const markdownStyles = css({
 type NotesTooltipProps = {
   editable?: boolean;
   defaultNotes?: string;
-  position?: string;
+  position?: TooltipPosition;
   onClose?: (notes: string) => void;
 };
 function NotesTooltip({
@@ -138,7 +138,7 @@ type NotesButtonProps = {
   width?: number;
   height?: number;
   defaultColor?: string;
-  tooltipPosition?: string;
+  tooltipPosition?: TooltipPosition;
   style?: CSSProperties;
 };
 export default function NotesButton({
diff --git a/packages/desktop-client/src/components/tooltips.js b/packages/desktop-client/src/components/tooltips.tsx
similarity index 83%
rename from packages/desktop-client/src/components/tooltips.js
rename to packages/desktop-client/src/components/tooltips.tsx
index 990397668..b86a9128a 100644
--- a/packages/desktop-client/src/components/tooltips.js
+++ b/packages/desktop-client/src/components/tooltips.tsx
@@ -1,18 +1,28 @@
-import React, { Component, createContext, createRef, useState } from 'react';
+import {
+  Component,
+  createContext,
+  createRef,
+  useState,
+  type RefObject,
+  type ReactNode,
+  type MouseEventHandler,
+  type MouseEvent,
+  type ContextType,
+} from 'react';
 import ReactDOM from 'react-dom';
 
 import { css } from 'glamor';
 
-import { styles, theme } from '../style';
+import { type CSSProperties, styles, theme } from '../style';
 
-export const IntersectionBoundary = createContext();
+export const IntersectionBoundary = createContext<RefObject<HTMLElement>>(null);
 
 export function useTooltip() {
-  const [isOpen, setIsOpen] = useState(false);
+  const [isOpen, setIsOpen] = useState<boolean>(false);
 
   return {
-    getOpenEvents: (events = {}) => ({
-      onClick: e => {
+    getOpenEvents: (events: { onClick?: MouseEventHandler } = {}) => ({
+      onClick: (e: MouseEvent) => {
         e.stopPropagation();
         events.onClick?.(e);
         setIsOpen(true);
@@ -24,14 +34,56 @@ export function useTooltip() {
   };
 }
 
-export class Tooltip extends Component {
+export type TooltipPosition =
+  | 'top'
+  | 'top-left'
+  | 'top-right'
+  | 'bottom'
+  | 'bottom-left'
+  | 'bottom-right'
+  | 'bottom-stretch'
+  | 'top-stretch'
+  | 'bottom-center'
+  | 'top-center'
+  | 'left-center'
+  | 'right';
+
+type TooltipProps = {
+  position?: TooltipPosition;
+  onClose?: () => void;
+  forceLayout?: boolean;
+  forceTop?: number;
+  ignoreBoundary?: boolean;
+  targetRect?: DOMRect;
+  offset?: number;
+  style?: CSSProperties;
+  width?: number;
+  children: ReactNode;
+  targetHeight?: number;
+};
+type MutableDomRect = {
+  top: number;
+  left: number;
+  width: number;
+  height: number;
+};
+
+export class Tooltip extends Component<TooltipProps> {
   static contextType = IntersectionBoundary;
-  state = { position: null };
+  position: TooltipPosition;
+  contentRef: RefObject<HTMLDivElement>;
+  cleanup: () => void;
+  target: HTMLDivElement;
+  context: ContextType<typeof IntersectionBoundary> = this.context; // assign type to context without using declare.
 
   constructor(props) {
     super(props);
     this.position = props.position || 'bottom-right';
-    this.contentRef = createRef();
+    this.contentRef = createRef<HTMLDivElement>();
+  }
+
+  isHTMLElement(element: unknown): element is HTMLElement {
+    return element instanceof HTMLElement;
   }
 
   setup() {
@@ -107,7 +159,7 @@ export class Tooltip extends Component {
     }
   }
 
-  getContainer() {
+  getContainer(): HTMLElement {
     const { ignoreBoundary = false } = this.props;
 
     if (!ignoreBoundary && this.context) {
@@ -123,6 +175,7 @@ export class Tooltip extends Component {
 
     if (
       container.parentNode &&
+      this.isHTMLElement(container.parentNode) &&
       container.parentNode.style.overflow === 'auto'
     ) {
       return container.parentNode;
@@ -138,9 +191,18 @@ export class Tooltip extends Component {
     }
 
     const box = contentEl.getBoundingClientRect();
+
     const anchorEl = this.target.parentNode;
 
-    let anchorRect = targetRect || anchorEl.getBoundingClientRect();
+    let anchorRect: MutableDomRect | undefined =
+      targetRect ||
+      (this.isHTMLElement(anchorEl)
+        ? anchorEl?.getBoundingClientRect()
+        : undefined);
+
+    if (!anchorRect) {
+      return;
+    }
 
     // Copy it so we can mutate it
     anchorRect = {
@@ -238,6 +300,7 @@ export class Tooltip extends Component {
       bottom: 'inherit',
       left: 'inherit',
       right: 'inherit',
+      width: undefined as string | undefined,
     };
 
     if (
diff --git a/upcoming-release-notes/2073.md b/upcoming-release-notes/2073.md
new file mode 100644
index 000000000..ec82b1108
--- /dev/null
+++ b/upcoming-release-notes/2073.md
@@ -0,0 +1,6 @@
+---
+category: Enhancements
+authors: [IzStriker]
+---
+
+Migrate tooltips.js to typescript
-- 
GitLab