Browse Source

mathjax dynamic (#2632)

* mathjax dynamic
resolves #2621

* test

---------

Co-authored-by: Fred Lefévère-Laoide <Fred.Lefevere-Laoide@Taipy.io>
Fred Lefévère-Laoide 1 day ago
parent
commit
afc424236e

File diff suppressed because it is too large
+ 47 - 598
frontend/taipy-gui/package-lock.json


+ 74 - 54
frontend/taipy-gui/src/components/Router.tsx

@@ -12,13 +12,14 @@
  */
 
 import React, { useEffect, useReducer, useState } from "react";
-import axios from "axios";
 import Box from "@mui/material/Box";
 import CircularProgress from "@mui/material/CircularProgress";
 import CssBaseline from "@mui/material/CssBaseline";
 import { ThemeProvider } from "@mui/system";
 import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
 import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFnsV3";
+import axios from "axios";
+import { MathJaxContext } from "better-react-mathjax";
 import { SnackbarProvider } from "notistack";
 import { HelmetProvider } from "react-helmet-async";
 import { BrowserRouter, Route, Routes } from "react-router";
@@ -34,16 +35,16 @@ import {
     taipyInitialize,
     taipyReducer,
 } from "../context/taipyReducers";
+import { getBaseURL } from "../utils";
+import ErrorFallback from "../utils/ErrorBoundary";
 import UIBlocker from "./Taipy/UIBlocker";
 import Navigate from "./Taipy/Navigate";
 import Menu from "./Taipy/Menu";
 import TaipyNotification from "./Taipy/Notification";
 import GuiDownload from "./Taipy/GuiDownload";
-import ErrorFallback from "../utils/ErrorBoundary";
 import MainPage from "./pages/MainPage";
 import TaipyRendered from "./pages/TaipyRendered";
 import NotFound404 from "./pages/NotFound404";
-import { getBaseURL } from "../utils";
 import { useLocalStorageWithEvent } from "../hooks";
 
 interface AxiosRouter {
@@ -56,6 +57,19 @@ const mainSx = { flexGrow: 1, bgcolor: "background.default" };
 const containerSx = { display: "flex" };
 const progressSx = { position: "fixed", bottom: "1em", right: "1em" };
 const pageStore = {};
+const mathJaxConfig = {
+    tex: {
+        inlineMath: [
+            ["$", "$"],
+            ["\\(", "\\)"],
+        ],
+        displayMath: [
+            ["$$", "$$"],
+            ["\\[", "\\]"],
+        ],
+    },
+    asyncLoad: true,
+};
 
 const Router = () => {
     const [state, dispatch] = useReducer(taipyReducer, INITIAL_STATE, taipyInitialize);
@@ -112,60 +126,66 @@ const Router = () => {
                     <SnackbarProvider maxSnack={5}>
                         <LocalizationProvider dateAdapter={AdapterDateFns}>
                             <PageContext.Provider value={pageStore}>
-                                <BrowserRouter>
-                                    <Box style={containerSx}>
-                                        <CssBaseline />
-                                        <ErrorBoundary FallbackComponent={ErrorFallback}>
-                                            <Menu {...state.menu} />
-                                        </ErrorBoundary>
-                                        <Box component="main" sx={mainSx}>
+                                <MathJaxContext config={mathJaxConfig}>
+                                    <BrowserRouter>
+                                        <Box style={containerSx}>
+                                            <CssBaseline />
                                             <ErrorBoundary FallbackComponent={ErrorFallback}>
-                                                {Object.keys(routes).length ? (
-                                                    <Routes>
-                                                        <Route
-                                                            path={baseURL}
-                                                            element={
-                                                                <MainPage
-                                                                    path={routes["/"]}
-                                                                    route={Object.keys(routes).find(
-                                                                        (path) => path !== "/",
-                                                                    )}
-                                                                />
-                                                            }
-                                                        >
-                                                            {Object.entries(routes)
-                                                                .filter(([path]) => path !== "/")
-                                                                .map(([path, name]) => (
-                                                                    <Route
-                                                                        key={name}
-                                                                        path={path.substring(1)}
-                                                                        element={<TaipyRendered />}
-                                                                    />
-                                                                ))}
-                                                            <Route path="*" key="NotFound" element={<NotFound404 />} />
-                                                        </Route>
-                                                    </Routes>
-                                                ) : null}
+                                                <Menu {...state.menu} />
                                             </ErrorBoundary>
-                                        </Box>
-                                        {state.ackList.length ? (
-                                            <Box sx={progressSx} className="taipy-busy">
-                                                <CircularProgress size="1em" disableShrink />
+                                            <Box component="main" sx={mainSx}>
+                                                <ErrorBoundary FallbackComponent={ErrorFallback}>
+                                                    {Object.keys(routes).length ? (
+                                                        <Routes>
+                                                            <Route
+                                                                path={baseURL}
+                                                                element={
+                                                                    <MainPage
+                                                                        path={routes["/"]}
+                                                                        route={Object.keys(routes).find(
+                                                                            (path) => path !== "/"
+                                                                        )}
+                                                                    />
+                                                                }
+                                                            >
+                                                                {Object.entries(routes)
+                                                                    .filter(([path]) => path !== "/")
+                                                                    .map(([path, name]) => (
+                                                                        <Route
+                                                                            key={name}
+                                                                            path={path.substring(1)}
+                                                                            element={<TaipyRendered />}
+                                                                        />
+                                                                    ))}
+                                                                <Route
+                                                                    path="*"
+                                                                    key="NotFound"
+                                                                    element={<NotFound404 />}
+                                                                />
+                                                            </Route>
+                                                        </Routes>
+                                                    ) : null}
+                                                </ErrorBoundary>
                                             </Box>
-                                        ) : null}
-                                    </Box>
-                                    <ErrorBoundary FallbackComponent={ErrorFallback}>
-                                        <TaipyNotification notifications={state.notifications} />
-                                        <UIBlocker block={state.block} />
-                                        <Navigate
-                                            to={state.navigateTo}
-                                            params={state.navigateParams}
-                                            tab={state.navigateTab}
-                                            force={state.navigateForce}
-                                        />
-                                        <GuiDownload download={state.download} />
-                                    </ErrorBoundary>
-                                </BrowserRouter>
+                                            {state.ackList.length ? (
+                                                <Box sx={progressSx} className="taipy-busy">
+                                                    <CircularProgress size="1em" disableShrink />
+                                                </Box>
+                                            ) : null}
+                                        </Box>
+                                        <ErrorBoundary FallbackComponent={ErrorFallback}>
+                                            <TaipyNotification notifications={state.notifications} />
+                                            <UIBlocker block={state.block} />
+                                            <Navigate
+                                                to={state.navigateTo}
+                                                params={state.navigateParams}
+                                                tab={state.navigateTab}
+                                                force={state.navigateForce}
+                                            />
+                                            <GuiDownload download={state.download} />
+                                        </ErrorBoundary>
+                                    </BrowserRouter>
+                                </MathJaxContext>
                             </PageContext.Provider>
                         </LocalizationProvider>
                     </SnackbarProvider>

+ 20 - 5
frontend/taipy-gui/src/components/Taipy/Field.spec.tsx

@@ -16,7 +16,20 @@ import { render, waitFor } from "@testing-library/react";
 import "@testing-library/jest-dom";
 
 import Field from "./Field";
+import { MathJaxContext } from "better-react-mathjax";
 
+const mathJaxConfig = {
+    tex: {
+        inlineMath: [
+            ["$", "$"],
+            ["\\(", "\\)"],
+        ],
+        displayMath: [
+            ["$$", "$$"],
+            ["\\[", "\\]"],
+        ],
+    },
+};
 describe("Field Component", () => {
     it("renders", async () => {
         const { getByText } = render(<Field value="toto" />);
@@ -74,16 +87,18 @@ describe("Field Component", () => {
     describe("latex mode", () => {
         it("renders LaTeX as block math", async () => {
             const { container, getByText } = render(
-                <Field value={"$$x = y + 1$$"} className="taipy-text" mode="latex" />
+                <MathJaxContext config={mathJaxConfig}>
+                    <Field value={"$$x = y + 1$$"} className="taipy-text" mode="latex" />
+                </MathJaxContext>
             );
-            getByText(/latex/i);
             await waitFor(() => expect(container.querySelector(".taipy-text-latex")).toBeInTheDocument());
         });
         it("renders LaTeX as inline math", async () => {
-            const { container, getByText, findByText } = render(
-                <Field value={"This is inline $x = y + 1$ math."} className="taipy-text" mode="latex" />
+            const { container, findByText } = render(
+                <MathJaxContext config={mathJaxConfig}>
+                    <Field value={"This is inline $x = y + 1$ math."} className="taipy-text" mode="latex" />
+                </MathJaxContext>
             );
-            // getByText(/latex/i); // already loaded ?
             await waitFor(() => expect(container.querySelector(".taipy-text-latex")).toBeInTheDocument());
             expect(await findByText(/inline/i)).toBeInTheDocument();
         });

+ 10 - 30
frontend/taipy-gui/src/components/Taipy/Field.tsx

@@ -14,6 +14,7 @@
 import React, { lazy, useMemo, Suspense } from "react";
 import Typography from "@mui/material/Typography";
 import Tooltip from "@mui/material/Tooltip";
+import { MathJax } from "better-react-mathjax";
 
 import { formatWSValue } from "../../utils";
 import { getSuffixedClassNames } from "./utils";
@@ -34,23 +35,6 @@ interface TaipyFieldProps extends TaipyBaseProps, TaipyHoverProps {
 const unsetWeightSx = { fontWeight: "unset" };
 
 const Markdown = lazy(() => import("react-markdown"));
-const MathJax = lazy(() => import("better-react-mathjax").then((module) => ({ default: module.MathJax })));
-const MathJaxContext = lazy(() =>
-    import("better-react-mathjax").then((module) => ({ default: module.MathJaxContext }))
-);
-
-const mathJaxConfig = {
-    tex: {
-        inlineMath: [
-            ["$", "$"],
-            ["\\(", "\\)"],
-        ],
-        displayMath: [
-            ["$$", "$$"],
-            ["\\[", "\\]"],
-        ],
-    },
-};
 
 const Field = (props: TaipyFieldProps) => {
     const { id, dataType, format, defaultValue, raw } = props;
@@ -117,19 +101,15 @@ const Field = (props: TaipyFieldProps) => {
                         {value}
                     </span>
                 ) : mode == "latex" ? (
-                    <Suspense fallback={<div>Loading LaTex...</div>}>
-                        <MathJaxContext config={mathJaxConfig}>
-                            <MathJax
-                                className={`${className} ${getSuffixedClassNames(
-                                    className,
-                                    "-latex"
-                                )} ${getComponentClassName(props.children)}`}
-                                id={id}
-                            >
-                                {value}
-                            </MathJax>
-                        </MathJaxContext>
-                    </Suspense>
+                    <MathJax
+                        className={`${className} ${getSuffixedClassNames(className, "-latex")} ${getComponentClassName(
+                            props.children
+                        )}`}
+                        id={id}
+                        dynamic={true}
+                    >
+                        {value}
+                    </MathJax>
                 ) : (
                     <Typography
                         className={`${className} ${

Some files were not shown because too many files changed in this diff