Browse Source

refactor: upgrade react-tooltip to v5

Nicolas Meienberger 2 năm trước cách đây
mục cha
commit
2f0f4bd498

+ 1 - 1
package.json

@@ -62,7 +62,7 @@
     "react-hook-form": "^7.43.7",
     "react-markdown": "^8.0.3",
     "react-select": "^5.6.1",
-    "react-tooltip": "^4.4.3",
+    "react-tooltip": "^5.10.5",
     "redis": "^4.6.5",
     "remark-breaks": "^3.0.2",
     "remark-gfm": "^3.0.1",

+ 12 - 14
pnpm-lock.yaml

@@ -101,8 +101,8 @@ dependencies:
     specifier: ^5.6.1
     version: 5.7.0(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
   react-tooltip:
-    specifier: ^4.4.3
-    version: 4.5.1(react-dom@18.2.0)(react@18.2.0)
+    specifier: ^5.10.5
+    version: 5.10.5(react-dom@18.2.0)(react@18.2.0)
   redis:
     specifier: ^4.6.5
     version: 4.6.5
@@ -3163,6 +3163,10 @@ packages:
     resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==}
     dev: true
 
+  /classnames@2.3.2:
+    resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==}
+    dev: false
+
   /cli-cursor@3.1.0:
     resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
     engines: {node: '>=8'}
@@ -7330,17 +7334,16 @@ packages:
       tslib: 2.5.0
     dev: false
 
-  /react-tooltip@4.5.1(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-Zo+CSFUGXar1uV+bgXFFDe7VeS2iByeIp5rTgTcc2HqtuOS5D76QapejNNfx320MCY91TlhTQat36KGFTqgcvw==}
-    engines: {npm: '>=6.13'}
+  /react-tooltip@5.10.5(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-3bi4UtoPSdaQh0R17B3vMPhNFiATpAbXIV8AqlHqrrIdqo33OJyxuPHtgborw3KXVQ5a6iyyAmCY8ztjUB4CrA==}
     peerDependencies:
-      react: '>=16.0.0'
-      react-dom: '>=16.0.0'
+      react: '>=16.14.0'
+      react-dom: '>=16.14.0'
     dependencies:
-      prop-types: 15.8.1
+      '@floating-ui/dom': 1.2.1
+      classnames: 2.3.2
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
-      uuid: 7.0.3
     dev: false
 
   /react-transition-group@4.4.5(react-dom@18.2.0)(react@18.2.0):
@@ -8424,11 +8427,6 @@ packages:
     engines: {node: '>= 0.4.0'}
     dev: false
 
-  /uuid@7.0.3:
-    resolution: {integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==}
-    hasBin: true
-    dev: false
-
   /uuid@8.3.2:
     resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
     hasBin: true

+ 13 - 5
src/client/components/AppStatus/AppStatus.tsx

@@ -1,10 +1,11 @@
 import clsx from 'clsx';
 import React from 'react';
+import { Tooltip } from 'react-tooltip';
 import * as AppTypes from '../../core/types';
 import styles from './AppStatus.module.scss';
 
 export const AppStatus: React.FC<{ status: AppTypes.AppStatus; lite?: boolean }> = ({ status, lite }) => {
-  const formattedStatus = `${status[0]}${status.substring(1, status.length).toLowerCase()}`;
+  const formattedStatus = `${status[0]?.toUpperCase()}${status.substring(1, status.length).toLowerCase()}`;
 
   const classes = clsx('status-dot status-gray', {
     'status-dot-animated status-green': status === 'running',
@@ -12,9 +13,16 @@ export const AppStatus: React.FC<{ status: AppTypes.AppStatus; lite?: boolean }>
   });
 
   return (
-    <div data-place="top" data-tip={lite && formattedStatus} className="d-flex align-items-center">
-      <span className={classes} />
-      {!lite && <span className={clsx(styles.text, 'ms-2 text-muted')}>{formattedStatus}</span>}
-    </div>
+    <>
+      {lite && (
+        <Tooltip anchorSelect=".appStatus" place="top">
+          {formattedStatus}
+        </Tooltip>
+      )}
+      <div className="appStatus d-flex align-items-center">
+        <span className={classes} />
+        {!lite && <span className={clsx(styles.text, 'ms-2 text-muted')}>{formattedStatus}</span>}
+      </div>
+    </>
   );
 };

+ 7 - 3
src/client/components/AppTile/AppTile.tsx

@@ -1,6 +1,7 @@
 import Link from 'next/link';
 import React from 'react';
 import { IconDownload } from '@tabler/icons-react';
+import { Tooltip } from 'react-tooltip';
 import { AppStatus } from '../AppStatus';
 import { AppLogo } from '../AppLogo/AppLogo';
 import { limitText } from '../../modules/AppStore/helpers/table.helpers';
@@ -30,9 +31,12 @@ export const AppTile: React.FC<{ app: AppTileInfo; status: AppStatusEnum; update
           </div>
         </div>
         {updateAvailable && (
-          <div data-tip="Update available" className="ribbon bg-green ribbon-top">
-            <IconDownload size={20} />
-          </div>
+          <>
+            <Tooltip anchorSelect=".updateAvailable">Update available</Tooltip>
+            <div className="updateAvailable ribbon bg-green ribbon-top">
+              <IconDownload size={20} />
+            </div>
+          </>
         )}
       </Link>
     </div>

+ 0 - 2
src/client/components/Layout/Layout.tsx

@@ -2,7 +2,6 @@ import Head from 'next/head';
 import Link from 'next/link';
 import React, { useEffect } from 'react';
 import clsx from 'clsx';
-import ReactTooltip from 'react-tooltip';
 import semver from 'semver';
 import { useRouter } from 'next/router';
 import { Header } from '../ui/Header';
@@ -61,7 +60,6 @@ export const Layout: React.FC<IProps> = ({ children, breadcrumbs, title, actions
       <Head>
         <title>{`${title} - Tipi`}</title>
       </Head>
-      <ReactTooltip offset={{ right: 1 }} effect="solid" place="bottom" />
       <Header isUpdateAvailable={!isLatest} />
       <div className="page-wrapper">
         <div className="page-header d-print-none">

+ 11 - 12
src/client/components/ui/Header/Header.test.tsx

@@ -5,8 +5,7 @@ import { Header } from './Header';
 
 describe('Header', () => {
   it('renders without crashing', () => {
-    const { container } = render(<Header />);
-    expect(container).toBeInTheDocument();
+    render(<Header />);
   });
 
   it('renders the brand logo', () => {
@@ -16,22 +15,22 @@ describe('Header', () => {
   });
 
   it('renders the dark mode toggle', () => {
-    const { container } = render(<Header />);
-    const darkModeToggle = container.querySelector('[data-tip="Dark mode"]');
+    render(<Header />);
+    const darkModeToggle = screen.getByTestId('dark-mode-toggle');
     expect(darkModeToggle).toContainElement(screen.getByTestId('icon-moon'));
   });
 
   it('renders the light mode toggle', () => {
-    const { container } = render(<Header />);
-    const lightModeToggle = container.querySelector('[data-tip="Light mode"]');
+    render(<Header />);
+    const lightModeToggle = screen.getByTestId('light-mode-toggle');
     expect(lightModeToggle).toContainElement(screen.getByTestId('icon-sun'));
   });
 
   it('Should toggle the dark mode on click of the dark mode toggle', () => {
     const { result } = renderHook(() => useUIStore());
 
-    const { container } = render(<Header />);
-    const darkModeToggle = container.querySelector('[data-tip="Dark mode"]');
+    render(<Header />);
+    const darkModeToggle = screen.getByTestId('dark-mode-toggle');
     fireEvent.click(darkModeToggle as Element);
 
     expect(result.current.darkMode).toBe(true);
@@ -40,8 +39,8 @@ describe('Header', () => {
   it('Should toggle the dark mode on click of the light mode toggle', () => {
     const { result } = renderHook(() => useUIStore());
 
-    const { container } = render(<Header />);
-    const lightModeToggle = container.querySelector('[data-tip="Light mode"]');
+    render(<Header />);
+    const lightModeToggle = screen.getByTestId('light-mode-toggle');
     fireEvent.click(lightModeToggle as Element);
 
     expect(result.current.darkMode).toBe(false);
@@ -49,8 +48,8 @@ describe('Header', () => {
 
   it('Should remove the token from local storage on logout', async () => {
     localStorage.setItem('token', 'token');
-    const { container } = render(<Header />);
-    const logoutButton = container.querySelector('[data-tip="Log out"]');
+    render(<Header />);
+    const logoutButton = screen.getByTestId('logout-button');
     fireEvent.click(logoutButton as Element);
 
     await waitFor(() => {

+ 8 - 4
src/client/components/ui/Header/Header.tsx

@@ -4,6 +4,7 @@ import Image from 'next/image';
 import clsx from 'clsx';
 import Link from 'next/link';
 import { useRouter } from 'next/router';
+import { Tooltip } from 'react-tooltip';
 import { getUrl } from '../../../core/helpers/url-helpers';
 import { useUIStore } from '../../../state/uiStore';
 import { NavBar } from '../NavBar';
@@ -62,14 +63,17 @@ export const Header: React.FC<IProps> = ({ isUpdateAvailable }) => {
               </a>
             </div>
           </div>
-          <div className="d-flex">
-            <div onClick={() => setDarkMode(true)} role="button" aria-hidden="true" className="nav-link px-0 hide-theme-dark cursor-pointer" data-tip="Dark mode">
+          <div style={{ zIndex: 1 }} className="d-flex">
+            <Tooltip anchorSelect=".darkMode">Dark mode</Tooltip>
+            <div onClick={() => setDarkMode(true)} role="button" aria-hidden="true" className="darkMode nav-link px-0 hide-theme-dark cursor-pointer" data-testid="dark-mode-toggle">
               <IconMoon data-testid="icon-moon" size={20} />
             </div>
-            <div onClick={() => setDarkMode(false)} aria-hidden="true" className="nav-link px-0 hide-theme-light cursor-pointer" data-tip="Light mode">
+            <Tooltip anchorSelect=".lightMode">Light mode</Tooltip>
+            <div onClick={() => setDarkMode(false)} aria-hidden="true" className="lightMode nav-link px-0 hide-theme-light cursor-pointer" data-testid="light-mode-toggle">
               <IconSun data-testid="icon-sun" size={20} />
             </div>
-            <div onClick={() => logout.mutate()} tabIndex={0} onKeyPress={() => logout.mutate()} role="button" className="nav-link px-0 cursor-pointer" data-tip="Log out">
+            <Tooltip anchorSelect=".logOut">Log out</Tooltip>
+            <div onClick={() => logout.mutate()} tabIndex={0} onKeyPress={() => logout.mutate()} role="button" className="logOut nav-link px-0 cursor-pointer" data-testid="logout-button">
               <IconLogout size={20} />
             </div>
           </div>

+ 1 - 0
src/pages/_app.tsx

@@ -4,6 +4,7 @@ import type { AppProps } from 'next/app';
 import Head from 'next/head';
 import '../client/styles/global.css';
 import '../client/styles/global.scss';
+import 'react-tooltip/dist/react-tooltip.css';
 import { useUIStore } from '../client/state/uiStore';
 import { ToastProvider } from '../client/components/hoc/ToastProvider';
 import { StatusProvider } from '../client/components/hoc/StatusProvider';