1
0
Эх сурвалжийг харах

Build/#2581 upgrade to mui 7 (#2653)

* upgrade to Mui 7
WiP

* fix gui fe tests

* start work on taipy fe
WiP

* fix typo that became an issue

* data-selectable

* rebase

* mui issue

* spell check

* rebase : remove base

* depends on 4.2

* improve coverage

---------

Co-authored-by: Fred Lefévère-Laoide <Fred.Lefevere-Laoide@Taipy.io>
Fred Lefévère-Laoide 1 өдөр өмнө
parent
commit
260af4cf41
42 өөрчлөгдсөн 818 нэмэгдсэн , 1317 устгасан
  1. 2 2
      doc/gui/examples/controls/chat_discuss.py
  2. 3 4
      frontend/taipy-gui/dom/package-lock.json
  3. 1 1
      frontend/taipy-gui/dom/package.json
  4. 3 1
      frontend/taipy-gui/jest.config.js
  5. 107 295
      frontend/taipy-gui/package-lock.json
  6. 7 8
      frontend/taipy-gui/package.json
  7. 1 1
      frontend/taipy-gui/packaging/package.json
  8. 1 1
      frontend/taipy-gui/src/components/Router.tsx
  9. 1 1
      frontend/taipy-gui/src/components/Taipy/Chat.tsx
  10. 121 74
      frontend/taipy-gui/src/components/Taipy/DateRange.spec.tsx
  11. 5 5
      frontend/taipy-gui/src/components/Taipy/DateRange.tsx
  12. 52 37
      frontend/taipy-gui/src/components/Taipy/DateSelector.spec.tsx
  13. 5 5
      frontend/taipy-gui/src/components/Taipy/DateSelector.tsx
  14. 10 10
      frontend/taipy-gui/src/components/Taipy/Field.tsx
  15. 9 2
      frontend/taipy-gui/src/components/Taipy/TableFilter.spec.tsx
  16. 2 2
      frontend/taipy-gui/src/components/Taipy/TableFilter.tsx
  17. 1 1
      frontend/taipy-gui/src/components/Taipy/TableSort.tsx
  18. 166 167
      frontend/taipy-gui/src/components/Taipy/TimeSelector.spec.tsx
  19. 9 9
      frontend/taipy-gui/src/components/Taipy/TreeView.spec.tsx
  20. 92 93
      frontend/taipy-gui/src/components/Taipy/TreeView.tsx
  21. 2 2
      frontend/taipy-gui/src/components/Taipy/tableUtils.tsx
  22. 1 0
      frontend/taipy-gui/src/utils/image.ts
  23. 11 11
      frontend/taipy-gui/src/workers/fileupload.worker.ts
  24. 18 0
      frontend/taipy-gui/test-config/errorBoundary.js
  25. 7 0
      frontend/taipy-gui/test-config/errorBoundary.tsx
  26. 1 3
      frontend/taipy-gui/test-config/markdown.tsx
  27. 1 3
      frontend/taipy-gui/test-config/nanoid.js
  28. 5 0
      frontend/taipy-gui/webpack.config.js
  29. 95 554
      frontend/taipy/package-lock.json
  30. 8 5
      frontend/taipy/package.json
  31. 6 6
      frontend/taipy/src/CoreSelector.tsx
  32. 1 1
      frontend/taipy/src/DataNodeChart.tsx
  33. 1 1
      frontend/taipy/src/DataNodeTable.tsx
  34. 4 4
      frontend/taipy/src/DataNodeViewer.tsx
  35. 2 2
      frontend/taipy/src/JobSelector.tsx
  36. 1 1
      frontend/taipy/src/JobViewer.tsx
  37. 1 1
      frontend/taipy/src/PropertiesEditor.tsx
  38. 2 2
      frontend/taipy/src/ScenarioSelector.tsx
  39. 1 1
      frontend/taipy/src/ScenarioViewer.tsx
  40. 1 1
      frontend/taipy/src/utils/ConfirmDialog.tsx
  41. 4 0
      taipy/gui/utils/datatype.py
  42. 47 0
      tests/gui/utils/test_datatype.py

+ 2 - 2
doc/gui/examples/controls/chat_discuss.py

@@ -19,7 +19,7 @@
 # incognito windows so a given user's context is not reused.
 # -----------------------------------------------------------------------------------------
 from os import path
-from typing import Optional, Union
+from typing import Optional, Union, cast
 
 from taipy.gui import Gui, Icon
 from taipy.gui.gui_actions import navigate, notify
@@ -63,7 +63,7 @@ def register(state):
 
 def send(state, _: str, payload: dict):
     (_, _, message, sender_id, image_url) = payload.get("args", [])
-    messages.append((f"{len(messages)}", message, sender_id, image_url))
+    messages.append((f"{len(messages)}", cast(str, message), cast(str, sender_id), cast(str, image_url)))
     state.messages = messages
 
 

+ 3 - 4
frontend/taipy-gui/dom/package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "taipy-gui-dom",
-  "version": "4.1.0",
+  "version": "4.2.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "taipy-gui-dom",
-      "version": "4.1.0",
+      "version": "4.2.0",
       "dependencies": {
         "react": "^18.2.0",
         "react-dom": "^18.2.0",
@@ -14,8 +14,7 @@
       }
     },
     "../packaging": {
-      "name": "taipy-gui",
-      "version": "4.1.0"
+      "version": "4.2.0"
     },
     "node_modules/js-tokens": {
       "version": "4.0.0",

+ 1 - 1
frontend/taipy-gui/dom/package.json

@@ -1,6 +1,6 @@
 {
   "name": "taipy-gui-dom",
-  "version": "4.1.0",
+  "version": "4.2.0",
   "private": true,
   "dependencies": {
     "react": "^18.2.0",

+ 3 - 1
frontend/taipy-gui/jest.config.js

@@ -27,7 +27,9 @@ module.exports = {
     ],
     coverageReporters: ["json", "html", "text"],
     modulePathIgnorePatterns: ["<rootDir>/packaging/"],
-    moduleNameMapper: {"react-markdown": "<rootDir>/test-config/markdown.tsx"},
+    moduleNameMapper: {"react-markdown": "<rootDir>/test-config/markdown.tsx",
+        "react-error-boundary": "<rootDir>/test-config/errorBoundary.tsx"
+    },
     transformIgnorePatterns: ["<rootDir>/node_modules/(?!react-jsx-parser/)"],
     ...createJsWithTsPreset()
 };

+ 107 - 295
frontend/taipy-gui/package-lock.json

@@ -1,19 +1,19 @@
 {
   "name": "taipy-gui",
-  "version": "4.1.0",
+  "version": "4.2.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "taipy-gui",
-      "version": "4.1.0",
+      "version": "4.2.0",
       "dependencies": {
         "@emotion/react": "^11.10.0",
         "@emotion/styled": "^11.10.0",
-        "@mui/icons-material": "^6.0.1",
-        "@mui/material": "^6.0.1",
-        "@mui/x-date-pickers": "^7.0.0",
-        "@mui/x-tree-view": "^7.0.0",
+        "@mui/icons-material": "^7.1.0",
+        "@mui/material": "^7.1.0",
+        "@mui/x-date-pickers": "^8.3.0",
+        "@mui/x-tree-view": "^8.3.0",
         "apache-arrow": "^16.1.0",
         "axios": "^1.2.0",
         "better-react-mathjax": "^2.0.3",
@@ -25,10 +25,10 @@
         "plotly.js": "^3.0.0",
         "react": "^18.3.1",
         "react-dom": "^18.3.1",
-        "react-error-boundary": "^5.0.0",
+        "react-error-boundary": "^6.0.0",
         "react-helmet-async": "^2.0.1",
         "react-jsx-parser": "^2.1.0",
-        "react-markdown": "^9.0.1",
+        "react-markdown": "^10.1.0",
         "react-plotly.js": "^2.5.1",
         "react-router": "^7.1.1",
         "react-virtualized-auto-sizer": "^1.0.6",
@@ -51,7 +51,6 @@
         "@types/react-dom": "^18.3.5",
         "@types/react-plotly.js": "^2.5.0",
         "@types/react-router": "^5.1.20",
-        "@types/react-virtualized-auto-sizer": "^1.0.1",
         "@types/react-window": "^1.8.5",
         "@types/react-window-infinite-loader": "^1.0.5",
         "@types/sprintf-js": "^1.1.2",
@@ -1668,20 +1667,20 @@
       "dev": true
     },
     "node_modules/@mui/core-downloads-tracker": {
-      "version": "6.4.11",
-      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.11.tgz",
-      "integrity": "sha512-CzAQs9CTzlwbsF9ZYB4o4lLwBv1/qNE264NjuYao+ctAXsmlPtYa8RtER4UsUXSMxNN9Qi+aQdYcKl2sUpnmAw==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.1.0.tgz",
+      "integrity": "sha512-E0OqhZv548Qdc0PwWhLVA2zmjJZSTvaL4ZhoswmI8NJEC1tpW2js6LLP827jrW9MEiXYdz3QS6+hask83w74yQ==",
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/mui-org"
       }
     },
     "node_modules/@mui/icons-material": {
-      "version": "6.4.11",
-      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.11.tgz",
-      "integrity": "sha512-+jjJGIrB1awNbMv4ZVPPdN/p7O1UKFZ+xqRvNIQ8B1KnlID5hPMPBLM6UUbRF4bu3UDCbu79rn9Nye5LGNzmeA==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.1.0.tgz",
+      "integrity": "sha512-1mUPMAZ+Qk3jfgL5ftRR06ATH/Esi0izHl1z56H+df6cwIlCWG66RXciUqeJCttbOXOQ5y2DCjLZI/4t3Yg3LA==",
       "dependencies": {
-        "@babel/runtime": "^7.26.0"
+        "@babel/runtime": "^7.27.1"
       },
       "engines": {
         "node": ">=14.0.0"
@@ -1691,7 +1690,7 @@
         "url": "https://opencollective.com/mui-org"
       },
       "peerDependencies": {
-        "@mui/material": "^6.4.11",
+        "@mui/material": "^7.1.0",
         "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
         "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
       },
@@ -1702,21 +1701,21 @@
       }
     },
     "node_modules/@mui/material": {
-      "version": "6.4.11",
-      "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.11.tgz",
-      "integrity": "sha512-k2D3FLJS+/qD0qnd6ZlAjGFvaaxe1Dl10NyvpeDzIebMuYdn8VqYe6XBgGueEAtnzSJM4V03VD9kb5Fi24dnTA==",
-      "dependencies": {
-        "@babel/runtime": "^7.26.0",
-        "@mui/core-downloads-tracker": "^6.4.11",
-        "@mui/system": "^6.4.11",
-        "@mui/types": "~7.2.24",
-        "@mui/utils": "^6.4.9",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.1.0.tgz",
+      "integrity": "sha512-ahUJdrhEv+mCp4XHW+tHIEYzZMSRLg8z4AjUOsj44QpD1ZaMxQoVOG2xiHvLFdcsIPbgSRx1bg1eQSheHBgvtg==",
+      "dependencies": {
+        "@babel/runtime": "^7.27.1",
+        "@mui/core-downloads-tracker": "^7.1.0",
+        "@mui/system": "^7.1.0",
+        "@mui/types": "^7.4.2",
+        "@mui/utils": "^7.1.0",
         "@popperjs/core": "^2.11.8",
         "@types/react-transition-group": "^4.4.12",
         "clsx": "^2.1.1",
         "csstype": "^3.1.3",
         "prop-types": "^15.8.1",
-        "react-is": "^19.0.0",
+        "react-is": "^19.1.0",
         "react-transition-group": "^4.4.5"
       },
       "engines": {
@@ -1729,7 +1728,7 @@
       "peerDependencies": {
         "@emotion/react": "^11.5.0",
         "@emotion/styled": "^11.3.0",
-        "@mui/material-pigment-css": "^6.4.11",
+        "@mui/material-pigment-css": "^7.1.0",
         "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
         "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
         "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
@@ -1749,109 +1748,10 @@
         }
       }
     },
-    "node_modules/@mui/material/node_modules/@mui/private-theming": {
-      "version": "6.4.9",
-      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.9.tgz",
-      "integrity": "sha512-LktcVmI5X17/Q5SkwjCcdOLBzt1hXuc14jYa7NPShog0GBDCDvKtcnP0V7a2s6EiVRlv7BzbWEJzH6+l/zaCxw==",
-      "dependencies": {
-        "@babel/runtime": "^7.26.0",
-        "@mui/utils": "^6.4.9",
-        "prop-types": "^15.8.1"
-      },
-      "engines": {
-        "node": ">=14.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/mui-org"
-      },
-      "peerDependencies": {
-        "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
-        "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
-      },
-      "peerDependenciesMeta": {
-        "@types/react": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@mui/material/node_modules/@mui/styled-engine": {
-      "version": "6.4.11",
-      "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.11.tgz",
-      "integrity": "sha512-74AUmlHXaGNbyUqdK/+NwDJOZqgRQw6BcNvhoWYLq3LGbLTkE+khaJ7soz6cIabE4CPYqO2/QAIU1Z/HEjjpcw==",
-      "dependencies": {
-        "@babel/runtime": "^7.26.0",
-        "@emotion/cache": "^11.13.5",
-        "@emotion/serialize": "^1.3.3",
-        "@emotion/sheet": "^1.4.0",
-        "csstype": "^3.1.3",
-        "prop-types": "^15.8.1"
-      },
-      "engines": {
-        "node": ">=14.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/mui-org"
-      },
-      "peerDependencies": {
-        "@emotion/react": "^11.4.1",
-        "@emotion/styled": "^11.3.0",
-        "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
-      },
-      "peerDependenciesMeta": {
-        "@emotion/react": {
-          "optional": true
-        },
-        "@emotion/styled": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@mui/material/node_modules/@mui/system": {
-      "version": "6.4.11",
-      "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.11.tgz",
-      "integrity": "sha512-gibtsrZEwnDaT5+I/KloOj/yHluX5G8heknuxBpQOdEQ3Gc0avjSImn5hSeKp8D4thiwZiApuggIjZw1dQguUA==",
-      "dependencies": {
-        "@babel/runtime": "^7.26.0",
-        "@mui/private-theming": "^6.4.9",
-        "@mui/styled-engine": "^6.4.11",
-        "@mui/types": "~7.2.24",
-        "@mui/utils": "^6.4.9",
-        "clsx": "^2.1.1",
-        "csstype": "^3.1.3",
-        "prop-types": "^15.8.1"
-      },
-      "engines": {
-        "node": ">=14.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/mui-org"
-      },
-      "peerDependencies": {
-        "@emotion/react": "^11.5.0",
-        "@emotion/styled": "^11.3.0",
-        "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
-        "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
-      },
-      "peerDependenciesMeta": {
-        "@emotion/react": {
-          "optional": true
-        },
-        "@emotion/styled": {
-          "optional": true
-        },
-        "@types/react": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/@mui/private-theming": {
       "version": "7.1.0",
       "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.1.0.tgz",
       "integrity": "sha512-4Kck4jxhqF6YxNwJdSae1WgDfXVg0lIH6JVJ7gtuFfuKcQCgomJxPvUEOySTFRPz1IZzwz5OAcToskRdffElDA==",
-      "peer": true,
       "dependencies": {
         "@babel/runtime": "^7.27.1",
         "@mui/utils": "^7.1.0",
@@ -1874,58 +1774,10 @@
         }
       }
     },
-    "node_modules/@mui/private-theming/node_modules/@mui/types": {
-      "version": "7.4.2",
-      "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.2.tgz",
-      "integrity": "sha512-edRc5JcLPsrlNFYyTPxds+d5oUovuUxnnDtpJUbP6WMeV4+6eaX/mqai1ZIWT62lCOe0nlrON0s9HDiv5en5bA==",
-      "peer": true,
-      "dependencies": {
-        "@babel/runtime": "^7.27.1"
-      },
-      "peerDependencies": {
-        "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
-      },
-      "peerDependenciesMeta": {
-        "@types/react": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@mui/private-theming/node_modules/@mui/utils": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.1.0.tgz",
-      "integrity": "sha512-/OM3S8kSHHmWNOP+NH9xEtpYSG10upXeQ0wLZnfDgmgadTAk5F4MQfFLyZ5FCRJENB3eRzltMmaNl6UtDnPovw==",
-      "peer": true,
-      "dependencies": {
-        "@babel/runtime": "^7.27.1",
-        "@mui/types": "^7.4.2",
-        "@types/prop-types": "^15.7.14",
-        "clsx": "^2.1.1",
-        "prop-types": "^15.8.1",
-        "react-is": "^19.1.0"
-      },
-      "engines": {
-        "node": ">=14.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/mui-org"
-      },
-      "peerDependencies": {
-        "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
-        "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
-      },
-      "peerDependenciesMeta": {
-        "@types/react": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/@mui/styled-engine": {
       "version": "7.1.0",
       "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.1.0.tgz",
       "integrity": "sha512-m0mJ0c6iRC+f9hMeRe0W7zZX1wme3oUX0+XTVHjPG7DJz6OdQ6K/ggEOq7ZdwilcpdsDUwwMfOmvO71qDkYd2w==",
-      "peer": true,
       "dependencies": {
         "@babel/runtime": "^7.27.1",
         "@emotion/cache": "^11.13.5",
@@ -1959,7 +1811,6 @@
       "version": "7.1.0",
       "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.1.0.tgz",
       "integrity": "sha512-iedAWgRJMCxeMHvkEhsDlbvkK+qKf9me6ofsf7twk/jfT4P1ImVf7Rwb5VubEA0sikrVL+1SkoZM41M4+LNAVA==",
-      "peer": true,
       "dependencies": {
         "@babel/runtime": "^7.27.1",
         "@mui/private-theming": "^7.1.0",
@@ -1995,11 +1846,10 @@
         }
       }
     },
-    "node_modules/@mui/system/node_modules/@mui/types": {
+    "node_modules/@mui/types": {
       "version": "7.4.2",
       "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.2.tgz",
       "integrity": "sha512-edRc5JcLPsrlNFYyTPxds+d5oUovuUxnnDtpJUbP6WMeV4+6eaX/mqai1ZIWT62lCOe0nlrON0s9HDiv5en5bA==",
-      "peer": true,
       "dependencies": {
         "@babel/runtime": "^7.27.1"
       },
@@ -2012,11 +1862,10 @@
         }
       }
     },
-    "node_modules/@mui/system/node_modules/@mui/utils": {
+    "node_modules/@mui/utils": {
       "version": "7.1.0",
       "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.1.0.tgz",
       "integrity": "sha512-/OM3S8kSHHmWNOP+NH9xEtpYSG10upXeQ0wLZnfDgmgadTAk5F4MQfFLyZ5FCRJENB3eRzltMmaNl6UtDnPovw==",
-      "peer": true,
       "dependencies": {
         "@babel/runtime": "^7.27.1",
         "@mui/types": "^7.4.2",
@@ -2042,57 +1891,15 @@
         }
       }
     },
-    "node_modules/@mui/types": {
-      "version": "7.2.24",
-      "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz",
-      "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==",
-      "peerDependencies": {
-        "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
-      },
-      "peerDependenciesMeta": {
-        "@types/react": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@mui/utils": {
-      "version": "6.4.9",
-      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.9.tgz",
-      "integrity": "sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg==",
-      "dependencies": {
-        "@babel/runtime": "^7.26.0",
-        "@mui/types": "~7.2.24",
-        "@types/prop-types": "^15.7.14",
-        "clsx": "^2.1.1",
-        "prop-types": "^15.8.1",
-        "react-is": "^19.0.0"
-      },
-      "engines": {
-        "node": ">=14.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/mui-org"
-      },
-      "peerDependencies": {
-        "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
-        "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
-      },
-      "peerDependenciesMeta": {
-        "@types/react": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/@mui/x-date-pickers": {
-      "version": "7.29.3",
-      "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.29.3.tgz",
-      "integrity": "sha512-/A0/8fpLnEFeJKr5YQsI8jqlWPJlOtgfCGcqXHVDOLxgV3lW49+Kh5TZAc1yi6HKT3AG6k4DkNwTuu/RjJeMFA==",
-      "dependencies": {
-        "@babel/runtime": "^7.25.7",
-        "@mui/utils": "^5.16.6 || ^6.0.0 || ^7.0.0",
-        "@mui/x-internals": "7.29.0",
-        "@types/react-transition-group": "^4.4.11",
+      "version": "8.4.0",
+      "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-8.4.0.tgz",
+      "integrity": "sha512-x7jI7JnKK25xL3yjD2Z1r86gAWtabKj9ogI2WDKd/v9WwE1VxmDD/NTiXprEZFo9psPOoqr+juPGDz5Cb2v7jw==",
+      "dependencies": {
+        "@babel/runtime": "^7.27.1",
+        "@mui/utils": "^7.0.2",
+        "@mui/x-internals": "8.4.0",
+        "@types/react-transition-group": "^4.4.12",
         "clsx": "^2.1.1",
         "prop-types": "^15.8.1",
         "react-transition-group": "^4.4.5"
@@ -2150,12 +1957,12 @@
       }
     },
     "node_modules/@mui/x-internals": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.29.0.tgz",
-      "integrity": "sha512-+Gk6VTZIFD70XreWvdXBwKd8GZ2FlSCuecQFzm6znwqXg1ZsndavrhG9tkxpxo2fM1Zf7Tk8+HcOO0hCbhTQFA==",
+      "version": "8.4.0",
+      "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.4.0.tgz",
+      "integrity": "sha512-Z7FCahC4MLfTVzEwnKOB7P1fiR9DzFuMzHOPRNaMXc/rsS7unbtBKAG94yvsRzReCyjzZUVA7h37lnQ1DoPKJw==",
       "dependencies": {
-        "@babel/runtime": "^7.25.7",
-        "@mui/utils": "^5.16.6 || ^6.0.0 || ^7.0.0"
+        "@babel/runtime": "^7.27.1",
+        "@mui/utils": "^7.0.2"
       },
       "engines": {
         "node": ">=14.0.0"
@@ -2169,17 +1976,19 @@
       }
     },
     "node_modules/@mui/x-tree-view": {
-      "version": "7.29.1",
-      "resolved": "https://registry.npmjs.org/@mui/x-tree-view/-/x-tree-view-7.29.1.tgz",
-      "integrity": "sha512-hjfgDVxiuRr5BYKEI2bemkqMaWbh/YIVRJ01OxEU5An2hL5DKAA/Ziv6UV9jse3nTXJwOGkZ3uj0ofoxb9iznQ==",
-      "dependencies": {
-        "@babel/runtime": "^7.25.7",
-        "@mui/utils": "^5.16.6 || ^6.0.0 || ^7.0.0",
-        "@mui/x-internals": "7.29.0",
-        "@types/react-transition-group": "^4.4.11",
+      "version": "8.4.0",
+      "resolved": "https://registry.npmjs.org/@mui/x-tree-view/-/x-tree-view-8.4.0.tgz",
+      "integrity": "sha512-TsweCJ2fazVIvVdNq7KuY7BEInPMPeWCCxG4XxHyoN3GdSa/8a+S4dH6iww3yaYE5nBXZQV3wZm940W4eV1uYw==",
+      "dependencies": {
+        "@babel/runtime": "^7.27.1",
+        "@mui/utils": "^7.0.2",
+        "@mui/x-internals": "8.4.0",
+        "@types/react-transition-group": "^4.4.12",
         "clsx": "^2.1.1",
         "prop-types": "^15.8.1",
-        "react-transition-group": "^4.4.5"
+        "react-transition-group": "^4.4.5",
+        "reselect": "^5.1.1",
+        "use-sync-external-store": "^1.5.0"
       },
       "engines": {
         "node": ">=14.0.0"
@@ -3103,9 +2912,9 @@
       "integrity": "sha512-Gjm4+H9noDJgu5EdT3rUw5MhPBag46fiOy27BefvWkNL8mlZnKnCaVVVTLKj6RYXed9b62CPKnPav9govyQDzA=="
     },
     "node_modules/@types/lodash": {
-      "version": "4.17.16",
-      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz",
-      "integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==",
+      "version": "4.17.17",
+      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.17.tgz",
+      "integrity": "sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==",
       "dev": true
     },
     "node_modules/@types/mapbox__point-geometry": {
@@ -3137,9 +2946,9 @@
       "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="
     },
     "node_modules/@types/node": {
-      "version": "20.17.48",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.48.tgz",
-      "integrity": "sha512-KpSfKOHPsiSC4IkZeu2LsusFwExAIVGkhG1KkbaBMLwau0uMhj0fCrvyg9ddM2sAvd+gtiBJLir4LAw1MNMIaw==",
+      "version": "20.17.50",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.50.tgz",
+      "integrity": "sha512-Mxiq0ULv/zo1OzOhwPqOA13I81CV/W3nvd3ChtQZRT5Cwz3cr0FKo/wMSsbTqL3EXpaBAEQhva2B8ByRkOIh9A==",
       "dependencies": {
         "undici-types": "~6.19.2"
       }
@@ -3166,9 +2975,9 @@
       "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ=="
     },
     "node_modules/@types/react": {
-      "version": "18.3.21",
-      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.21.tgz",
-      "integrity": "sha512-gXLBtmlcRJeT09/sI4PxVwyrku6SaNUj/6cMubjE6T6XdY1fDmBL7r0nX0jbSZPU/Xr0KuwLLZh6aOYY5d91Xw==",
+      "version": "18.3.22",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.22.tgz",
+      "integrity": "sha512-vUhG0YmQZ7kL/tmKLrD3g5zXbXXreZXB3pmROW8bg3CnLnpjkRVwUlLne7Ufa2r9yJ8+/6B73RzhAek5TBKh2Q==",
       "dependencies": {
         "@types/prop-types": "*",
         "csstype": "^3.0.2"
@@ -3211,16 +3020,6 @@
         "@types/react": "*"
       }
     },
-    "node_modules/@types/react-virtualized-auto-sizer": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.8.tgz",
-      "integrity": "sha512-keJpNyhiwfl2+N12G1ocCVA5ZDBArbPLe/S90X3kt7fam9naeHdaYYWbpe2sHczp70JWJ+2QLhBE8kLvLuVNjA==",
-      "deprecated": "This is a stub types definition. react-virtualized-auto-sizer provides its own type definitions, so you do not need this installed.",
-      "dev": true,
-      "dependencies": {
-        "react-virtualized-auto-sizer": "*"
-      }
-    },
     "node_modules/@types/react-window": {
       "version": "1.8.8",
       "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz",
@@ -5959,9 +5758,9 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.5.155",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz",
-      "integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng=="
+      "version": "1.5.157",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.157.tgz",
+      "integrity": "sha512-/0ybgsQd1muo8QlnuTpKwtl0oX5YMlUGbm8xyqgDU00motRkKFFbUJySAQBWcY79rVqNLWIWa87BGVGClwAB2w=="
     },
     "node_modules/element-size": {
       "version": "1.1.1",
@@ -6115,27 +5914,27 @@
       }
     },
     "node_modules/es-abstract": {
-      "version": "1.23.9",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz",
-      "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==",
+      "version": "1.23.10",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.10.tgz",
+      "integrity": "sha512-MtUbM072wlJNyeYAe0mhzrD+M6DIJa96CZAOBBrhDbgKnB4MApIKefcyAB1eOdYn8cUNZgvwBvEzdoAYsxgEIw==",
       "dev": true,
       "dependencies": {
         "array-buffer-byte-length": "^1.0.2",
         "arraybuffer.prototype.slice": "^1.0.4",
         "available-typed-arrays": "^1.0.7",
         "call-bind": "^1.0.8",
-        "call-bound": "^1.0.3",
+        "call-bound": "^1.0.4",
         "data-view-buffer": "^1.0.2",
         "data-view-byte-length": "^1.0.2",
         "data-view-byte-offset": "^1.0.1",
         "es-define-property": "^1.0.1",
         "es-errors": "^1.3.0",
-        "es-object-atoms": "^1.0.0",
+        "es-object-atoms": "^1.1.1",
         "es-set-tostringtag": "^2.1.0",
         "es-to-primitive": "^1.3.0",
         "function.prototype.name": "^1.1.8",
-        "get-intrinsic": "^1.2.7",
-        "get-proto": "^1.0.0",
+        "get-intrinsic": "^1.3.0",
+        "get-proto": "^1.0.1",
         "get-symbol-description": "^1.1.0",
         "globalthis": "^1.0.4",
         "gopd": "^1.2.0",
@@ -6151,13 +5950,13 @@
         "is-shared-array-buffer": "^1.0.4",
         "is-string": "^1.1.1",
         "is-typed-array": "^1.1.15",
-        "is-weakref": "^1.1.0",
+        "is-weakref": "^1.1.1",
         "math-intrinsics": "^1.1.0",
-        "object-inspect": "^1.13.3",
+        "object-inspect": "^1.13.4",
         "object-keys": "^1.1.1",
         "object.assign": "^4.1.7",
         "own-keys": "^1.0.1",
-        "regexp.prototype.flags": "^1.5.3",
+        "regexp.prototype.flags": "^1.5.4",
         "safe-array-concat": "^1.1.3",
         "safe-push-apply": "^1.0.0",
         "safe-regex-test": "^1.1.0",
@@ -6170,7 +5969,7 @@
         "typed-array-byte-offset": "^1.0.4",
         "typed-array-length": "^1.0.7",
         "unbox-primitive": "^1.1.0",
-        "which-typed-array": "^1.1.18"
+        "which-typed-array": "^1.1.19"
       },
       "engines": {
         "node": ">= 0.4"
@@ -12504,9 +12303,9 @@
       }
     },
     "node_modules/react-error-boundary": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-5.0.0.tgz",
-      "integrity": "sha512-tnjAxG+IkpLephNcePNA7v6F/QpWLH8He65+DmedchDwg162JZqx4NmbXj0mlAYVVEd81OW7aFhmbsScYfiAFQ==",
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.0.0.tgz",
+      "integrity": "sha512-gdlJjD7NWr0IfkPlaREN2d9uUZUlksrfOx7SX62VRerwXbMY6ftGCIZua1VG1aXFNOimhISsTq+Owp725b9SiA==",
       "dependencies": {
         "@babel/runtime": "^7.12.5"
       },
@@ -12558,9 +12357,9 @@
       }
     },
     "node_modules/react-markdown": {
-      "version": "9.1.0",
-      "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.1.0.tgz",
-      "integrity": "sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==",
+      "version": "10.1.0",
+      "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz",
+      "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==",
       "dependencies": {
         "@types/hast": "^3.0.0",
         "@types/mdast": "^4.0.0",
@@ -12596,9 +12395,9 @@
       }
     },
     "node_modules/react-router": {
-      "version": "7.6.0",
-      "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.0.tgz",
-      "integrity": "sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==",
+      "version": "7.6.1",
+      "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.1.tgz",
+      "integrity": "sha512-hPJXXxHJZEsPFNVbtATH7+MMX43UDeOauz+EAU4cgqTn7ojdI9qQORqS8Z0qmDlL1TclO/6jLRYUEtbWidtdHQ==",
       "dependencies": {
         "cookie": "^1.0.1",
         "set-cookie-parser": "^2.6.0"
@@ -12939,6 +12738,11 @@
       "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
       "dev": true
     },
+    "node_modules/reselect": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
+      "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="
+    },
     "node_modules/resolve": {
       "version": "1.22.10",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
@@ -14262,9 +14066,9 @@
       "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="
     },
     "node_modules/tinyglobby": {
-      "version": "0.2.13",
-      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
-      "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
+      "version": "0.2.14",
+      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
+      "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
       "dev": true,
       "dependencies": {
         "fdir": "^6.4.4",
@@ -14924,6 +14728,14 @@
         "requires-port": "^1.0.0"
       }
     },
+    "node_modules/use-sync-external-store": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
+      "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+      }
+    },
     "node_modules/util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -15013,9 +14825,9 @@
       }
     },
     "node_modules/watchpack": {
-      "version": "2.4.3",
-      "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.3.tgz",
-      "integrity": "sha512-adBYQLivcg1jbdKEJeqScJJFvgm4qY9+3tXw+jdG6lkVeqRJEtiQmSWjmth8GKmDZuX7sYM4YFxQsf0AzMfGGw==",
+      "version": "2.4.4",
+      "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz",
+      "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==",
       "dependencies": {
         "glob-to-regexp": "^0.4.1",
         "graceful-fs": "^4.1.2"
@@ -15158,9 +14970,9 @@
       }
     },
     "node_modules/webpack-sources": {
-      "version": "3.2.3",
-      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
-      "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.0.tgz",
+      "integrity": "sha512-77R0RDmJfj9dyv5p3bM5pOHa+X8/ZkO9c7kpDstigkC4nIDobadsfSGCwB4bKhMVxqAok8tajaoR8rirM7+VFQ==",
       "engines": {
         "node": ">=10.13.0"
       }

+ 7 - 8
frontend/taipy-gui/package.json

@@ -1,14 +1,14 @@
 {
   "name": "taipy-gui",
-  "version": "4.1.0",
+  "version": "4.2.0",
   "private": true,
   "dependencies": {
     "@emotion/react": "^11.10.0",
     "@emotion/styled": "^11.10.0",
-    "@mui/icons-material": "^6.0.1",
-    "@mui/material": "^6.0.1",
-    "@mui/x-date-pickers": "^7.0.0",
-    "@mui/x-tree-view": "^7.0.0",
+    "@mui/icons-material": "^7.1.0",
+    "@mui/material": "^7.1.0",
+    "@mui/x-date-pickers": "^8.3.0",
+    "@mui/x-tree-view": "^8.3.0",
     "apache-arrow": "^16.1.0",
     "axios": "^1.2.0",
     "better-react-mathjax": "^2.0.3",
@@ -20,10 +20,10 @@
     "plotly.js": "^3.0.0",
     "react": "^18.3.1",
     "react-dom": "^18.3.1",
-    "react-error-boundary": "^5.0.0",
+    "react-error-boundary": "^6.0.0",
     "react-helmet-async": "^2.0.1",
     "react-jsx-parser": "^2.1.0",
-    "react-markdown": "^9.0.1",
+    "react-markdown": "^10.1.0",
     "react-plotly.js": "^2.5.1",
     "react-router": "^7.1.1",
     "react-virtualized-auto-sizer": "^1.0.6",
@@ -86,7 +86,6 @@
     "@types/react-dom": "^18.3.5",
     "@types/react-plotly.js": "^2.5.0",
     "@types/react-router": "^5.1.20",
-    "@types/react-virtualized-auto-sizer": "^1.0.1",
     "@types/react-window": "^1.8.5",
     "@types/react-window-infinite-loader": "^1.0.5",
     "@types/sprintf-js": "^1.1.2",

+ 1 - 1
frontend/taipy-gui/packaging/package.json

@@ -1,6 +1,6 @@
 {
   "name": "taipy-gui",
-  "version": "4.1.0",
+  "version": "4.2.0",
   "private": true,
   "main": "./taipy-gui.js",
   "types": "./taipy-gui.d.ts"

+ 1 - 1
frontend/taipy-gui/src/components/Router.tsx

@@ -17,7 +17,7 @@ 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 { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns";
 import axios from "axios";
 import { MathJaxContext } from "better-react-mathjax";
 import { SnackbarProvider } from "notistack";

+ 1 - 1
frontend/taipy-gui/src/components/Taipy/Chat.tsx

@@ -29,7 +29,7 @@ import Avatar from "@mui/material/Avatar";
 import Box from "@mui/material/Box";
 import Button from "@mui/material/Button";
 import Chip from "@mui/material/Chip";
-import Grid from "@mui/material/Grid2";
+import Grid from "@mui/material/Grid";
 import IconButton from "@mui/material/IconButton";
 import InputAdornment from "@mui/material/InputAdornment";
 import Paper from "@mui/material/Paper";

+ 121 - 74
frontend/taipy-gui/src/components/Taipy/DateRange.spec.tsx

@@ -17,7 +17,7 @@ import userEvent from "@testing-library/user-event";
 import "@testing-library/jest-dom";
 
 import { LocalizationProvider } from "@mui/x-date-pickers";
-import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFnsV3";
+import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns";
 
 import DateRange from "./DateRange";
 import { TaipyContext } from "../../context/taipyContext";
@@ -77,19 +77,19 @@ describe("DateRange Component", () => {
                 <DateRange dates={curDates} />
             </LocalizationProvider>
         );
-        const elts = getAllByTestId("CalendarIcon");
-        expect(elts).toHaveLength(2);
-        expect(elts[0].parentElement?.tagName).toBe("BUTTON");
+        const elements = getAllByTestId("CalendarIcon");
+        expect(elements).toHaveLength(2);
+        expect(elements[0].parentElement?.tagName).toBe("BUTTON");
     });
     it("renders with analog time picker", async () => {
         const { getAllByTestId } = render(
             <LocalizationProvider dateAdapter={AdapterDateFns}>
-                <DateRange dates={curDates} withTime={true} analogic={true}/>
+                <DateRange dates={curDates} withTime={true} analogic={true} />
             </LocalizationProvider>
         );
-        const elts = getAllByTestId("CalendarIcon");
-        expect(elts).toHaveLength(2);
-        expect(elts[0].parentElement?.tagName).toBe("BUTTON");
+        const elements = getAllByTestId("CalendarIcon");
+        expect(elements).toHaveLength(2);
+        expect(elements[0].parentElement?.tagName).toBe("BUTTON");
     });
     it("displays the right info for string", async () => {
         const { getAllByTestId } = render(
@@ -97,20 +97,20 @@ describe("DateRange Component", () => {
                 <DateRange dates={curDates} defaultDates={defaultDates} className="taipy-date-2" />
             </LocalizationProvider>
         );
-        const elts = getAllByTestId("CalendarIcon");
-        expect(elts).toHaveLength(2);
-        expect(elts[0].parentElement?.parentElement?.parentElement?.parentElement).toHaveClass(
+        const elements = getAllByTestId("CalendarIcon");
+        expect(elements).toHaveLength(2);
+        expect(elements[0].parentElement?.parentElement?.parentElement?.parentElement).toHaveClass(
             "taipy-date-2-picker",
             "taipy-date-2-picker-start"
         );
-        expect(elts[0].parentElement?.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass(
+        expect(elements[0].parentElement?.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass(
             "taipy-date-2"
         );
-        expect(elts[1].parentElement?.parentElement?.parentElement?.parentElement).toHaveClass(
+        expect(elements[1].parentElement?.parentElement?.parentElement?.parentElement).toHaveClass(
             "taipy-date-2-picker",
             "taipy-date-2-picker-end"
         );
-        expect(elts[1].parentElement?.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass(
+        expect(elements[1].parentElement?.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass(
             "taipy-date-2"
         );
     });
@@ -150,7 +150,7 @@ describe("DateRange Component", () => {
         expect(cleanText(input2?.value || "")).toEqual("31-01-2001");
     });
     it("shows labels", async () => {
-        const { getByLabelText } = render(
+        render(
             <LocalizationProvider dateAdapter={AdapterDateFns}>
                 <DateRange
                     defaultDates={defaultDates}
@@ -161,10 +161,13 @@ describe("DateRange Component", () => {
                 />
             </LocalizationProvider>
         );
-        const startInput = getByLabelText("start") as HTMLInputElement;
-        expect(startInput.value).toBe("01/01/2001");
-        const endInput = getByLabelText("end") as HTMLInputElement;
-        expect(endInput.value).toBe("01/31/2001");
+        const labels = document.querySelectorAll("label");
+        expect(labels).toHaveLength(2);
+        const first = labels[0].textContent === "start";
+        const startLabel = first ? labels[0] : labels[1];
+        expect(startLabel.parentElement?.querySelector("input")?.value).toBe("01/01/2001");
+        const endLabel = first ? labels[1] : labels[0];
+        expect(endLabel.parentElement?.querySelector("input")?.value).toBe("01/31/2001");
     });
     it("displays with width=70%", async () => {
         render(
@@ -220,29 +223,44 @@ describe("DateRange Component", () => {
     it("dispatch a well formed message", async () => {
         const dispatch = jest.fn();
         const state: TaipyState = INITIAL_STATE;
-        render(
+        const { getAllByText } = render(
             <TaipyContext.Provider value={{ state, dispatch }}>
                 <LocalizationProvider dateAdapter={AdapterDateFns}>
-                    <DateRange dates={curDates} className="taipy-date-range" />
+                    <DateRange dates={undefined as unknown as string[]} className="taipy-date-range" />
                 </LocalizationProvider>
             </TaipyContext.Provider>
         );
-        const input = document.querySelector(".taipy-date-range-picker-start input");
-        expect(input).toBeInTheDocument();
-        const input2 = document.querySelector(".taipy-date-range-picker-end input");
-        expect(input2).toBeInTheDocument();
-        if (input && input2) {
-            // await userEvent.clear(input);
-            await userEvent.type(input, "{ArrowLeft}{ArrowLeft}{ArrowLeft}01012001", { delay: 1 });
-            // await userEvent.clear(input2);
-            await userEvent.type(input2, "{ArrowLeft}{ArrowLeft}{ArrowLeft}01312001", { delay: 1 });
-            expect(dispatch).toHaveBeenLastCalledWith({
-                name: "",
-                payload: { value: ["Mon Jan 01 2001", "Wed Jan 31 2001"] },
-                propagate: true,
-                type: "SEND_UPDATE_ACTION",
-            });
+        const years = getAllByText("YYYY");
+        expect(years).toHaveLength(2);
+        expect(years[0]).toBeInTheDocument();
+        expect(years[1]).toBeInTheDocument();
+        await userEvent.type(years[0], "2001", { delay: 1 });
+        await userEvent.type(years[1], "2001", { delay: 1 });
+
+        const months = getAllByText("MM");
+        expect(months).toHaveLength(2);
+        expect(months[0]).toBeInTheDocument();
+        expect(months[1]).toBeInTheDocument();
+        if (months[0].closest(".taipy-date-range-picker-start")) {
+            await userEvent.type(months[0], "01", { delay: 1 });
+            await userEvent.type(months[1], "03", { delay: 1 });
+        } else {
+            await userEvent.type(months[1], "01", { delay: 1 });
+            await userEvent.type(months[0], "03", { delay: 1 });
         }
+        const days = getAllByText("DD");
+        expect(days).toHaveLength(2);
+        expect(days[0]).toBeInTheDocument();
+        expect(days[1]).toBeInTheDocument();
+        await userEvent.type(days[0], "01", { delay: 1 });
+        await userEvent.type(days[1], "01", { delay: 1 });
+
+        expect(dispatch).toHaveBeenLastCalledWith({
+            name: "",
+            payload: { value: ["Mon Jan 01 2001", "Thu Mar 01 2001"] },
+            propagate: true,
+            type: "SEND_UPDATE_ACTION",
+        });
     });
 });
 
@@ -253,9 +271,9 @@ describe("DateRange with time Component", () => {
                 <DateRange dates={curDates} withTime={true} />
             </LocalizationProvider>
         );
-        const elts = getAllByTestId("CalendarIcon");
-        expect(elts).toHaveLength(2);
-        expect(elts[0].parentElement?.tagName).toBe("BUTTON");
+        const elements = getAllByTestId("CalendarIcon");
+        expect(elements).toHaveLength(2);
+        expect(elements[0].parentElement?.tagName).toBe("BUTTON");
     });
     it("displays the right info for string", async () => {
         const { getAllByTestId } = render(
@@ -263,20 +281,20 @@ describe("DateRange with time Component", () => {
                 <DateRange dates={curDates} withTime={true} className="taipy-time" />
             </LocalizationProvider>
         );
-        const elts = getAllByTestId("CalendarIcon");
-        expect(elts).toHaveLength(2);
-        expect(elts[0].parentElement?.parentElement?.parentElement?.parentElement).toHaveClass(
+        const elements = getAllByTestId("CalendarIcon");
+        expect(elements).toHaveLength(2);
+        expect(elements[0].parentElement?.parentElement?.parentElement?.parentElement).toHaveClass(
             "taipy-time-picker",
             "taipy-time-picker-start"
         );
-        expect(elts[0].parentElement?.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass(
+        expect(elements[0].parentElement?.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass(
             "taipy-time"
         );
-        expect(elts[1].parentElement?.parentElement?.parentElement?.parentElement).toHaveClass(
+        expect(elements[1].parentElement?.parentElement?.parentElement?.parentElement).toHaveClass(
             "taipy-time-picker",
             "taipy-time-picker-end"
         );
-        expect(elts[1].parentElement?.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass(
+        expect(elements[1].parentElement?.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass(
             "taipy-time"
         );
     });
@@ -330,10 +348,13 @@ describe("DateRange with time Component", () => {
                 />
             </LocalizationProvider>
         );
-        const startInput = getByLabelText("start") as HTMLInputElement;
-        expect(startInput.value.toLocaleLowerCase()).toBe("01/01/2001 12:00 am");
-        const endInput = getByLabelText("end") as HTMLInputElement;
-        expect(endInput.value.toLocaleLowerCase()).toBe("01/31/2001 12:00 am");
+        const labels = document.querySelectorAll("label");
+        expect(labels).toHaveLength(2);
+        const first = labels[0].textContent === "start";
+        const startLabel = first ? labels[0] : labels[1];
+        expect(startLabel.parentElement?.querySelector("input")?.value).toBe("01/01/2001 12:00 AM");
+        const endLabel = first ? labels[1] : labels[0];
+        expect(endLabel.parentElement?.querySelector("input")?.value).toBe("01/31/2001 12:00 AM");
     });
     it("is disabled", async () => {
         render(
@@ -371,36 +392,62 @@ describe("DateRange with time Component", () => {
     it("dispatch a well formed message", async () => {
         const dispatch = jest.fn();
         const state: TaipyState = INITIAL_STATE;
-        render(
+        const { getAllByText } = render(
             <TaipyContext.Provider value={{ state, dispatch }}>
                 <LocalizationProvider dateAdapter={AdapterDateFns}>
-                    <DateRange dates={curDates} withTime={true} updateVarName="varname" className="tp-dt" />
+                    <DateRange dates={[]} withTime={true} updateVarName="varname" className="tp-dt" />
                 </LocalizationProvider>
             </TaipyContext.Provider>
         );
-        const input = document.querySelector(".tp-dt-picker-start input");
-        expect(input).toBeInTheDocument();
-        const input2 = document.querySelector(".tp-dt-picker-end input");
-        expect(input2).toBeInTheDocument();
-        if (input && input2) {
-            // await userEvent.clear(input);
-            await userEvent.type(
-                input,
-                "{ArrowLeft}{ArrowLeft}{ArrowLeft}{ArrowLeft}{ArrowLeft}{ArrowLeft}010120010101am",
-                { delay: 1 }
-            );
-            // await userEvent.clear(input2);
-            await userEvent.type(
-                input2,
-                "{ArrowLeft}{ArrowLeft}{ArrowLeft}{ArrowLeft}{ArrowLeft}{ArrowLeft}123120010101am",
-                { delay: 1 }
-            );
-            expect(dispatch).toHaveBeenLastCalledWith({
-                name: "varname",
-                payload: { value: ["2001-01-01T01:01:00.000Z", "2001-12-31T01:01:00.000Z"] },
-                propagate: true,
-                type: "SEND_UPDATE_ACTION",
-            });
+
+        const years = getAllByText("YYYY");
+        expect(years).toHaveLength(2);
+        expect(years[0]).toBeInTheDocument();
+        expect(years[1]).toBeInTheDocument();
+        await userEvent.type(years[0], "2001", { delay: 1 });
+        await userEvent.type(years[1], "2001", { delay: 1 });
+
+        const months = getAllByText("MM");
+        expect(months).toHaveLength(2);
+        expect(months[0]).toBeInTheDocument();
+        expect(months[1]).toBeInTheDocument();
+        if (months[0].closest(".taipy-date-range-picker-start")) {
+            await userEvent.type(months[0], "12", { delay: 1 });
+            await userEvent.type(months[1], "01", { delay: 1 });
+        } else {
+            await userEvent.type(months[1], "12", { delay: 1 });
+            await userEvent.type(months[0], "01", { delay: 1 });
         }
+        const days = getAllByText("DD");
+        expect(days).toHaveLength(2);
+        expect(days[0]).toBeInTheDocument();
+        expect(days[1]).toBeInTheDocument();
+        await userEvent.type(days[0], "01", { delay: 1 });
+        await userEvent.type(days[1], "01", { delay: 1 });
+        const hours = getAllByText("hh");
+        expect(hours).toHaveLength(2);
+        expect(hours[0]).toBeInTheDocument();
+        expect(hours[1]).toBeInTheDocument();
+        await userEvent.type(hours[0], "01", { delay: 1 });
+        await userEvent.type(hours[1], "01", { delay: 1 });
+        const minutes = getAllByText("mm");
+        expect(minutes).toHaveLength(2);
+        expect(minutes[0]).toBeInTheDocument();
+        expect(minutes[1]).toBeInTheDocument();
+        await userEvent.type(minutes[0], "01", { delay: 1 });
+        await userEvent.type(minutes[1], "01", { delay: 1 });
+        const ams = getAllByText("aa");
+        expect(ams).toHaveLength(2);
+        expect(ams[0]).toBeInTheDocument();
+        expect(ams[1]).toBeInTheDocument();
+        await userEvent.type(ams[0], "am", { delay: 1 });
+        await userEvent.type(ams[1], "am", { delay: 1 });
+
+        expect(dispatch).toHaveBeenLastCalledWith({
+            name: "varname",
+            payload: { value: ["2001-01-01T01:01:00.000Z", "2001-12-01T01:01:00.000Z"] },
+            propagate: true,
+            type: "SEND_UPDATE_ACTION",
+        });
     });
 });

+ 5 - 5
frontend/taipy-gui/src/components/Taipy/DateRange.tsx

@@ -44,7 +44,7 @@ interface DateRangeProps extends TaipyActiveProps, TaipyChangeProps {
     analogic?: boolean;
 }
 
-const textFieldProps = { textField: { margin: "dense" } } as BaseDateTimePickerSlotProps<Date>;
+const textFieldProps = { textField: { margin: "dense" } } as BaseDateTimePickerSlotProps;
 
 const getRangeDateTime = (
     json: string | string[] | undefined,
@@ -152,7 +152,7 @@ const DateRange = (props: DateRangeProps) => {
                         withTime ? (
                             <>
                                 <DateTimePicker
-                                    {...(startProps as DateTimePickerProps<Date>)}
+                                    {...(startProps as DateTimePickerProps)}
                                     value={value[0]}
                                     onChange={handleChangeStart}
                                     className={
@@ -169,7 +169,7 @@ const DateRange = (props: DateRangeProps) => {
                                 />
                                 <Typography>{separator}</Typography>
                                 <DateTimePicker
-                                    {...(endProps as DateTimePickerProps<Date>)}
+                                    {...(endProps as DateTimePickerProps)}
                                     value={value[1]}
                                     onChange={handleChangeEnd}
                                     className={
@@ -188,7 +188,7 @@ const DateRange = (props: DateRangeProps) => {
                         ) : (
                             <>
                                 <DatePicker
-                                    {...(startProps as DatePickerProps<Date>)}
+                                    {...(startProps as DatePickerProps)}
                                     value={value[0]}
                                     onChange={handleChangeStart}
                                     className={
@@ -204,7 +204,7 @@ const DateRange = (props: DateRangeProps) => {
                                 />
                                 <Typography>{separator}</Typography>
                                 <DatePicker
-                                    {...(endProps as DatePickerProps<Date>)}
+                                    {...(endProps as DatePickerProps)}
                                     value={value[1]}
                                     onChange={handleChangeEnd}
                                     className={

+ 52 - 37
frontend/taipy-gui/src/components/Taipy/DateSelector.spec.tsx

@@ -17,7 +17,7 @@ import userEvent from "@testing-library/user-event";
 import "@testing-library/jest-dom";
 
 import { LocalizationProvider } from "@mui/x-date-pickers";
-import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFnsV3";
+import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns";
 
 import DateSelector from "./DateSelector";
 import { TaipyContext } from "../../context/taipyContext";
@@ -111,7 +111,7 @@ describe("DateSelector Component", () => {
         expect(cleanText(input?.value || "")).toEqual("11-01-01");
     });
     it("shows label", async () => {
-        const { getByLabelText } = render(
+        render(
             <LocalizationProvider dateAdapter={AdapterDateFns}>
                 <DateSelector
                     defaultDate="2001-01-01T00:00:01.001Z"
@@ -121,8 +121,10 @@ describe("DateSelector Component", () => {
                 />
             </LocalizationProvider>
         );
-        const input = getByLabelText("a label") as HTMLInputElement;
-        expect(input.value).toBe("01/01/2001");
+        const label = document.querySelector("label");
+        expect(label).toBeInTheDocument();
+        expect(label).toHaveTextContent("a label");
+        expect(label?.parentElement?.querySelector("input")?.value).toBe("01/01/2001");
     });
     it("displays with width=70%", async () => {
         render(
@@ -175,25 +177,29 @@ describe("DateSelector Component", () => {
     it("dispatch a well formed message", async () => {
         const dispatch = jest.fn();
         const state: TaipyState = INITIAL_STATE;
-        render(
+        const { getByText } = render(
             <TaipyContext.Provider value={{ state, dispatch }}>
                 <LocalizationProvider dateAdapter={AdapterDateFns}>
-                    <DateSelector date={curDateStr} />
+                    <DateSelector date="" />
                 </LocalizationProvider>
             </TaipyContext.Provider>
         );
-        const input = document.querySelector("input");
-        expect(input).toBeInTheDocument();
-        if (input) {
-            // await userEvent.clear(input);
-            await userEvent.type(input, "{ArrowLeft}{ArrowLeft}{ArrowLeft}01012001", { delay: 1 });
-            expect(dispatch).toHaveBeenLastCalledWith({
-                name: "",
-                payload: { value: "Mon Jan 01 2001" },
-                propagate: true,
-                type: "SEND_UPDATE_ACTION",
-            });
-        }
+        const year = getByText("YYYY");
+        expect(year).toBeInTheDocument();
+        await userEvent.type(year, "2001", { delay: 1 });
+        const month = getByText("MM");
+        expect(month).toBeInTheDocument();
+        await userEvent.type(month, "01", { delay: 1 });
+        const day = getByText("DD");
+        expect(day).toBeInTheDocument();
+        await userEvent.type(day, "01", { delay: 1 });
+
+        expect(dispatch).toHaveBeenLastCalledWith({
+            name: "",
+            payload: { value: "Mon Jan 01 2001" },
+            propagate: true,
+            type: "SEND_UPDATE_ACTION",
+        });
     });
 });
 
@@ -210,7 +216,7 @@ describe("DateSelector with time Component", () => {
     it("renders with analog time picker", async () => {
         const { getByTestId } = render(
             <LocalizationProvider dateAdapter={AdapterDateFns}>
-                <DateSelector date={curDateStr} withTime={true} analogic={true}/>
+                <DateSelector date={curDateStr} withTime={true} analogic={true} />
             </LocalizationProvider>
         );
         const elt = getByTestId("CalendarIcon");
@@ -287,28 +293,37 @@ describe("DateSelector with time Component", () => {
     it("dispatch a well formed message", async () => {
         const dispatch = jest.fn();
         const state: TaipyState = INITIAL_STATE;
-        render(
+        const { getByText } = render(
             <TaipyContext.Provider value={{ state, dispatch }}>
                 <LocalizationProvider dateAdapter={AdapterDateFns}>
-                    <DateSelector date={curDateStr} withTime={true} updateVarName="varname" />
+                    <DateSelector date="" withTime={true} updateVarName="varname" />
                 </LocalizationProvider>
             </TaipyContext.Provider>
         );
-        const input = document.querySelector("input");
-        expect(input).toBeInTheDocument();
-        if (input) {
-            // await userEvent.clear(input);
-            await userEvent.type(
-                input,
-                "{ArrowLeft}{ArrowLeft}{ArrowLeft}{ArrowLeft}{ArrowLeft}{ArrowLeft}010120010101am",
-                { delay: 1 }
-            );
-            expect(dispatch).toHaveBeenLastCalledWith({
-                name: "varname",
-                payload: { value: "2001-01-01T01:01:00.000Z" },
-                propagate: true,
-                type: "SEND_UPDATE_ACTION",
-            });
-        }
+        const year = getByText("YYYY");
+        expect(year).toBeInTheDocument();
+        await userEvent.type(year, "2001", { delay: 1 });
+        const month = getByText("MM");
+        expect(month).toBeInTheDocument();
+        await userEvent.type(month, "01", { delay: 1 });
+        const day = getByText("DD");
+        expect(day).toBeInTheDocument();
+        await userEvent.type(day, "01", { delay: 1 });
+        const hour = getByText("hh");
+        expect(hour).toBeInTheDocument();
+        await userEvent.type(hour, "01", { delay: 1 });
+        const minute = getByText("mm");
+        expect(minute).toBeInTheDocument();
+        await userEvent.type(minute, "01", { delay: 1 });
+        const am = getByText("aa");
+        expect(am).toBeInTheDocument();
+        await userEvent.type(am, "am", { delay: 1 });
+
+        expect(dispatch).toHaveBeenLastCalledWith({
+            name: "varname",
+            payload: { value: "2001-01-01T01:01:00.000Z" },
+            propagate: true,
+            type: "SEND_UPDATE_ACTION",
+        });
     });
 });

+ 5 - 5
frontend/taipy-gui/src/components/Taipy/DateSelector.tsx

@@ -50,7 +50,7 @@ interface DateSelectorProps extends TaipyActiveProps, TaipyChangeProps {
 }
 
 const boxSx = { display: "inline-block" };
-const textFieldProps = { textField: { margin: "dense" } } as BaseDateTimePickerSlotProps<Date>;
+const textFieldProps = { textField: { margin: "dense" } } as BaseDateTimePickerSlotProps;
 
 const analogicRenderers = {
     hours: renderTimeViewClock,
@@ -122,8 +122,8 @@ const DateSelector = (props: DateSelectorProps) => {
                     {editable ? (
                         withTime ? (
                             <DateTimePicker
-                                    {...(startProps as DateTimePickerProps<Date>)}
-                                    {...(endProps as DateTimePickerProps<Date>)}
+                                    {...(startProps as DateTimePickerProps)}
+                                    {...(endProps as DateTimePickerProps)}
                                     value={value}
                                     onChange={handleChange}
                                     className={getSuffixedClassNames(className, "-picker")}
@@ -136,8 +136,8 @@ const DateSelector = (props: DateSelectorProps) => {
                                 />
                         ) : (
                             <DatePicker
-                                {...(startProps as DatePickerProps<Date>)}
-                                {...(endProps as DatePickerProps<Date>)}
+                                {...(startProps as DatePickerProps)}
+                                {...(endProps as DatePickerProps)}
                                 value={value}
                                 onChange={handleChange}
                                 className={getSuffixedClassNames(className, "-picker")}

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

@@ -80,16 +80,16 @@ const Field = (props: TaipyFieldProps) => {
                         {value}
                     </pre>
                 ) : mode == "markdown" || mode == "md" ? (
-                    <Suspense fallback={<div>Loading Markdown...</div>}>
-                        <Markdown
-                            className={`${className} ${getSuffixedClassNames(
-                                className,
-                                "-markdown"
-                            )} ${getComponentClassName(props.children)}`}
-                        >
-                            {value}
-                        </Markdown>
-                    </Suspense>
+                    <div
+                        className={`${className} ${getSuffixedClassNames(
+                            className,
+                            "-markdown"
+                        )} ${getComponentClassName(props.children)}`}
+                    >
+                        <Suspense fallback={<div>Loading Markdown...</div>}>
+                            <Markdown>{value}</Markdown>
+                        </Suspense>
+                    </div>
                 ) : raw || mode == "raw" ? (
                     <span
                         className={`${className} ${getSuffixedClassNames(className, "-raw")} ${getComponentClassName(

+ 9 - 2
frontend/taipy-gui/src/components/Taipy/TableFilter.spec.tsx

@@ -155,8 +155,15 @@ describe("Table Filter Component", () => {
         await userEvent.click(getByText("is on or before"));
         const validate = getByTestId("CheckIcon").parentElement;
         expect(validate).toBeDisabled();
-        const input = getByPlaceholderText("YYYY/MM/DD");
-        await userEvent.type(input, "{ArrowLeft}{ArrowLeft}{ArrowLeft}2020/11/11", { delay: 1 });
+        const year = getByText("YYYY");
+        expect(year).toBeInTheDocument();
+        await userEvent.type(year, "2020", { delay: 1 });
+        const month = getByText("MM");
+        expect(month).toBeInTheDocument();
+        await userEvent.type(month, "01", { delay: 1 });
+        const day = getByText("DD");
+        expect(day).toBeInTheDocument();
+        await userEvent.type(day, "01", { delay: 1 });
         expect(validate).not.toBeDisabled();
     });
     it("adds a row on validation", async () => {

+ 2 - 2
frontend/taipy-gui/src/components/Taipy/TableFilter.tsx

@@ -18,7 +18,7 @@ import DeleteIcon from "@mui/icons-material/Delete";
 import FilterListIcon from "@mui/icons-material/FilterList";
 import Badge from "@mui/material/Badge";
 import FormControl from "@mui/material/FormControl";
-import Grid from "@mui/material/Grid2";
+import Grid from "@mui/material/Grid";
 import IconButton from "@mui/material/IconButton";
 import InputLabel from "@mui/material/InputLabel";
 import MenuItem from "@mui/material/MenuItem";
@@ -28,7 +28,7 @@ import Select, { SelectChangeEvent } from "@mui/material/Select";
 import TextField from "@mui/material/TextField";
 import Tooltip from "@mui/material/Tooltip";
 import { DateField, LocalizationProvider } from "@mui/x-date-pickers";
-import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFnsV3";
+import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns";
 
 import { ColumnDesc, defaultDateFormat, getSortByIndex, iconInRowSx, FilterDesc } from "./tableUtils";
 import { getDateTime, getTypeFromDf } from "../../utils";

+ 1 - 1
frontend/taipy-gui/src/components/Taipy/TableSort.tsx

@@ -17,7 +17,7 @@ import DeleteIcon from "@mui/icons-material/Delete";
 import SortByAlpha from "@mui/icons-material/SortByAlpha";
 import Badge from "@mui/material/Badge";
 import FormControl from "@mui/material/FormControl";
-import Grid from "@mui/material/Grid2";
+import Grid from "@mui/material/Grid";
 import IconButton from "@mui/material/IconButton";
 import InputLabel from "@mui/material/InputLabel";
 import MenuItem from "@mui/material/MenuItem";

+ 166 - 167
frontend/taipy-gui/src/components/Taipy/TimeSelector.spec.tsx

@@ -12,19 +12,16 @@
  */
 
 import React from "react";
-import { render, fireEvent, screen } from "@testing-library/react";
+import { render } from "@testing-library/react";
 import userEvent from "@testing-library/user-event";
 import "@testing-library/jest-dom";
 
 import { LocalizationProvider } from "@mui/x-date-pickers";
-import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFnsV3";
+import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns";
 
 import TimeSelector from "./TimeSelector";
-import { TaipyContext } from "../../context/taipyContext";
-import { TaipyState, INITIAL_STATE } from "../../context/taipyReducers";
 import { getClientServerTimeZoneOffset } from "../../utils";
 
-
 jest.mock("../../utils", () => {
     const originalModule = jest.requireActual("../../utils");
 
@@ -67,169 +64,171 @@ const curDateStr = curDate.toISOString();
 
 const cleanText = (val: string) => val.replace(/\u200e|\u2066|\u2067|\u2068|\u2069/g, "");
 
-describe("TimeSelector component with digital time picker", () => {
-    it("renders", async () => {
-        const { getByTestId } = render(
-            <LocalizationProvider dateAdapter={AdapterDateFns}>
-                <TimeSelector time={curDateStr} />
-            </LocalizationProvider>
-        );
-        const elt = getByTestId("ClockIcon");
-        expect(elt.parentElement?.tagName).toBe("BUTTON");
-    });
-
-    it("displays the right info for string", async () => {
-        const { getByTestId } = render(
-            <LocalizationProvider dateAdapter={AdapterDateFns}>
-                <TimeSelector time={curDateStr} className="taipy-time" />
-            </LocalizationProvider>
-        );
-        const elt = getByTestId("ClockIcon");
-        expect(elt.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass("taipy-time-picker");
-        expect(elt.parentElement?.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass("taipy-time");
-    });
-
-    it("displays the right value after supplied by picker ", async () => {
-        render(
-            <LocalizationProvider dateAdapter={AdapterDateFns}>
-                <TimeSelector
-                    time={undefined as unknown as string}
-                />
-            </LocalizationProvider>
-        );
-        const input = document.querySelector("input");
-        expect(input).toBeInTheDocument();
-        fireEvent.change(screen.getByRole("textbox"), {target: {value: '10:20 AM'}});
-        expect(screen.getByRole('textbox')).toHaveValue('10:20 AM');
-    });
-
-    it("displays the default value", async () => {
-        render(
-            <LocalizationProvider dateAdapter={AdapterDateFns}>
-                <TimeSelector
-                    defaultTime="2001-01-01T01:01:01"
-                    time={undefined as unknown as string}
-                />
-            </LocalizationProvider>
-        );
-        const input = document.querySelector("input");
-        expect(input).toBeInTheDocument();
-        expect(cleanText(input?.value || "").toLocaleLowerCase()).toEqual("01:01 am");
-    });
-    it("displays the default value with format", async () => {
-        render(
-            <LocalizationProvider dateAdapter={AdapterDateFns}>
-                <TimeSelector
-                    defaultTime="2001-01-01T14:20:01"
-                    time={undefined as unknown as string}
-                    format="hh aa"
-                />
-            </LocalizationProvider>
-        );
-        const input = document.querySelector("input");
-        expect(input).toBeInTheDocument();
-        expect(cleanText(input?.value || "")).toEqual("02 PM");
-    });
-    it("is disabled", async () => {
-        render(
-            <LocalizationProvider dateAdapter={AdapterDateFns}>
-                <TimeSelector time={curDateStr} active={false} />
-            </LocalizationProvider>
-        );
-        const input = document.querySelector("input");
-        expect(input).toBeInTheDocument();
-        expect(input).toBeDisabled();
-    });
-    it("is enabled by default", async () => {
-        render(
-            <LocalizationProvider dateAdapter={AdapterDateFns}>
-                <TimeSelector time={curDateStr} />
-            </LocalizationProvider>
-        );
-        const input = document.querySelector("input");
-        expect(input).toBeInTheDocument();
-        expect(input).not.toBeDisabled();
-    });
-    it("is enabled by active", async () => {
-        render(
-            <LocalizationProvider dateAdapter={AdapterDateFns}>
-                <TimeSelector time={curDateStr} active={true} />
-            </LocalizationProvider>
-        );
-        const input = document.querySelector("input");
-        expect(input).toBeInTheDocument();
-        expect(input).not.toBeDisabled();
-    });
-});
-
-describe("TimeSelector component with analogue time picker", () => {
-    it("renders", async () => {
-        const { getByTestId } = render(
-            <LocalizationProvider dateAdapter={AdapterDateFns}>
-                <TimeSelector time={curDateStr} analogic={true} />
-            </LocalizationProvider>
-        );
-        const input = document.querySelector("input");
-        expect(input).toBeInTheDocument();
-    });
-    it("displays the default value", async () => {
-        render(
-            <LocalizationProvider dateAdapter={AdapterDateFns}>
-                <TimeSelector
-                    defaultTime="2001-01-01T01:01:01"
-                    time={undefined as unknown as string}
-                    analogic={true}
-                />
-            </LocalizationProvider>
-        );
-        const input = document.querySelector("input");
-        expect(input).toBeInTheDocument();
-        expect(cleanText(input?.value || "").toLocaleLowerCase()).toEqual("01:01 am");
-    });
-    it("displays the default value with format", async () => {
-        render(
-            <LocalizationProvider dateAdapter={AdapterDateFns}>
-                <TimeSelector
-                    defaultTime="2001-01-01T14:20:01"
-                    time={undefined as unknown as string}
-                    analogic={true}
-                    format="hh aa"
-                />
-            </LocalizationProvider>
-        );
-        const input = document.querySelector("input");
-        expect(input).toBeInTheDocument();
-        expect(cleanText(input?.value || "")).toEqual("02 PM");
+describe("TimeSelector component", () => {
+    describe("TimeSelector component with digital time picker", () => {
+        it("renders", async () => {
+            const { getByTestId } = render(
+                <LocalizationProvider dateAdapter={AdapterDateFns}>
+                    <TimeSelector time={curDateStr} />
+                </LocalizationProvider>
+            );
+            const elt = getByTestId("ClockIcon");
+            expect(elt.parentElement?.tagName).toBe("BUTTON");
+        });
+
+        it("displays the right info for string", async () => {
+            const { getByTestId } = render(
+                <LocalizationProvider dateAdapter={AdapterDateFns}>
+                    <TimeSelector time={curDateStr} className="taipy-time" />
+                </LocalizationProvider>
+            );
+            const elt = getByTestId("ClockIcon");
+            expect(elt.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass("taipy-time-picker");
+            expect(elt.parentElement?.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass(
+                "taipy-time"
+            );
+        });
+
+        it("displays the right value after supplied by picker ", async () => {
+            const { getByText } = render(
+                <LocalizationProvider dateAdapter={AdapterDateFns}>
+                    <TimeSelector time={undefined as unknown as string} />
+                </LocalizationProvider>
+            );
+            const hour = getByText("hh");
+            expect(hour).toBeInTheDocument();
+            await userEvent.type(hour, "1020AM", { delay: 1 });
+
+            const input = document.querySelector("input");
+            expect(input).toBeInTheDocument();
+            expect(input).toHaveValue("10:20 AM");
+        });
+
+        it("displays the default value", async () => {
+            render(
+                <LocalizationProvider dateAdapter={AdapterDateFns}>
+                    <TimeSelector defaultTime="2001-01-01T01:01:01" time={undefined as unknown as string} />
+                </LocalizationProvider>
+            );
+            const input = document.querySelector("input");
+            expect(input).toBeInTheDocument();
+            expect(cleanText(input?.value || "").toLocaleLowerCase()).toEqual("01:01 am");
+        });
+        it("displays the default value with format", async () => {
+            render(
+                <LocalizationProvider dateAdapter={AdapterDateFns}>
+                    <TimeSelector
+                        defaultTime="2001-01-01T14:20:01"
+                        time={undefined as unknown as string}
+                        format="hh aa"
+                    />
+                </LocalizationProvider>
+            );
+            const input = document.querySelector("input");
+            expect(input).toBeInTheDocument();
+            expect(cleanText(input?.value || "")).toEqual("02 PM");
+        });
+        it("is disabled", async () => {
+            render(
+                <LocalizationProvider dateAdapter={AdapterDateFns}>
+                    <TimeSelector time={curDateStr} active={false} />
+                </LocalizationProvider>
+            );
+            const input = document.querySelector("input");
+            expect(input).toBeInTheDocument();
+            expect(input).toBeDisabled();
+        });
+        it("is enabled by default", async () => {
+            render(
+                <LocalizationProvider dateAdapter={AdapterDateFns}>
+                    <TimeSelector time={curDateStr} />
+                </LocalizationProvider>
+            );
+            const input = document.querySelector("input");
+            expect(input).toBeInTheDocument();
+            expect(input).not.toBeDisabled();
+        });
+        it("is enabled by active", async () => {
+            render(
+                <LocalizationProvider dateAdapter={AdapterDateFns}>
+                    <TimeSelector time={curDateStr} active={true} />
+                </LocalizationProvider>
+            );
+            const input = document.querySelector("input");
+            expect(input).toBeInTheDocument();
+            expect(input).not.toBeDisabled();
+        });
     });
 
-    it("is disabled", async () => {
-        render(
-            <LocalizationProvider dateAdapter={AdapterDateFns}>
-                <TimeSelector time={curDateStr} active={false} analogic={true} />
-            </LocalizationProvider>
-        );
-        const input = document.querySelector("input");
-        expect(input).toBeInTheDocument();
-        expect(input).toBeDisabled();
-    });
-    it("is enabled by default", async () => {
-        render(
-            <LocalizationProvider dateAdapter={AdapterDateFns}>
-                <TimeSelector time={curDateStr} analogic={true} />
-            </LocalizationProvider>
-        );
-        const input = document.querySelector("input");
-        expect(input).toBeInTheDocument();
-        expect(input).not.toBeDisabled();
-    });
-    it("is enabled by active", async () => {
-        render(
-            <LocalizationProvider dateAdapter={AdapterDateFns}>
-                <TimeSelector time={curDateStr} active={true} analogic={true} />
-            </LocalizationProvider>
-        );
-        const input = document.querySelector("input");
-        expect(input).toBeInTheDocument();
-        expect(input).not.toBeDisabled();
+    describe("TimeSelector component with analogue time picker", () => {
+        it("renders", async () => {
+            const { getByTestId } = render(
+                <LocalizationProvider dateAdapter={AdapterDateFns}>
+                    <TimeSelector time={curDateStr} analogic={true} />
+                </LocalizationProvider>
+            );
+            const input = document.querySelector("input");
+            expect(input).toBeInTheDocument();
+        });
+        it("displays the default value", async () => {
+            render(
+                <LocalizationProvider dateAdapter={AdapterDateFns}>
+                    <TimeSelector
+                        defaultTime="2001-01-01T01:01:01"
+                        time={undefined as unknown as string}
+                        analogic={true}
+                    />
+                </LocalizationProvider>
+            );
+            const input = document.querySelector("input");
+            expect(input).toBeInTheDocument();
+            expect(cleanText(input?.value || "").toLocaleLowerCase()).toEqual("01:01 am");
+        });
+        it("displays the default value with format", async () => {
+            render(
+                <LocalizationProvider dateAdapter={AdapterDateFns}>
+                    <TimeSelector
+                        defaultTime="2001-01-01T14:20:01"
+                        time={undefined as unknown as string}
+                        analogic={true}
+                        format="hh aa"
+                    />
+                </LocalizationProvider>
+            );
+            const input = document.querySelector("input");
+            expect(input).toBeInTheDocument();
+            expect(cleanText(input?.value || "")).toEqual("02 PM");
+        });
+
+        it("is disabled", async () => {
+            render(
+                <LocalizationProvider dateAdapter={AdapterDateFns}>
+                    <TimeSelector time={curDateStr} active={false} analogic={true} />
+                </LocalizationProvider>
+            );
+            const input = document.querySelector("input");
+            expect(input).toBeInTheDocument();
+            expect(input).toBeDisabled();
+        });
+        it("is enabled by default", async () => {
+            render(
+                <LocalizationProvider dateAdapter={AdapterDateFns}>
+                    <TimeSelector time={curDateStr} analogic={true} />
+                </LocalizationProvider>
+            );
+            const input = document.querySelector("input");
+            expect(input).toBeInTheDocument();
+            expect(input).not.toBeDisabled();
+        });
+        it("is enabled by active", async () => {
+            render(
+                <LocalizationProvider dateAdapter={AdapterDateFns}>
+                    <TimeSelector time={curDateStr} active={true} analogic={true} />
+                </LocalizationProvider>
+            );
+            const input = document.querySelector("input");
+            expect(input).toBeInTheDocument();
+            expect(input).not.toBeDisabled();
+        });
     });
 });

+ 9 - 9
frontend/taipy-gui/src/components/Taipy/TreeView.spec.tsx

@@ -88,19 +88,19 @@ describe("TreeView Component", () => {
         expect(elt2.parentElement).toHaveClass("Mui-selected");
     });
     it("is disabled", async () => {
-        const { getAllByRole } = render(<TreeView lov={lov} active={false} />);
-        const elts = getAllByRole("treeitem");
-        elts.forEach((elt) => expect(elt.firstElementChild).toHaveClass("Mui-disabled"));
+        const { getByText } = render(<TreeView lov={lov} active={false} />);
+        const elt = getByText("Item 1");
+        expect(elt).toHaveClass("Mui-disabled");
     });
     it("is enabled by default", async () => {
-        const { getAllByRole } = render(<TreeView lov={lov} />);
-        const elts = getAllByRole("treeitem");
-        elts.forEach((elt) => expect(elt.firstElementChild).not.toHaveClass("Mui-disabled"));
+        const { getByText } = render(<TreeView lov={lov} />);
+        const elt = getByText("Item 1");
+        expect(elt).not.toHaveClass("Mui-disabled");
     });
     it("is enabled by active", async () => {
-        const { getAllByRole } = render(<TreeView lov={lov} active={true} />);
-        const elts = getAllByRole("treeitem");
-        elts.forEach((elt) => expect(elt.firstElementChild).not.toHaveClass("Mui-disabled"));
+        const { getByText } = render(<TreeView lov={lov} active={true} />);
+        const elt = getByText("Item 1");
+        expect(elt).not.toHaveClass("Mui-disabled");
     });
     it("dispatch a well formed message base", async () => {
         const dispatch = jest.fn();

+ 92 - 93
frontend/taipy-gui/src/components/Taipy/TreeView.tsx

@@ -17,19 +17,21 @@ import React, {
     useEffect,
     useMemo,
     SyntheticEvent,
-    HTMLAttributes,
     forwardRef,
     Ref,
     CSSProperties,
+    HTMLAttributes,
+    ReactNode,
 } from "react";
-import Box from "@mui/material/Box";
-import { SimpleTreeView as MuiTreeView } from "@mui/x-tree-view/SimpleTreeView";
 import ChevronRightIcon from "@mui/icons-material/ChevronRight";
-import { TreeItem, TreeItemContentProps, useTreeItemState, TreeItemProps } from "@mui/x-tree-view/TreeItem";
+import Box from "@mui/material/Box";
 import Paper from "@mui/material/Paper";
 import TextField from "@mui/material/TextField";
 import Tooltip from "@mui/material/Tooltip";
-import Typography from "@mui/material/Typography";
+import { RichTreeView } from "@mui/x-tree-view/RichTreeView";
+import { TreeItem, TreeItemProps } from "@mui/x-tree-view/TreeItem";
+import { TreeViewBaseItem } from "@mui/x-tree-view/models";
+import { useTreeItemModel } from "@mui/x-tree-view/hooks";
 
 import { createSendUpdateAction } from "../../context/taipyReducers";
 import { isLovParent, LovImage, paperBaseSx, SelTreeProps, showItem, useLovListMemo } from "./lovUtils";
@@ -45,92 +47,70 @@ import { getUpdateVar } from "./utils";
 import { Icon } from "../../utils/icon";
 import { getComponentClassName } from "./TaipyStyle";
 
-const treeSlots = { expandIcon: ChevronRightIcon };
-
-const CustomContent = forwardRef(function CustomContent(props: TreeItemContentProps, ref) {
-    // need a display name
-    const { classes, className, label, itemId, icon: iconProp, expansionIcon, displayIcon } = props;
-    const { allowSelection, lovIcon, height } = props as unknown as CustomTreeProps;
-
-    const { disabled, expanded, selected, focused, handleExpansion, handleSelection, preventSelection } =
-        useTreeItemState(itemId);
+type TreeItemWithLabel = {
+    id: string;
+    label: string;
+    lovIcon?: Icon;
+    height?: string;
+    allowSelection?: boolean;
+};
 
-    const icon = iconProp || expansionIcon || displayIcon;
+interface CustomTreeProps extends HTMLAttributes<HTMLElement> {
+    children?: ReactNode;
+    className?: string;
+    lovIcon?: Icon;
+    height?: string;
+    disabled?: boolean;
+}
 
-    const classNames = [className, classes.root];
-    if (expanded) {
-        classNames.push(classes.expanded);
-    }
-    if (selected) {
-        classNames.push(classes.selected);
-    }
-    if (allowSelection && focused) {
-        classNames.push(classes.focused);
-    }
-    if (disabled) {
-        classNames.push(classes.disabled);
-    }
-    const divStyle = useMemo(() => (height ? { height: height } : undefined), [height]);
+const CustomLabel = (props: CustomTreeProps) => {
+    // need a display name
+    const { lovIcon, height } = props;
 
     return (
-        <div
-            className={classNames.join(" ")}
-            onMouseDown={preventSelection}
-            ref={ref as Ref<HTMLDivElement>}
-            style={divStyle}
-        >
-            <div onClick={handleExpansion} className={classes.iconContainer}>
-                {icon}
-            </div>
-            <Typography
-                onClick={allowSelection ? handleSelection : handleExpansion}
-                component="div"
-                className={classes.label}
-            >
-                {lovIcon ? <LovImage item={lovIcon} disableTypo={true} height={height} /> : label}
-            </Typography>
+        <div className={`${props.className}${props.disabled? " Mui-disabled": ""}`}>
+            {lovIcon ? <LovImage item={lovIcon} disableTypo={true} height={height} /> : props.children}
         </div>
     );
-});
+};
 
-interface CustomTreeProps extends HTMLAttributes<HTMLElement> {
-    allowSelection: boolean;
-    lovIcon?: Icon;
-    height?: string;
-}
+const CustomTreeItem = forwardRef(function CustomTreeItem(props: TreeItemProps, ref: Ref<HTMLLIElement>) {
+    const item = useTreeItemModel<TreeItemWithLabel>(props.itemId)!;
+    const { lovIcon, height, allowSelection = true } = item;
+    const ctProps = { lovIcon, height, disabled: props.disabled } as CustomTreeProps;
+    return <TreeItem {...props} data-selectable={allowSelection} ref={ref} slots={{ label: CustomLabel }} slotProps={{ label: ctProps }} />;
+});
 
-const CustomTreeItem = (props: TreeItemProps & CustomTreeProps) => {
-    const { allowSelection, lovIcon, height, ...tiProps } = props;
-    const ctProps = { allowSelection, lovIcon, height } as CustomTreeProps;
-    return <TreeItem ContentComponent={CustomContent} ContentProps={ctProps} {...tiProps} />;
-};
+const treeSlots = { expandIcon: ChevronRightIcon, item: CustomTreeItem };
 
 const renderTree = (
     lov: LovItem[],
-    active: boolean,
     searchValue: string,
     selectLeafsOnly: boolean,
-    rowHeight?: string
+    rowHeight?: string,
+    forbidSelections: Record<string, true> = {}
 ) => {
-    return lov.map((li) => {
-        const children = li.children ? renderTree(li.children, active, searchValue, selectLeafsOnly, rowHeight) : [];
-        if (!children.filter((c) => c).length && !showItem(li, searchValue)) {
-            return null;
-        }
-        return (
-            <CustomTreeItem
-                key={li.id}
-                itemId={li.id}
-                label={typeof li.item === "string" ? li.item : "undefined item"}
-                disabled={!active}
-                allowSelection={selectLeafsOnly ? !children || children.length == 0 : true}
-                lovIcon={typeof li.item !== "string" ? (li.item as Icon) : undefined}
-                height={rowHeight}
-            >
-                {children}
-            </CustomTreeItem>
-        );
-    });
+    const items = lov
+        .map((li) => {
+            const [children] = li.children
+                ? renderTree(li.children, searchValue, selectLeafsOnly, rowHeight, forbidSelections)
+                : [[]];
+            if (!children.length && !showItem(li, searchValue)) {
+                return null;
+            }
+            if (selectLeafsOnly && children.length) {
+                forbidSelections[li.id] = true;
+            }
+            return {
+                id: li.id,
+                label: typeof li.item === "string" ? li.item : "undefined item",
+                lovIcon: typeof li.item !== "string" ? (li.item as Icon) : undefined,
+                height: rowHeight,
+                children,
+            } as TreeViewBaseItem<TreeItemWithLabel>;
+        })
+        .filter((c) => c) as TreeViewBaseItem<TreeItemWithLabel>[];
+    return [items, forbidSelections] as [TreeViewBaseItem<TreeItemWithLabel>[], Record<string, true>];
 };
 
 const boxSx = { width: "100%" } as CSSProperties;
@@ -173,6 +153,14 @@ const TreeView = (props: TreeViewProps) => {
     const active = useDynamicProperty(props.active, props.defaultActive, true);
     const hover = useDynamicProperty(props.hoverText, props.defaultHoverText, undefined);
 
+    const slotProps = useMemo(
+        () => ({
+            item: {
+                disabled: !active,
+            },
+        }),
+        [active]
+    );
     useDispatchRequestUpdateOnFirstRender(dispatch, id, module, updateVars, updateVarName);
 
     const lovList = useLovListMemo(lov, defaultLov, true);
@@ -185,6 +173,11 @@ const TreeView = (props: TreeViewProps) => {
         return { ...sx, overflow: "hidden", py: 1 };
     }, [height]);
 
+    const [items, forbidSelections] = useMemo(
+        () => renderTree(lovList, searchValue, selectLeafsOnly, rowHeight),
+        [lovList, searchValue, selectLeafsOnly, rowHeight]
+    );
+
     useEffect(() => {
         let refExp = false;
         let oneExp = false;
@@ -237,14 +230,24 @@ const TreeView = (props: TreeViewProps) => {
     }, [defaultValue, value, multiple]);
 
     const clickHandler = useCallback(
-        (event: SyntheticEvent, nodeIds: string[] | string | null) => {
+        (event: SyntheticEvent | null, nodeIds: string[] | string | null) => {
             const ids = nodeIds === null ? [] : Array.isArray(nodeIds) ? nodeIds : [nodeIds];
-            setSelectedValue(ids);
+            const allowedIds = ids.filter((id) => !forbidSelections[id]);
+            if (multiple) {
+                if (allowedIds.length) {
+                    setSelectedValue(allowedIds);
+                }
+            } else if (allowedIds.length) {
+                setSelectedValue(allowedIds);
+            }
+            if (!allowedIds.length && (ids.length || !multiple)) {
+                return;
+            }
             updateVarName &&
                 dispatch(
                     createSendUpdateAction(
                         updateVarName,
-                        ids,
+                        allowedIds,
                         module,
                         props.onChange,
                         propagate,
@@ -252,13 +255,13 @@ const TreeView = (props: TreeViewProps) => {
                     )
                 );
         },
-        [updateVarName, dispatch, propagate, updateVars, valueById, props.onChange, module]
+        [forbidSelections, multiple, updateVarName, dispatch, propagate, updateVars, valueById, props.onChange, module]
     );
 
     const handleInput = useCallback((e: React.ChangeEvent<HTMLInputElement>) => setSearchValue(e.target.value), []);
 
     const handleNodeToggle = useCallback(
-        (event: React.SyntheticEvent, nodeIds: string[]) => {
+        (event: React.SyntheticEvent | null, nodeIds: string[]) => {
             const expVar = getUpdateVar(updateVars, "expanded");
             if (oneExpanded) {
                 setExpandedNodes((en) => {
@@ -281,11 +284,6 @@ const TreeView = (props: TreeViewProps) => {
         [oneExpanded, refreshExpanded, lovList, propagate, updateVars, dispatch, props.onChange, module]
     );
 
-    const treeProps = useMemo(
-        () => ({ multiSelect: multiple, selectedItems: selectedValue }),
-        [multiple, selectedValue]
-    );
-
     return (
         <Box id={id} sx={boxSx} className={`${className} ${getComponentClassName(props.children)}`}>
             <Tooltip title={hover || ""}>
@@ -302,17 +300,18 @@ const TreeView = (props: TreeViewProps) => {
                             />
                         )}
                     </Box>
-                    <MuiTreeView
-                        aria-label="tree"
+                    <RichTreeView
                         slots={treeSlots}
+                        slotProps={slotProps}
                         sx={treeSx}
                         onSelectedItemsChange={clickHandler}
                         expandedItems={expandedNodes}
                         onExpandedItemsChange={handleNodeToggle}
-                        {...treeProps}
-                    >
-                        {renderTree(lovList, !!active, searchValue, selectLeafsOnly, rowHeight)}
-                    </MuiTreeView>
+                        multiSelect={multiple}
+                        selectedItems={selectedValue}
+                        items={items}
+                        disableSelection={!active}
+                    />
                 </Paper>
             </Tooltip>
             {props.children}

+ 2 - 2
frontend/taipy-gui/src/components/Taipy/tableUtils.tsx

@@ -54,7 +54,7 @@ import { TaipyActiveProps, TaipyMultiSelectProps, getSuffixedClassNames } from "
 export const generateHeaderClassName = (columnName: string | undefined): string => {
     // logic for the css header classname
     if (!columnName) {
-        // return an empty string if columname is undefined or empty
+        // return an empty string if columnName is undefined or empty
         return "";
     }
     return "-" + columnName.replace(/\W+/g, "-").replace(/-+/g, "-").toLowerCase();
@@ -392,7 +392,7 @@ export const getColumnHeader = (columns: Record<string, ColumnDesc>, columnKey:
 
 const setInputFocus = (input: HTMLInputElement) => input && input.focus();
 
-const textFieldProps = { textField: { margin: "dense" } } as BaseDateTimePickerSlotProps<Date>;
+const textFieldProps = { textField: { margin: "dense" } } as BaseDateTimePickerSlotProps;
 
 const filter = createFilterOptions<string>();
 const getOptionKey = (option: string) => (Array.isArray(option) ? option[0] : option);

+ 1 - 0
frontend/taipy-gui/src/utils/image.ts

@@ -15,6 +15,7 @@ export const toDataUrl = (url: string | null) =>
     new Promise((resolve, reject) => {
         if (!url) {
             resolve(null);
+            return
         }
         const xhr = new XMLHttpRequest();
         xhr.onload = () => {

+ 11 - 11
frontend/taipy-gui/src/workers/fileupload.worker.ts

@@ -32,17 +32,17 @@ const uploadFile = (
     xhr.open("POST", `${uploadUrl}?client_id=${id}`, false);
     xhr.onerror = (e) => self.postMessage({ message: "Error: " + e, error: true });
     xhr.onload = (e) => progressCb(e.lengthComputable ? e.loaded : 0);
-    const fdata = new FormData();
-    fdata.append("blob", blobOrFile, fileName);
-    fdata.append("part", part.toString());
-    fdata.append("total", total.toString());
-    fdata.append("var_name", varName);
-    context && fdata.append("context", context);
-    onAction && fdata.append("on_action", onAction);
-    uploadData && fdata.append("upload_data", uploadData);
-    fdata.append("multiple", multiple ? "True" : "False");
-    fdata.append("path", filePath)
-    xhr.send(fdata);
+    const formData = new FormData();
+    formData.append("blob", blobOrFile, fileName);
+    formData.append("part", part.toString());
+    formData.append("total", total.toString());
+    formData.append("var_name", varName);
+    context && formData.append("context", context);
+    onAction && formData.append("on_action", onAction);
+    uploadData && formData.append("upload_data", uploadData);
+    formData.append("multiple", multiple ? "True" : "False");
+    formData.append("path", filePath)
+    xhr.send(formData);
 };
 
 // 1MB chunk sizes.

+ 18 - 0
frontend/taipy-gui/test-config/errorBoundary.js

@@ -0,0 +1,18 @@
+/*
+ * Copyright 2021-2025 Avaiga Private Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+jest.mock("react-error-boundary", () => ({
+    ErrorBoundary:
+        () =>
+        ({ children }) =>
+            children,
+}));

+ 7 - 0
frontend/taipy-gui/test-config/errorBoundary.tsx

@@ -0,0 +1,7 @@
+import React, { ReactNode } from "react";
+
+interface ChildrenProps {
+    children: ReactNode;
+}
+
+export const ErrorBoundary = ({ children }: ChildrenProps) => <>{children}</>;

+ 1 - 3
frontend/taipy-gui/test-config/markdown.tsx

@@ -4,8 +4,6 @@ interface ChildrenProps {
     children: ReactNode;
 }
 
-function ReactMarkdownMock({ children }: ChildrenProps) {
-    return <p>{children}</p>;
-}
+const ReactMarkdownMock = ({ children }: ChildrenProps) => <p>{children}</p>;
 
 export default ReactMarkdownMock;

+ 1 - 3
frontend/taipy-gui/test-config/nanoid.js

@@ -13,6 +13,4 @@
 
 // mock nanoid that is ESM and does not work with jest
 // https://github.com/ai/nanoid/issues/363
-jest.mock("nanoid", () => { return {
-    nanoid : ()=>{}
-  } });
+jest.mock("nanoid", () => ({ nanoid: () => {} }));

+ 5 - 0
frontend/taipy-gui/webpack.config.js

@@ -110,11 +110,16 @@ module.exports = (env, options) => {
         },
         {
             mode: options.mode, //'development', //'production',
+            name: "TaipyGuiDom",
             context: resolveApp("dom"),
             entry: ["./src/index.tsx"],
             output: {
                 filename: "taipy-gui-dom.js",
                 path: webAppPath,
+                library: {
+                    name: "TaipyGuiDom",
+                    type: "umd",
+                },
                 publicPath: "",
             },
             dependencies: [taipyBundleName, reactBundleName],

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 95 - 554
frontend/taipy/package-lock.json


+ 8 - 5
frontend/taipy/package.json

@@ -1,6 +1,6 @@
 {
   "name": "taipy-gui-core",
-  "version": "4.1.0",
+  "version": "4.2.0",
   "private": true,
   "devDependencies": {
     "@eslint/compat": "^1.2.7",
@@ -32,10 +32,10 @@
   "dependencies": {
     "@emotion/react": "^11.10.6",
     "@emotion/styled": "^11.10.8",
-    "@mui/icons-material": "^6.0.1",
-    "@mui/material": "^6.0.1",
-    "@mui/x-date-pickers": "^7.0.0",
-    "@mui/x-tree-view": "^7.0.0",
+    "@mui/icons-material": "^7.1.0",
+    "@mui/material": "^7.1.0",
+    "@mui/x-date-pickers": "^8.3.0",
+    "@mui/x-tree-view": "^8.3.0",
     "@projectstorm/react-diagrams": "^7.0.2",
     "@textea/json-viewer": "^4.0.0",
     "date-fns": "^4.1.0",
@@ -45,6 +45,9 @@
     "react-dom": "^18.2.0",
     "taipy-gui": "file:../../taipy/gui/webapp"
   },
+  "overrides": {
+    "@mui/material": "$@mui/material"
+  },
   "scripts": {
     "postinstall": "node scripts/install.js",
     "build:dev": "webpack --mode development",

+ 6 - 6
frontend/taipy/src/CoreSelector.tsx

@@ -24,7 +24,7 @@ import React, {
 import { TextField, Theme, alpha } from "@mui/material";
 import Badge, { BadgeOrigin } from "@mui/material/Badge";
 import FormControlLabel from "@mui/material/FormControlLabel";
-import Grid from "@mui/material/Grid2";
+import Grid from "@mui/material/Grid";
 import IconButton from "@mui/material/IconButton";
 import Switch from "@mui/material/Switch";
 import Tooltip from "@mui/material/Tooltip";
@@ -71,7 +71,7 @@ import {
     BaseTreeViewSx,
     CoreProps,
     FlagSx,
-    ParentItemSx,
+    // ParentItemSx,
 } from "./utils";
 
 export interface EditProps {
@@ -205,7 +205,7 @@ const CoreItem = (props: {
                     ) : null}
                 </Grid>
             }
-            sx={nodeType === NodeType.NODE ? undefined : ParentItemSx}
+            // sx={nodeType === NodeType.NODE ? undefined : ParentItemSx}
         >
             {items
                 ? items.filter(v => v).map((item) => (
@@ -352,7 +352,7 @@ const CoreSelector = (props: CoreSelectorProps) => {
 
     useDispatchRequestUpdateOnFirstRender(dispatch, id, module, updateVars, undefined, true);
 
-    const onItemExpand = useCallback((e: SyntheticEvent, itemId: string, expanded: boolean) => {
+    const onItemExpand = useCallback((e: SyntheticEvent | null, itemId: string, expanded: boolean) => {
         setExpandedItems((old) => {
             if (!expanded) {
                 return old.filter((id) => id != itemId);
@@ -362,8 +362,8 @@ const CoreSelector = (props: CoreSelectorProps) => {
     }, []);
 
     const onNodeSelect = useCallback(
-        (e: SyntheticEvent, nodeId: string | string[] | null) => {
-            const { selectable = "false" } = e.currentTarget.parentElement?.dataset || {};
+        (e: SyntheticEvent | null, nodeId: string | string[] | null) => {
+            const { selectable = "false" } = e?.currentTarget.parentElement?.dataset || {};
             const isSelectable = selectable === "true";
             if (!isSelectable && multiple) {
                 return;

+ 1 - 1
frontend/taipy/src/DataNodeChart.tsx

@@ -23,7 +23,7 @@ import Box from "@mui/material/Box";
 import Button from "@mui/material/Button";
 import FormControl from "@mui/material/FormControl";
 import FormControlLabel from "@mui/material/FormControlLabel";
-import Grid from "@mui/material/Grid2";
+import Grid from "@mui/material/Grid";
 import IconButton from "@mui/material/IconButton";
 import InputLabel from "@mui/material/InputLabel";
 import OutlinedInput from "@mui/material/OutlinedInput";

+ 1 - 1
frontend/taipy/src/DataNodeTable.tsx

@@ -22,7 +22,7 @@ import Button from "@mui/material/Button";
 import Checkbox from "@mui/material/Checkbox";
 import FormControl from "@mui/material/FormControl";
 import FormControlLabel from "@mui/material/FormControlLabel";
-import Grid from "@mui/material/Grid2";
+import Grid from "@mui/material/Grid";
 import InputLabel from "@mui/material/InputLabel";
 import OutlinedInput from "@mui/material/OutlinedInput";
 import ListItemText from "@mui/material/ListItemText";

+ 4 - 4
frontend/taipy/src/DataNodeViewer.tsx

@@ -32,7 +32,7 @@ import Alert from "@mui/material/Alert";
 import Box from "@mui/material/Box";
 import Button from "@mui/material/Button";
 import Divider from "@mui/material/Divider";
-import Grid from "@mui/material/Grid2";
+import Grid from "@mui/material/Grid";
 import IconButton from "@mui/material/IconButton";
 import InputAdornment from "@mui/material/InputAdornment";
 import Popover from "@mui/material/Popover";
@@ -50,12 +50,12 @@ import CheckCircle from "@mui/icons-material/CheckCircle";
 import Download from "@mui/icons-material/Download";
 import Launch from "@mui/icons-material/Launch";
 import LockOutlined from "@mui/icons-material/LockOutlined";
-import Upload from "@mui/icons-material/Upload";
+import {Upload} from "@mui/icons-material"; // issue with Mui not exporting Upload icon in the latest version
 
 import { DateTimePicker } from "@mui/x-date-pickers/DateTimePicker";
 import { BaseDateTimePickerSlotProps } from "@mui/x-date-pickers/DateTimePicker/shared";
 import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
-import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFnsV3";
+import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns";
 import { format } from "date-fns";
 import deepEqual from "fast-deep-equal/es6";
 
@@ -111,7 +111,7 @@ const editSx = {
     fontSize: "smaller",
     "& > div": { writingMode: "vertical-rl", transform: "rotate(180deg)", paddingBottom: "1em" },
 };
-const textFieldProps = { textField: { margin: "dense" } } as BaseDateTimePickerSlotProps<Date>;
+const textFieldProps = { textField: { margin: "dense" } } as BaseDateTimePickerSlotProps;
 const buttonSx = { minWidth: "0px" };
 
 type DataNodeFull = [

+ 2 - 2
frontend/taipy/src/JobSelector.tsx

@@ -25,11 +25,11 @@ import Box from "@mui/material/Box";
 import Button from "@mui/material/Button";
 import Checkbox from "@mui/material/Checkbox";
 import Dialog from "@mui/material/Dialog";
-import DialogActions from "@mui/material//DialogActions";
+import DialogActions from "@mui/material/DialogActions";
 import DialogContent from "@mui/material/DialogContent";
 import DialogTitle from "@mui/material/DialogTitle";
 import FormControl from "@mui/material/FormControl";
-import Grid from "@mui/material/Grid2";
+import Grid from "@mui/material/Grid";
 import IconButton from "@mui/material/IconButton";
 import InputLabel from "@mui/material/InputLabel";
 import ListItemText from "@mui/material/ListItemText";

+ 1 - 1
frontend/taipy/src/JobViewer.tsx

@@ -14,7 +14,7 @@
 import React, { useEffect, useCallback } from "react";
 import Button from "@mui/material/Button";
 import Divider from "@mui/material/Divider";
-import Grid from "@mui/material/Grid2";
+import Grid from "@mui/material/Grid";
 import ListItemText from "@mui/material/ListItemText";
 import Tooltip from "@mui/material/Tooltip";
 import Typography from "@mui/material/Typography";

+ 1 - 1
frontend/taipy/src/PropertiesEditor.tsx

@@ -13,7 +13,7 @@
 
 import React, { useState, useCallback, useEffect, ChangeEvent, MouseEvent, KeyboardEvent } from "react";
 import Divider from "@mui/material/Divider";
-import Grid from "@mui/material/Grid2";
+import Grid from "@mui/material/Grid";
 import IconButton from "@mui/material/IconButton";
 import TextField from "@mui/material/TextField";
 import Tooltip from "@mui/material/Tooltip";

+ 2 - 2
frontend/taipy/src/ScenarioSelector.tsx

@@ -24,7 +24,7 @@ import DialogTitle from "@mui/material/DialogTitle";
 import FormControl from "@mui/material/FormControl";
 import FormGroup from "@mui/material/FormGroup";
 import FormHelperText from "@mui/material/FormHelperText";
-import Grid from "@mui/material/Grid2";
+import Grid from "@mui/material/Grid";
 import IconButton from "@mui/material/IconButton";
 import InputLabel from "@mui/material/InputLabel";
 import MenuItem from "@mui/material/MenuItem";
@@ -33,7 +33,7 @@ import Stack from "@mui/material/Stack";
 import TextField from "@mui/material/TextField";
 import Typography from "@mui/material/Typography";
 import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers";
-import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFnsV3";
+import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns";
 import { useFormik } from "formik";
 
 import {

+ 1 - 1
frontend/taipy/src/ScenarioViewer.tsx

@@ -20,7 +20,7 @@ import Chip from "@mui/material/Chip";
 import Box from "@mui/material/Box";
 import Button from "@mui/material/Button";
 import Divider from "@mui/material/Divider";
-import Grid from "@mui/material/Grid2";
+import Grid from "@mui/material/Grid";
 import IconButton from "@mui/material/IconButton";
 import InputAdornment from "@mui/material/InputAdornment";
 import Stack from "@mui/material/Stack";

+ 1 - 1
frontend/taipy/src/utils/ConfirmDialog.tsx

@@ -4,7 +4,7 @@ import DialogActions from "@mui/material/DialogActions";
 import DialogContent from "@mui/material/DialogContent";
 import DialogTitle from "@mui/material/DialogTitle";
 import Button from "@mui/material/Button";
-import Grid from "@mui/material/Grid2";
+import Grid from "@mui/material/Grid";
 import IconButton from "@mui/material/IconButton";
 import Tooltip from "@mui/material/Tooltip";
 import Typography from "@mui/material/Typography";

+ 4 - 0
taipy/gui/utils/datatype.py

@@ -13,9 +13,13 @@ import re
 
 import pandas as pd
 
+from ._map_dict import _MapDict
+
 
 def _get_data_type(value):
     if not isinstance(value, str):
+        if isinstance(value, (dict, _MapDict)):
+            return "dict"
         if pd.api.types.is_bool_dtype(value):
             return "bool"
         elif pd.api.types.is_integer_dtype(value):

+ 47 - 0
tests/gui/utils/test_datatype.py

@@ -0,0 +1,47 @@
+# Copyright 2021-2025 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+
+
+from taipy.gui.utils._map_dict import _MapDict
+from taipy.gui.utils.datatype import _get_data_type
+
+
+def test_datatype_str():
+    ret = _get_data_type("a string")
+    assert ret == "str"
+
+
+def test_datatype_dict():
+    a_dict = {"a": "b", "c": "d"}
+    ret = _get_data_type(a_dict)
+    assert ret == "dict"
+    ret = _get_data_type(_MapDict(a_dict))
+    assert ret == "dict"
+
+
+def test_datatype_bool():
+    ret = _get_data_type(True)
+    assert ret == "bool"
+
+
+def test_datatype_int():
+    ret = _get_data_type(42)
+    assert ret == "int"
+
+
+def test_datatype_float():
+    ret = _get_data_type(3.14)
+    assert ret == "float"
+
+
+def test_datatype_none():
+    ret = _get_data_type(None)
+    assert ret == "NoneType"

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно