Skip to content

Commit ce9c188

Browse files
FredLL-AvaigaFred Lefévère-LaoideCopilot
authored
Manage to json properties (#2733)
* allow insert in list Closes #2727 * Update taipy/gui/utils/patch.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update frontend/taipy-gui/src/context/patch.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix boundary check * update for react-is version * more info on doc * handle property types that are sent as json * Update taipy/gui/utils/types.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update taipy/gui/utils/types.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Fred Lefévère-Laoide <Fred.Lefevere-Laoide@Taipy.io> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent ab2c94e commit ce9c188

File tree

13 files changed

+1547
-669
lines changed

13 files changed

+1547
-669
lines changed

frontend/taipy-gui/package-lock.json

Lines changed: 165 additions & 222 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/taipy-gui/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
},
3535
"overrides": {
3636
"react": "$react",
37-
"react-dom": "$react-dom"
37+
"react-dom": "$react-dom",
38+
"react-is": "$react"
3839
},
3940
"scripts": {
4041
"inst": "npm i",

frontend/taipy-gui/src/context/patch.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,33 @@ describe("patchValue", () => {
8080
const newObj = patchValue(obj, { a: { b: { c: 1 } } });
8181
expect(JSON.stringify(newObj)).toBe(originalObj);
8282
});
83+
84+
it("should insert in an array if index < 0", () => {
85+
const obj = { a: { b: [0, 1, 2] } };
86+
const newObj = patchValue(obj, { a: { b: { "-1": [3] as unknown as number } } });
87+
expect(newObj.a.b[0]).toBe(3);
88+
expect(newObj.a.b[1]).toBe(0);
89+
expect(newObj.a.b.length).toBe(4);
90+
91+
const newObj2 = patchValue(obj, { a: { b: { "-3": [3] as unknown as number } } });
92+
expect(newObj2.a.b[2]).toBe(3);
93+
expect(newObj2.a.b[3]).toBe(2);
94+
expect(newObj2.a.b.length).toBe(4);
95+
});
96+
97+
it("should update an empty array", () => {
98+
const obj = { a: { b: [] } };
99+
const newObj = patchValue(obj, { a: { b: { 0: 1 as unknown as number } } });
100+
expect(newObj.a.b[0]).toBe(1);
101+
expect(newObj.a.b.length).toBe(1);
102+
103+
const newObj2 = patchValue(obj, { a: { b: { 0: [2, 3] as unknown as number } } });
104+
expect(newObj2.a.b[0]).toBe(2);
105+
expect(newObj2.a.b[1]).toBe(3);
106+
expect(newObj2.a.b.length).toBe(2);
107+
108+
});
109+
83110
});
84111
describe("remove", () => {
85112
it("should remove the value at the specified path", () => {

frontend/taipy-gui/src/context/patch.ts

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,36 +18,53 @@ export interface PatchRemove {
1818
[key: string | number]: null | PatchRemove;
1919
}
2020

21-
const ONLY_DIGITS = /^\d+$/;
21+
const RE_NUMBER = /^-?\d+$/;
2222

2323
export const patchValue = <T>(toBePatched: T, change?: PatchChange, remove?: PatchRemove): T => {
2424
let patchedValue = toBePatched;
2525
if (change) {
2626
// Apply changes
2727
Object.entries(change).forEach(([k, v]) => {
28-
const idx = Number(k);
29-
if (ONLY_DIGITS.test(k) && Array.isArray(toBePatched) && toBePatched.length > idx) {
28+
const sIdx = Number(k);
29+
const idx = sIdx < 0 ? -1 - sIdx : sIdx;
30+
const insert = sIdx < 0;
31+
if (
32+
Array.isArray(toBePatched) &&
33+
RE_NUMBER.test(k) &&
34+
(insert ? toBePatched.length > idx : toBePatched.length >= idx)
35+
) {
3036
// TODO deal with _tp_index
31-
const oldValue = (toBePatched as Array<unknown>)[idx];
37+
const oldValue = toBePatched[idx];
3238
if (oldValue !== v) {
3339
if (Array.isArray(v) && v.length > 0) {
34-
if (patchedValue === toBePatched) {
35-
patchedValue = [...toBePatched] as T;
36-
}
37-
const newArray = (v as Array<unknown>).map((item, j) => {
38-
const oldItem = (toBePatched as Array<unknown>)[idx + j];
39-
if (
40-
typeof item === "object" &&
41-
item !== null &&
42-
typeof oldItem === "object" &&
43-
oldItem !== null
44-
) {
45-
return patchValue(oldItem, item as PatchChange);
40+
if (insert) {
41+
if (idx >= (patchedValue as Array<unknown>).length) {
42+
patchedValue = [...(patchedValue as Array<unknown>), ...v] as T;
4643
} else {
47-
return item;
44+
if (patchedValue === toBePatched) {
45+
patchedValue = [...toBePatched] as T;
46+
}
47+
(patchedValue as Array<unknown>).splice(idx, 0, ...v);
48+
}
49+
} else {
50+
if (patchedValue === toBePatched) {
51+
patchedValue = [...toBePatched] as T;
4852
}
49-
});
50-
(patchedValue as Array<unknown>).splice(idx, newArray.length, ...newArray);
53+
const newArray = (v as Array<unknown>).map((item, j) => {
54+
const oldItem = (toBePatched as Array<unknown>)[idx + j];
55+
if (
56+
typeof item === "object" &&
57+
item !== null &&
58+
typeof oldItem === "object" &&
59+
oldItem !== null
60+
) {
61+
return patchValue(oldItem, item as PatchChange);
62+
} else {
63+
return item;
64+
}
65+
});
66+
(patchedValue as Array<unknown>).splice(idx, newArray.length, ...newArray);
67+
}
5168
} else if (
5269
oldValue !== null &&
5370
typeof oldValue === "object" &&
@@ -65,10 +82,14 @@ export const patchValue = <T>(toBePatched: T, change?: PatchChange, remove?: Pat
6582
(oldValue === null || typeof oldValue !== "object") &&
6683
(v === null || typeof v !== "object")
6784
) {
68-
if (patchedValue === toBePatched) {
69-
patchedValue = [...toBePatched] as T;
85+
if (idx >= (patchedValue as Array<unknown>).length) {
86+
patchedValue = [...(patchedValue as Array<unknown>), v] as T;
87+
} else {
88+
if (patchedValue === toBePatched) {
89+
patchedValue = [...toBePatched] as T;
90+
}
91+
(patchedValue as Array<unknown>)[idx] = v;
7092
}
71-
(patchedValue as Array<unknown>)[idx] = v;
7293
}
7394
}
7495
} else if (toBePatched && typeof toBePatched === "object" && !Array.isArray(toBePatched)) {
@@ -109,7 +130,7 @@ export const patchValue = <T>(toBePatched: T, change?: PatchChange, remove?: Pat
109130
// Apply removals
110131
Object.entries(remove).forEach(([k, v]) => {
111132
const idx = Number(k);
112-
if (ONLY_DIGITS.test(k) && Array.isArray(toBePatched) && toBePatched.length > idx) {
133+
if (RE_NUMBER.test(k) && Array.isArray(toBePatched) && toBePatched.length > idx) {
113134
const oldValue = (toBePatched as Array<unknown>)[idx];
114135
if (oldValue !== undefined) {
115136
if (v !== null && typeof v === "object" && !Array.isArray(v)) {

0 commit comments

Comments
 (0)