Explorar el Código

make data tab first in datanode viewer (#843)

* make data tab first in datanode viewer
- handle Series in pandas data accessor
- handle float.nan as much as possible
resolves #745

* display NaN

---------

Co-authored-by: Fred Lefévère-Laoide <Fred.Lefevere-Laoide@Taipy.io>
Fred Lefévère-Laoide hace 1 año
padre
commit
701fabe543

+ 60 - 60
frontend/taipy-gui/package-lock.json

@@ -1523,9 +1523,9 @@
       }
     },
     "node_modules/@jridgewell/resolve-uri": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
-      "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
       "dev": true,
       "engines": {
         "node": ">=6.0.0"
@@ -2134,9 +2134,9 @@
       "dev": true
     },
     "node_modules/@sindresorhus/merge-streams": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.2.0.tgz",
-      "integrity": "sha512-UTce8mUwUW0RikMb/eseJ7ys0BRkZVFB86orHzrfW12ZmFtym5zua8joZ4L7okH2dDFHkcFjqnZ5GocWBXOFtA==",
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.2.1.tgz",
+      "integrity": "sha512-255V7MMIKw6aQ43Wbqp9HZ+VHn6acddERTLiiLnlcPLU9PdTq9Aijl12oklAgUEblLWye+vHLzmqBx6f2TGcZw==",
       "dev": true,
       "engines": {
         "node": ">=18"
@@ -3844,9 +3844,9 @@
       }
     },
     "node_modules/browserslist": {
-      "version": "4.22.3",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.3.tgz",
-      "integrity": "sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==",
+      "version": "4.23.0",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
+      "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
       "funding": [
         {
           "type": "opencollective",
@@ -3862,8 +3862,8 @@
         }
       ],
       "dependencies": {
-        "caniuse-lite": "^1.0.30001580",
-        "electron-to-chromium": "^1.4.648",
+        "caniuse-lite": "^1.0.30001587",
+        "electron-to-chromium": "^1.4.668",
         "node-releases": "^2.0.14",
         "update-browserslist-db": "^1.0.13"
       },
@@ -4537,9 +4537,9 @@
       }
     },
     "node_modules/core-js": {
-      "version": "3.35.1",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.35.1.tgz",
-      "integrity": "sha512-IgdsbxNyMskrTFxa9lWHyMwAJU5gXOPP+1yO+K59d50VLVAIDAbs7gIv705KzALModfK3ZrSZTPNpC0PQgIZuw==",
+      "version": "3.36.0",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.36.0.tgz",
+      "integrity": "sha512-mt7+TUBbTFg5+GngsAxeKBTl5/VS0guFeJacYge9OmHb+m058UwwIm41SE9T4Den7ClatV57B6TYTuJ0CX1MAw==",
       "hasInstallScript": true,
       "funding": {
         "type": "opencollective",
@@ -5063,15 +5063,14 @@
       }
     },
     "node_modules/define-data-property": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.3.tgz",
-      "integrity": "sha512-h3GBouC+RPtNX2N0hHVLo2ZwPYurq8mLmXpOLTsw71gr7lHt5VaI4vVkDUNOfiWmm48JEXe3VM7PmLX45AMmmg==",
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+      "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
       "dev": true,
       "dependencies": {
+        "es-define-property": "^1.0.0",
         "es-errors": "^1.3.0",
-        "get-intrinsic": "^1.2.4",
-        "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.1"
+        "gopd": "^1.0.1"
       },
       "engines": {
         "node": ">= 0.4"
@@ -5379,9 +5378,9 @@
       "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ=="
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.667",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.667.tgz",
-      "integrity": "sha512-66L3pLlWhTNVUhnmSA5+qDM3fwnXsM6KAqE36e2w4KN0g6pkEtlT5bs41FQtQwVwKnfhNBXiWRLPs30HSxd7Kw=="
+      "version": "1.4.672",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.672.tgz",
+      "integrity": "sha512-YYCy+goe3UqZqa3MOQCI5Mx/6HdBLzXL/mkbGCEWL3sP3Z1BP9zqAzeD3YEmLZlespYGFtyM8tRp5i2vfaUGCA=="
     },
     "node_modules/element-size": {
       "version": "1.1.1",
@@ -5508,50 +5507,52 @@
       }
     },
     "node_modules/es-abstract": {
-      "version": "1.22.3",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz",
-      "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==",
+      "version": "1.22.4",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.4.tgz",
+      "integrity": "sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==",
       "dev": true,
       "dependencies": {
-        "array-buffer-byte-length": "^1.0.0",
-        "arraybuffer.prototype.slice": "^1.0.2",
-        "available-typed-arrays": "^1.0.5",
-        "call-bind": "^1.0.5",
-        "es-set-tostringtag": "^2.0.1",
+        "array-buffer-byte-length": "^1.0.1",
+        "arraybuffer.prototype.slice": "^1.0.3",
+        "available-typed-arrays": "^1.0.6",
+        "call-bind": "^1.0.7",
+        "es-define-property": "^1.0.0",
+        "es-errors": "^1.3.0",
+        "es-set-tostringtag": "^2.0.2",
         "es-to-primitive": "^1.2.1",
         "function.prototype.name": "^1.1.6",
-        "get-intrinsic": "^1.2.2",
-        "get-symbol-description": "^1.0.0",
+        "get-intrinsic": "^1.2.4",
+        "get-symbol-description": "^1.0.2",
         "globalthis": "^1.0.3",
         "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.0",
+        "has-property-descriptors": "^1.0.2",
         "has-proto": "^1.0.1",
         "has-symbols": "^1.0.3",
-        "hasown": "^2.0.0",
-        "internal-slot": "^1.0.5",
-        "is-array-buffer": "^3.0.2",
+        "hasown": "^2.0.1",
+        "internal-slot": "^1.0.7",
+        "is-array-buffer": "^3.0.4",
         "is-callable": "^1.2.7",
         "is-negative-zero": "^2.0.2",
         "is-regex": "^1.1.4",
         "is-shared-array-buffer": "^1.0.2",
         "is-string": "^1.0.7",
-        "is-typed-array": "^1.1.12",
+        "is-typed-array": "^1.1.13",
         "is-weakref": "^1.0.2",
         "object-inspect": "^1.13.1",
         "object-keys": "^1.1.1",
-        "object.assign": "^4.1.4",
-        "regexp.prototype.flags": "^1.5.1",
-        "safe-array-concat": "^1.0.1",
-        "safe-regex-test": "^1.0.0",
+        "object.assign": "^4.1.5",
+        "regexp.prototype.flags": "^1.5.2",
+        "safe-array-concat": "^1.1.0",
+        "safe-regex-test": "^1.0.3",
         "string.prototype.trim": "^1.2.8",
         "string.prototype.trimend": "^1.0.7",
         "string.prototype.trimstart": "^1.0.7",
-        "typed-array-buffer": "^1.0.0",
+        "typed-array-buffer": "^1.0.1",
         "typed-array-byte-length": "^1.0.0",
         "typed-array-byte-offset": "^1.0.0",
         "typed-array-length": "^1.0.4",
         "unbox-primitive": "^1.0.2",
-        "which-typed-array": "^1.1.13"
+        "which-typed-array": "^1.1.14"
       },
       "engines": {
         "node": ">= 0.4"
@@ -5602,21 +5603,21 @@
       }
     },
     "node_modules/es-iterator-helpers": {
-      "version": "1.0.16",
-      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.16.tgz",
-      "integrity": "sha512-CREG2A9Vq7bpDRnldhFcMKuKArvkZtsH6Y0DHOHVg49qhf+LD8uEdUM3OkOAICv0EziGtDEnQtqY2/mfBILpFw==",
+      "version": "1.0.17",
+      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.17.tgz",
+      "integrity": "sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==",
       "dev": true,
       "dependencies": {
         "asynciterator.prototype": "^1.0.0",
-        "call-bind": "^1.0.6",
+        "call-bind": "^1.0.7",
         "define-properties": "^1.2.1",
-        "es-abstract": "^1.22.3",
+        "es-abstract": "^1.22.4",
         "es-errors": "^1.3.0",
         "es-set-tostringtag": "^2.0.2",
         "function-bind": "^1.1.2",
         "get-intrinsic": "^1.2.4",
         "globalthis": "^1.0.3",
-        "has-property-descriptors": "^1.0.1",
+        "has-property-descriptors": "^1.0.2",
         "has-proto": "^1.0.1",
         "has-symbols": "^1.0.3",
         "internal-slot": "^1.0.7",
@@ -11742,9 +11743,9 @@
       }
     },
     "node_modules/regl-line2d": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/regl-line2d/-/regl-line2d-3.1.2.tgz",
-      "integrity": "sha512-nmT7WWS/WxmXAQMkgaMKWXaVmwJ65KCrjbqHGOUjjqQi6shfT96YbBOvelXwO9hG7/hjvbzjtQ2UO0L3e7YaXQ==",
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/regl-line2d/-/regl-line2d-3.1.3.tgz",
+      "integrity": "sha512-fkgzW+tTn4QUQLpFKsUIE0sgWdCmXAM3ctXcCgoGBZTSX5FE2A0M7aynz7nrZT5baaftLrk9te54B+MEq4QcSA==",
       "dependencies": {
         "array-bounds": "^1.0.1",
         "array-find-index": "^1.0.2",
@@ -11753,7 +11754,6 @@
         "earcut": "^2.1.5",
         "es6-weak-map": "^2.0.3",
         "flatten-vertex-data": "^1.0.2",
-        "glslify": "^7.0.0",
         "object-assign": "^4.1.1",
         "parse-rect": "^1.2.0",
         "pick-by-alias": "^1.2.0",
@@ -12867,9 +12867,9 @@
       }
     },
     "node_modules/terser": {
-      "version": "5.27.0",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz",
-      "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==",
+      "version": "5.27.1",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.1.tgz",
+      "integrity": "sha512-29wAr6UU/oQpnTw5HoadwjUZnFQXGdOfj0LjZ4sVxzqwHh/QVkvr7m8y9WoR4iN3FRitVduTc6KdjcW38Npsug==",
       "dev": true,
       "dependencies": {
         "@jridgewell/source-map": "^0.3.3",
@@ -13774,9 +13774,9 @@
       }
     },
     "node_modules/webpack": {
-      "version": "5.90.1",
-      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.1.tgz",
-      "integrity": "sha512-SstPdlAC5IvgFnhiRok8hqJo/+ArAbNv7rhU4fnWGHNVfN59HSQFaxZDSAL3IFG2YmqxuRs+IU33milSxbPlog==",
+      "version": "5.90.2",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.2.tgz",
+      "integrity": "sha512-ziXu8ABGr0InCMEYFnHrYweinHK2PWrMqnwdHk2oK3rRhv/1B+2FnfwYv5oD+RrknK/Pp/Hmyvu+eAsaMYhzCw==",
       "dev": true,
       "dependencies": {
         "@types/eslint-scope": "^3.7.3",

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

@@ -28,7 +28,7 @@ import { isValid } from "date-fns";
 
 import { FormatConfig } from "../../context/taipyReducers";
 import { dateToString, getDateTime, getDateTimeString, getNumberString, getTimeZonedDate } from "../../utils/index";
-import { TaipyActiveProps, TaipyMultiSelectProps } from "./utils";
+import { TaipyActiveProps, TaipyMultiSelectProps, getSuffixedClassNames } from "./utils";
 
 /**
  * A column description as received by the backend.
@@ -380,7 +380,13 @@ export const EditableCell = (props: EditableCellProps) => {
     }, [onValidation]);
 
     return (
-        <TableCell {...getCellProps(colDesc, tableCellProps)} className={className} title={tooltip}>
+        <TableCell
+            {...getCellProps(colDesc, tableCellProps)}
+            className={
+                onValidation ? getSuffixedClassNames(className || "tpc", edit ? "-editing" : "-editable") : className
+            }
+            title={tooltip}
+        >
             {edit ? (
                 colDesc.type?.startsWith("bool") ? (
                     <Box sx={cellBoxSx}>

+ 40 - 39
frontend/taipy/package-lock.json

@@ -2257,15 +2257,14 @@
       }
     },
     "node_modules/define-data-property": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.3.tgz",
-      "integrity": "sha512-h3GBouC+RPtNX2N0hHVLo2ZwPYurq8mLmXpOLTsw71gr7lHt5VaI4vVkDUNOfiWmm48JEXe3VM7PmLX45AMmmg==",
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+      "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
       "dev": true,
       "dependencies": {
+        "es-define-property": "^1.0.0",
         "es-errors": "^1.3.0",
-        "get-intrinsic": "^1.2.4",
-        "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.1"
+        "gopd": "^1.0.1"
       },
       "engines": {
         "node": ">= 0.4"
@@ -2325,9 +2324,9 @@
       }
     },
     "node_modules/dotenv": {
-      "version": "16.4.3",
-      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.3.tgz",
-      "integrity": "sha512-II98GFrje5psQTSve0E7bnwMFybNLqT8Vu8JIFWRjsE3khyNUm/loZupuy5DVzG2IXf/ysxvrixYOQnM6mjD3A==",
+      "version": "16.4.4",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.4.tgz",
+      "integrity": "sha512-XvPXc8XAQThSjAbY6cQ/9PcBXmFoWuw1sQ3b8HqUCR6ziGXjkTi//kB9SWa2UwqlgdAIuRqAa/9hVljzPehbYg==",
       "dev": true,
       "engines": {
         "node": ">=12"
@@ -2337,9 +2336,9 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.667",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.667.tgz",
-      "integrity": "sha512-66L3pLlWhTNVUhnmSA5+qDM3fwnXsM6KAqE36e2w4KN0g6pkEtlT5bs41FQtQwVwKnfhNBXiWRLPs30HSxd7Kw==",
+      "version": "1.4.668",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.668.tgz",
+      "integrity": "sha512-ZOBocMYCehr9W31+GpMclR+KBaDZOoAEabLdhpZ8oU1JFDwIaFY0UDbpXVEUFc0BIP2O2Qn3rkfCjQmMR4T/bQ==",
       "dev": true
     },
     "node_modules/enhanced-resolve": {
@@ -2376,50 +2375,52 @@
       }
     },
     "node_modules/es-abstract": {
-      "version": "1.22.3",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz",
-      "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==",
+      "version": "1.22.4",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.4.tgz",
+      "integrity": "sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==",
       "dev": true,
       "dependencies": {
-        "array-buffer-byte-length": "^1.0.0",
-        "arraybuffer.prototype.slice": "^1.0.2",
-        "available-typed-arrays": "^1.0.5",
-        "call-bind": "^1.0.5",
-        "es-set-tostringtag": "^2.0.1",
+        "array-buffer-byte-length": "^1.0.1",
+        "arraybuffer.prototype.slice": "^1.0.3",
+        "available-typed-arrays": "^1.0.6",
+        "call-bind": "^1.0.7",
+        "es-define-property": "^1.0.0",
+        "es-errors": "^1.3.0",
+        "es-set-tostringtag": "^2.0.2",
         "es-to-primitive": "^1.2.1",
         "function.prototype.name": "^1.1.6",
-        "get-intrinsic": "^1.2.2",
-        "get-symbol-description": "^1.0.0",
+        "get-intrinsic": "^1.2.4",
+        "get-symbol-description": "^1.0.2",
         "globalthis": "^1.0.3",
         "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.0",
+        "has-property-descriptors": "^1.0.2",
         "has-proto": "^1.0.1",
         "has-symbols": "^1.0.3",
-        "hasown": "^2.0.0",
-        "internal-slot": "^1.0.5",
-        "is-array-buffer": "^3.0.2",
+        "hasown": "^2.0.1",
+        "internal-slot": "^1.0.7",
+        "is-array-buffer": "^3.0.4",
         "is-callable": "^1.2.7",
         "is-negative-zero": "^2.0.2",
         "is-regex": "^1.1.4",
         "is-shared-array-buffer": "^1.0.2",
         "is-string": "^1.0.7",
-        "is-typed-array": "^1.1.12",
+        "is-typed-array": "^1.1.13",
         "is-weakref": "^1.0.2",
         "object-inspect": "^1.13.1",
         "object-keys": "^1.1.1",
-        "object.assign": "^4.1.4",
-        "regexp.prototype.flags": "^1.5.1",
-        "safe-array-concat": "^1.0.1",
-        "safe-regex-test": "^1.0.0",
+        "object.assign": "^4.1.5",
+        "regexp.prototype.flags": "^1.5.2",
+        "safe-array-concat": "^1.1.0",
+        "safe-regex-test": "^1.0.3",
         "string.prototype.trim": "^1.2.8",
         "string.prototype.trimend": "^1.0.7",
         "string.prototype.trimstart": "^1.0.7",
-        "typed-array-buffer": "^1.0.0",
+        "typed-array-buffer": "^1.0.1",
         "typed-array-byte-length": "^1.0.0",
         "typed-array-byte-offset": "^1.0.0",
         "typed-array-length": "^1.0.4",
         "unbox-primitive": "^1.0.2",
-        "which-typed-array": "^1.1.13"
+        "which-typed-array": "^1.1.14"
       },
       "engines": {
         "node": ">= 0.4"
@@ -2450,21 +2451,21 @@
       }
     },
     "node_modules/es-iterator-helpers": {
-      "version": "1.0.16",
-      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.16.tgz",
-      "integrity": "sha512-CREG2A9Vq7bpDRnldhFcMKuKArvkZtsH6Y0DHOHVg49qhf+LD8uEdUM3OkOAICv0EziGtDEnQtqY2/mfBILpFw==",
+      "version": "1.0.17",
+      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.17.tgz",
+      "integrity": "sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==",
       "dev": true,
       "dependencies": {
         "asynciterator.prototype": "^1.0.0",
-        "call-bind": "^1.0.6",
+        "call-bind": "^1.0.7",
         "define-properties": "^1.2.1",
-        "es-abstract": "^1.22.3",
+        "es-abstract": "^1.22.4",
         "es-errors": "^1.3.0",
         "es-set-tostringtag": "^2.0.2",
         "function-bind": "^1.1.2",
         "get-intrinsic": "^1.2.4",
         "globalthis": "^1.0.3",
-        "has-property-descriptors": "^1.0.1",
+        "has-property-descriptors": "^1.0.2",
         "has-proto": "^1.0.1",
         "has-symbols": "^1.0.3",
         "internal-slot": "^1.0.7",

+ 46 - 28
frontend/taipy/src/DataNodeViewer.tsx

@@ -185,6 +185,12 @@ const getValidDataNode = (datanode: DataNodeFull | DataNodeFull[]) =>
 
 const invalidDatanode: DataNodeFull = ["", "", "", "", "", "", "", "", -1, [], false, "", false, false];
 
+enum TabValues {
+    Data,
+    Properties,
+    History,
+}
+
 const DataNodeViewer = (props: DataNodeViewerProps) => {
     const {
         id = "",
@@ -225,18 +231,18 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
     ] = datanode;
 
     // Tabs
-    const [tabValue, setTabValue] = useState(0);
+    const [tabValue, setTabValue] = useState<TabValues>(TabValues.Data);
     const handleTabChange = useCallback(
         (_: SyntheticEvent, newValue: number) => {
             if (valid) {
-                if (newValue == 1) {
+                if (newValue == TabValues.History) {
                     setHistoryRequested(
                         (req) =>
                             req ||
                             dispatch(createSendActionNameAction(id, module, props.onIdSelect, { history_id: dnId })) ||
                             true
                     );
-                } else if (newValue == 2) {
+                } else if (newValue == TabValues.Data) {
                     setDataRequested(
                         (req) =>
                             req ||
@@ -267,7 +273,8 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
             if (oldDn === dn) {
                 return oldDn;
             }
-            const isNewDn = oldDn[DataNodeFullProps.id] !== (dn || invalidDatanode)[DataNodeFullProps.id];
+            const newDnId = (dn || invalidDatanode)[DataNodeFullProps.id];
+            const isNewDn = oldDn[DataNodeFullProps.id] !== newDnId;
             // clean lock on change
             if (oldDn[DataNodeFullProps.id] && isNewDn && editLock.current) {
                 dispatch(
@@ -278,17 +285,17 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
                 );
             }
             if (!dn || isNewDn) {
-                setTabValue(0);
+                setTabValue(TabValues.Data);
             }
             if (!dn) {
                 return invalidDatanode;
             }
             editLock.current = dn[DataNodeFullProps.editInProgress];
             setHistoryRequested((req) => {
-                if (req && !isNewDn && tabValue == 1) {
+                if (req && !isNewDn && tabValue == TabValues.History) {
                     dispatch(
                         createSendActionNameAction(id, module, props.onIdSelect, {
-                            history_id: oldDn[DataNodeFullProps.id],
+                            history_id: newDnId,
                         })
                     );
                     return true;
@@ -296,10 +303,13 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
                 return false;
             });
             setDataRequested((req) => {
-                if (req && !isNewDn && tabValue == 2) {
+                if (!isNewDn) {
+                    return req;
+                }
+                if (req && tabValue == TabValues.Data) {
                     dispatch(
                         createSendActionNameAction(id, module, props.onIdSelect, {
-                            data_id: oldDn[DataNodeFullProps.id],
+                            data_id: newDnId,
                         })
                     );
                     return true;
@@ -311,7 +321,8 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
             }
             return dn;
         });
-    }, [props.dataNode, props.defaultDataNode, id, dispatch, module, props.onLock, tabValue, props.onIdSelect]);
+        // eslint-disable-next-line react-hooks/exhaustive-deps
+    }, [props.dataNode, props.defaultDataNode, id, dispatch, module, props.onLock, props.onIdSelect]);
 
     // clean lock on unmount
     useEffect(
@@ -400,16 +411,15 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
     useEffect(() => {
         setLabel(dnLabel);
         setUserExpanded(expanded && valid);
-        setTabValue(0);
         setHistoryRequested(false);
-        setDataRequested(false);
+        setDataRequested(true);
         setViewType(TableViewType);
         setComment("");
     }, [dnId, dnLabel, valid, expanded]);
 
     // Datanode data
-    const dtValue = (props.data && props.data[DatanodeDataProps.value]) ?? undefined;
     const dtType = props.data && props.data[DatanodeDataProps.type];
+    const dtValue = (props.data && props.data[DatanodeDataProps.value]) ?? dtType == "float" ? null : undefined;
     const dtTabular = (props.data && props.data[DatanodeDataProps.tabular]) ?? false;
     const dtError = props.data && props.data[DatanodeDataProps.error];
     const [dataValue, setDataValue] = useState<RowValue | Date>();
@@ -503,17 +513,6 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
                     <AccordionDetails>
                         <Box sx={tabBoxSx}>
                             <Tabs value={tabValue} onChange={handleTabChange}>
-                                <Tab
-                                    label="Properties"
-                                    id={`${uniqid}-properties`}
-                                    aria-controls={`${uniqid}-dn-tabpanel-properties`}
-                                />
-                                <Tab
-                                    label="History"
-                                    id={`${uniqid}-history`}
-                                    aria-controls={`${uniqid}-dn-tabpanel-history`}
-                                    style={showHistory ? undefined : noDisplay}
-                                />
                                 <Tab
                                     label={
                                         <Grid container alignItems="center">
@@ -536,11 +535,22 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
                                     aria-controls={`${uniqid}-dn-tabpanel-data`}
                                     style={showData ? undefined : noDisplay}
                                 />
+                                <Tab
+                                    label="Properties"
+                                    id={`${uniqid}-properties`}
+                                    aria-controls={`${uniqid}-dn-tabpanel-properties`}
+                                />
+                                <Tab
+                                    label="History"
+                                    id={`${uniqid}-history`}
+                                    aria-controls={`${uniqid}-dn-tabpanel-history`}
+                                    style={showHistory ? undefined : noDisplay}
+                                />
                             </Tabs>
                         </Box>
                         <div
                             role="tabpanel"
-                            hidden={tabValue !== 0}
+                            hidden={tabValue !== TabValues.Properties}
                             id={`${uniqid}-dn-tabpanel-properties`}
                             aria-labelledby={`${uniqid}-properties`}
                         >
@@ -699,7 +709,7 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
                         </div>
                         <div
                             role="tabpanel"
-                            hidden={tabValue !== 1}
+                            hidden={tabValue !== TabValues.History}
                             id={`${uniqid}-dn-tabpanel-history`}
                             aria-labelledby={`${uniqid}-history`}
                         >
@@ -746,7 +756,7 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
                         </div>
                         <div
                             role="tabpanel"
-                            hidden={tabValue !== 2}
+                            hidden={tabValue !== TabValues.Data}
                             id={`${uniqid}-dn-tabpanel-data`}
                             aria-labelledby={`${uniqid}-data`}
                         >
@@ -835,7 +845,13 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
                                                             sx={FieldNoMaxWidth}
                                                             value={dataValue || ""}
                                                             onChange={onDataValueChange}
-                                                            type={typeof dtValue == "number" ? "number" : undefined}
+                                                            type={
+                                                                typeof dtValue == "number"
+                                                                    ? "number"
+                                                                    : dtType == "float" && dtValue === null
+                                                                    ? "number"
+                                                                    : undefined
+                                                            }
                                                             InputProps={{
                                                                 endAdornment: (
                                                                     <InputAdornment position="end">
@@ -886,6 +902,8 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
                                                                 {dtType == "date"
                                                                     ? dataValue &&
                                                                       format(dataValue as Date, "yyyy/MM/dd HH:mm:ss")
+                                                                    : dtType == "float" && dtValue === null
+                                                                    ? "NaN"
                                                                     : dtValue}
                                                             </Typography>
                                                         )}

+ 1 - 1
taipy/gui/_renderers/json.py

@@ -33,7 +33,7 @@ def _default(o):
     if isinstance(o, Path):
         return str(o)
     try:
-        raise TypeError(f"Object of type {o.__class__.__name__} is not JSON serializable")
+        raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable")
     except Exception as e:
         _warn("Exception in JSONEncoder", e)
         return None

+ 1 - 1
taipy/gui/data/data_accessor.py

@@ -94,7 +94,7 @@ class _DataAccessors(object):
         value = value.get()
         access = self.__access_4_type.get(type(value).__name__)
         if access is None:
-            if value:
+            if value is not None:
                 _warn(f"Can't find Data Accessor for type {type(value).__name__}.")
             return self.__invalid_data_accessor
         return access

+ 13 - 9
taipy/gui/data/pandas_data_accessor.py

@@ -31,7 +31,7 @@ if util.find_spec("pyarrow"):
 
 
 class _PandasDataAccessor(_DataAccessor):
-    __types = (pd.DataFrame,)
+    __types = (pd.DataFrame, pd.Series)
 
     __INDEX_COL = "_tp_index"
 
@@ -200,7 +200,9 @@ class _PandasDataAccessor(_DataAccessor):
         return ret
 
     def get_col_types(self, var_name: str, value: t.Any) -> t.Union[None, t.Dict[str, str]]:  # type: ignore
-        if isinstance(value, _PandasDataAccessor.__types):  # type: ignore
+        if isinstance(value, pd.Series):
+            value = value.to_frame()
+        if isinstance(value, pd.DataFrame):  # type: ignore
             return {str(k): v for k, v in value.dtypes.apply(lambda x: x.name.lower()).items()}
         elif isinstance(value, list):
             ret_dict: t.Dict[str, str] = {}
@@ -213,7 +215,7 @@ class _PandasDataAccessor(_DataAccessor):
         self,
         gui: Gui,
         var_name: str,
-        value: pd.DataFrame,
+        value: t.Union[pd.DataFrame, pd.Series],
         payload: t.Dict[str, t.Any],
         data_format: _DataFormat,
         col_prefix: t.Optional[str] = "",
@@ -225,6 +227,8 @@ class _PandasDataAccessor(_DataAccessor):
         paged = not payload.get("alldata", False)
         is_copied = False
 
+        if isinstance(value, pd.Series):
+            value = value.to_frame()
         # add index if not chart
         if paged:
             if _PandasDataAccessor.__INDEX_COL not in value.columns:
@@ -244,7 +248,7 @@ class _PandasDataAccessor(_DataAccessor):
                 val = fd.get("value")
                 action = fd.get("action")
                 if isinstance(val, str):
-                    if self.__is_date_column(value, col):
+                    if self.__is_date_column(t.cast(pd.DataFrame, value), col):
                         val = datetime.fromisoformat(val[:-1])
                     vars.append(val)
                 val = f"@vars[{len(vars) - 1}]" if isinstance(val, (str, datetime)) else val
@@ -271,7 +275,7 @@ class _PandasDataAccessor(_DataAccessor):
                     if col not in applies_with_fn.keys():
                         applies_with_fn[col] = "first"
                 try:
-                    value = value.groupby(aggregates).agg(applies_with_fn)
+                    value = t.cast(pd.DataFrame, value).groupby(aggregates).agg(applies_with_fn)
                 except Exception:
                     _warn(f"Cannot aggregate {var_name} with groupby {aggregates} and aggregates {applies}.")
             inf = payload.get("infinite")
@@ -305,7 +309,7 @@ class _PandasDataAccessor(_DataAccessor):
                 try:
                     if value.columns.dtype.name == "int64":
                         order_by = int(order_by)
-                    new_indexes = value[order_by].values.argsort(axis=0)
+                    new_indexes = t.cast(pd.DataFrame, value)[order_by].values.argsort(axis=0)
                     if payload.get("sort") == "desc":
                         # reverse order
                         new_indexes = new_indexes[::-1]
@@ -318,7 +322,7 @@ class _PandasDataAccessor(_DataAccessor):
             value = self.__build_transferred_cols(
                 gui,
                 columns,
-                value,
+                t.cast(pd.DataFrame, value),
                 styles=payload.get("styles"),
                 tooltips=payload.get("tooltips"),
                 is_copied=is_copied,
@@ -370,7 +374,7 @@ class _PandasDataAccessor(_DataAccessor):
                             gui._call_on_change(f"{var_name}.{decimator}.nb_rows", len(value))
                         except Exception as e:
                             _warn(f"Limit rows error with {decimator} for Dataframe", e)
-            value = self.__build_transferred_cols(gui, columns, value, is_copied=is_copied)
+            value = self.__build_transferred_cols(gui, columns, t.cast(pd.DataFrame, value), is_copied=is_copied)
             dictret = self.__format_data(value, data_format, "list", data_extraction=True)
         ret_payload["value"] = dictret
         return ret_payload
@@ -390,7 +394,7 @@ class _PandasDataAccessor(_DataAccessor):
                 for i, v in enumerate(value):
                     ret = (
                         self.__get_data(gui, var_name, v, payload, data_format, f"{i}/")
-                        if isinstance(v, pd.DataFrame)
+                        if isinstance(v, _PandasDataAccessor.__types)
                         else {}
                     )
                     ret_val = ret.get("value", {})

+ 4 - 0
taipy/gui/gui.py

@@ -15,6 +15,7 @@ import contextlib
 import importlib
 import inspect
 import json
+import math
 import os
 import pathlib
 import re
@@ -984,6 +985,9 @@ class Gui:
                     newvalue = newvalue.get()
                 if isinstance(newvalue, (dict, _MapDict)):
                     continue  # this var has no transformer
+                if isinstance(newvalue, float) and math.isnan(newvalue):
+                    # do not let NaN go through json, it is not handle well (dies silently through websocket)
+                    newvalue = None
                 debug_warnings: t.List[warnings.WarningMessage] = []
                 with warnings.catch_warnings(record=True) as warns:
                     warnings.resetwarnings()

+ 27 - 8
taipy/gui_core/_context.py

@@ -10,6 +10,7 @@
 # specific language governing permissions and limitations under the License.
 
 import json
+import math
 import typing as t
 from collections import defaultdict
 from numbers import Number
@@ -715,6 +716,14 @@ class _GuiCoreContext(CoreEventConsumerBase):
             return sorted(res, key=lambda r: r[0], reverse=True)
         return _DoNotUpdate()
 
+    @staticmethod
+    def __is_tabular_data(datanode: DataNode, value: t.Any):
+        if isinstance(datanode, _AbstractTabularDataNode):
+            return True
+        if datanode.is_ready_for_reading:
+            return isinstance(value, (pd.DataFrame, pd.Series))
+        return False
+
     def get_data_node_data(self, datanode: DataNode, id: str):
         if (
             id
@@ -728,15 +737,21 @@ class _GuiCoreContext(CoreEventConsumerBase):
                     return (None, None, True, None)
                 try:
                     value = dn.read()
-                    if isinstance(value, (pd.DataFrame, pd.Series)):
+                    if _GuiCoreContext.__is_tabular_data(dn, value):
                         return (None, None, True, None)
-                    return (
-                        value,
+                    val_type = (
                         "date"
                         if "date" in type(value).__name__
                         else type(value).__name__
                         if isinstance(value, Number)
-                        else None,
+                        else None
+                    )
+                    if isinstance(value, float):
+                        if math.isnan(value):
+                            value = None
+                    return (
+                        value,
+                        val_type,
                         None,
                         None,
                     )
@@ -826,7 +841,9 @@ class _GuiCoreContext(CoreEventConsumerBase):
             and dn.is_ready_for_reading
         ):
             try:
-                return self.__read_tabular_data(dn)
+                value = self.__read_tabular_data(dn)
+                if _GuiCoreContext.__is_tabular_data(dn, value):
+                    return value
             except Exception:
                 return None
         return None
@@ -842,9 +859,11 @@ class _GuiCoreContext(CoreEventConsumerBase):
             and dn.is_ready_for_reading
         ):
             try:
-                return self.gui._tbl_cols(
-                    True, True, "{}", json.dumps({"data": "tabular_data"}), tabular_data=self.__read_tabular_data(dn)
-                )
+                value = self.__read_tabular_data(dn)
+                if _GuiCoreContext.__is_tabular_data(dn, value):
+                    return self.gui._tbl_cols(
+                        True, True, "{}", json.dumps({"data": "tabular_data"}), tabular_data=value
+                    )
             except Exception:
                 return None
         return None