Przeglądaj źródła

Merge branch 'master' into fix/update-tag-style

Ryoji Shimizu 3 lat temu
rodzic
commit
abf755c46a

+ 1 - 0
packages/app/package.json

@@ -165,6 +165,7 @@
     "react-multiline-clamp": "^2.0.0",
     "react-scroll": "^1.8.7",
     "react-syntax-highlighter": "^15.5.0",
+    "react-toastify": "^9.1.1",
     "react-use-ripple": "^1.5.2",
     "reactstrap": "^8.9.0",
     "reconnecting-websocket": "^4.4.0",

+ 6 - 52
packages/app/src/client/util/apiNotification.js

@@ -1,53 +1,7 @@
-// show API error/sucess toastr
+import { legacy } from './toastr';
 
-import * as toastr from 'toastr';
-import { toArrayIfNot } from '~/utils/array-utils';
-
-const toastrOption = {
-  error: {
-    closeButton: true,
-    progressBar: true,
-    newestOnTop: false,
-    showDuration: '100',
-    hideDuration: '100',
-    timeOut: '0',
-  },
-  success: {
-    closeButton: true,
-    progressBar: true,
-    newestOnTop: false,
-    showDuration: '100',
-    hideDuration: '100',
-    timeOut: '3000',
-  },
-  warning: {
-    closeButton: true,
-    progressBar: true,
-    newestOnTop: false,
-    showDuration: '100',
-    hideDuration: '100',
-    timeOut: '6000',
-  },
-};
-
-// accepts both a single error and an array of errors
-export const toastError = (err, header = 'Error', option = toastrOption.error) => {
-  const errs = toArrayIfNot(err);
-
-  if (err.length === 0) {
-    toastr.error('', header);
-  }
-
-  for (const err of errs) {
-    toastr.error(err.message || err, header, option);
-  }
-};
-
-// only accepts a single item
-export const toastSuccess = (body, header = 'Success', option = toastrOption.success) => {
-  toastr.success(body, header, option);
-};
-
-export const toastWarning = (body, header = 'Warning', option = toastrOption.warning) => {
-  toastr.warning(body, header, option);
-};
+// DEPRECATED -- 2022.12.07 Yuki Takei
+// Use methods from './toastr.ts' instead
+export const toastError = legacy.toastError;
+export const toastSuccess = legacy.toastSuccess;
+export const toastWarning = legacy.toastWarning;

+ 91 - 0
packages/app/src/client/util/toastr.ts

@@ -0,0 +1,91 @@
+import { toast, ToastContent, ToastOptions } from 'react-toastify';
+import * as toastrLegacy from 'toastr';
+
+import { toArrayIfNot } from '~/utils/array-utils';
+
+
+export const toastErrorOption: ToastOptions = {
+  autoClose: 0,
+  closeButton: true,
+};
+export const toastError = (err: string | Error | Error[], option: ToastOptions = toastErrorOption): void => {
+  const errs = toArrayIfNot(err);
+
+  if (errs.length === 0) {
+    return;
+  }
+
+  for (const err of errs) {
+    const message = (typeof err === 'string') ? err : err.message;
+    toast.error(message || err, option);
+  }
+};
+
+export const toastSuccessOption: ToastOptions = {
+  autoClose: 2000,
+  closeButton: true,
+};
+export const toastSuccess = (content: ToastContent, option: ToastOptions = toastSuccessOption): void => {
+  toast.success(content, option);
+};
+
+export const toastWarningOption: ToastOptions = {
+  autoClose: 5000,
+  closeButton: true,
+};
+export const toastWarning = (content: ToastContent, option: ToastOptions = toastWarningOption): void => {
+  toastrLegacy.warning(content, option);
+};
+
+
+const toastrLegacyOption = {
+  error: {
+    closeButton: true,
+    progressBar: true,
+    newestOnTop: false,
+    showDuration: '100',
+    hideDuration: '100',
+    timeOut: '0',
+  },
+  success: {
+    closeButton: true,
+    progressBar: true,
+    newestOnTop: false,
+    showDuration: '100',
+    hideDuration: '100',
+    timeOut: '3000',
+  },
+  warning: {
+    closeButton: true,
+    progressBar: true,
+    newestOnTop: false,
+    showDuration: '100',
+    hideDuration: '100',
+    timeOut: '6000',
+  },
+};
+
+export const legacy = {
+  // accepts both a single error and an array of errors
+  toastError: (err: string | Error | Error[], header = 'Error', option = toastrLegacyOption.error): void => {
+    const errs = toArrayIfNot(err);
+
+    if (errs.length === 0) {
+      toastrLegacy.error('', header);
+    }
+
+    for (const err of errs) {
+      const message = (typeof err === 'string') ? err : err.message;
+      toastrLegacy.error(message || err, header, option);
+    }
+  },
+
+  // only accepts a single item
+  toastSuccess: (body: string, header = 'Success', option = toastrLegacyOption.success): void => {
+    toastrLegacy.success(body, header, option);
+  },
+
+  toastWarning: (body: string, header = 'Warning', option = toastrLegacyOption.warning): void => {
+    toastrLegacy.warning(body, header, option);
+  },
+};

+ 4 - 1
packages/app/src/components/Admin/ElasticsearchManagement/ElasticsearchManagement.tsx

@@ -54,9 +54,12 @@ const ElasticsearchManagement = () => {
             setIsConfigured(false);
           }
         }
+        toastError(errors as Error[]);
+      }
+      else {
+        toastError(errors as Error);
       }
 
-      toastError(errors);
     }
     finally {
       setIsInitialized(true);

+ 2 - 0
packages/app/src/components/Layout/RawLayout.tsx

@@ -1,6 +1,7 @@
 import React, { ReactNode, useState } from 'react';
 
 import Head from 'next/head';
+import { ToastContainer } from 'react-toastify';
 import { useIsomorphicLayoutEffect } from 'usehooks-ts';
 
 import { useGrowiTheme } from '~/stores/context';
@@ -48,6 +49,7 @@ export const RawLayout = ({ children, title, className }: Props): JSX.Element =>
         <GrowiThemeProvider theme={growiTheme} colorScheme={colorScheme}>
           <div className={classNames.join(' ')} data-color-scheme={colorScheme}>
             {children}
+            <ToastContainer theme={colorScheme} />
           </div>
         </GrowiThemeProvider>
       </NextThemesProvider>

+ 2 - 1
packages/app/src/components/PageEditor.tsx

@@ -2,6 +2,7 @@ import React, {
   useCallback, useEffect, useMemo, useRef, useState,
 } from 'react';
 
+
 import EventEmitter from 'events';
 
 import {
@@ -13,8 +14,8 @@ import { useRouter } from 'next/router';
 import { throttle, debounce } from 'throttle-debounce';
 
 import { useSaveOrUpdate } from '~/client/services/page-operation';
-import { toastSuccess, toastError } from '~/client/util/apiNotification';
 import { apiGet, apiPostForm } from '~/client/util/apiv1-client';
+import { toastError, toastSuccess } from '~/client/util/toastr';
 import { IEditorMethods } from '~/interfaces/editor-methods';
 import { OptionsToSave } from '~/interfaces/page-operation';
 import { SocketEventName } from '~/interfaces/websocket';

+ 9 - 3
packages/app/src/styles/_editor.scss

@@ -69,9 +69,6 @@
     display: none;
   }
 
-  .toast-top-right {
-    top: 64px;
-  }
 
   /*****************
    * Expand Editor
@@ -276,6 +273,15 @@
       vertical-align: unset;
     }
   }
+
+
+  /*****************
+   *     Toastr
+   *****************/
+  .Toastify .Toastify__toast-container {
+    top: 4.5em;
+  }
+
 }
 
 .layout-root.editing {

+ 1 - 0
packages/app/src/styles/molecules/toastr.scss

@@ -1,3 +1,4 @@
 :root {
   @import '~toastr/build/toastr';
 }
+@import '~react-toastify/scss/main';

+ 1 - 1
packages/app/src/utils/array-utils.ts

@@ -1,6 +1,6 @@
 // converts non-array item to array
 
-export const toArrayIfNot = <T = unknown>(item?: T): T[] => {
+export const toArrayIfNot = <T = unknown>(item?: T | T[]): T[] => {
   if (item == null) {
     return [];
   }

+ 7 - 6
packages/app/test/cypress/integration/30-search/search.spec.ts

@@ -202,6 +202,7 @@ context('Search all pages', () => {
 
   });
 
+  // TODO: fix this VRT
   it('Successfully order page search results by tag', () => {
     const tag = 'help';
 
@@ -225,8 +226,8 @@ context('Search all pages', () => {
     cy.getByTestid('search-result-base').should('be.visible');
     cy.getByTestid('search-result-list').should('be.visible');
     cy.getByTestid('search-result-content').should('be.visible');
-    cy.get('.wiki').should('be.visible');
-    cy.screenshot(`${ssPrefix}2-tag-order-by-relevance`);
+    // cy.get('.wiki').should('be.visible');
+    // cy.screenshot(`${ssPrefix}2-tag-order-by-relevance`);
 
     cy.get('.grw-search-page-nav').within(() => {
       cy.get('button.dropdown-toggle').first().click({force: true});
@@ -236,8 +237,8 @@ context('Search all pages', () => {
     cy.getByTestid('search-result-base').should('be.visible');
     cy.getByTestid('search-result-list').should('be.visible');
     cy.getByTestid('search-result-content').should('be.visible');
-    cy.get('.wiki').should('be.visible');
-    cy.screenshot(`${ssPrefix}3-tag-order-by-creation-date`);
+    // cy.get('.wiki').should('be.visible');
+    // cy.screenshot(`${ssPrefix}3-tag-order-by-creation-date`);
 
     cy.get('.grw-search-page-nav').within(() => {
       cy.get('button.dropdown-toggle').first().click({force: true});
@@ -247,10 +248,10 @@ context('Search all pages', () => {
     cy.getByTestid('search-result-base').should('be.visible');
     cy.getByTestid('search-result-list').should('be.visible');
     cy.getByTestid('search-result-content').should('be.visible');
-    cy.get('.wiki').should('be.visible');
+    // cy.get('.wiki').should('be.visible');
     cy.waitUntilSpinnerDisappear();
 
-    cy.screenshot(`${ssPrefix}4-tag-order-by-last-update-date`);
+    // cy.screenshot(`${ssPrefix}4-tag-order-by-last-update-date`);
   });
 
 });

+ 12 - 0
yarn.lock

@@ -6987,6 +6987,11 @@ clsx@^1.0.4:
   resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.0.4.tgz#0c0171f6d5cb2fe83848463c15fcc26b4df8c2ec"
   integrity sha512-1mQ557MIZTrL/140j+JVdRM6e31/OA4vTYxXgqIIZlndyfjHpyawKZia1Im05Vp9BWmImkcNrNtFYQMyFcgJDg==
 
+clsx@^1.1.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
+  integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
+
 cmd-shim@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-4.1.0.tgz#b3a904a6743e9fede4148c6f3800bf2a08135bdd"
@@ -18648,6 +18653,13 @@ react-syntax-highlighter@^15.5.0:
     prismjs "^1.27.0"
     refractor "^3.6.0"
 
+react-toastify@^9.1.1:
+  version "9.1.1"
+  resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-9.1.1.tgz#9280caea4a13dc1739c350d90660a630807bf10b"
+  integrity sha512-pkFCla1z3ve045qvjEmn2xOJOy4ZciwRXm1oMPULVkELi5aJdHCN/FHnuqXq8IwGDLB7PPk2/J6uP9D8ejuiRw==
+  dependencies:
+    clsx "^1.1.1"
+
 react-transition-group@^2.3.1:
   version "2.9.0"
   resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d"