Skip to content

Commit 3b7e609

Browse files
committed
renamed getValue.ts into objPath.ts: first impl. of setValue()
1 parent fcf62a6 commit 3b7e609

File tree

6 files changed

+159
-42
lines changed

6 files changed

+159
-42
lines changed

dashi/src/lib/actions/handleHostStoreChange.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type {
99
} from "@/lib/types/model/callback";
1010
import type { Input } from "@/lib/types/model/channel";
1111
import { getInputValues } from "@/lib/actions/helpers/getInputValues";
12-
import { getValue, type PropertyPath } from "@/lib/utils/getValue";
12+
import { getValue, type ObjPath } from "@/lib/utils/objPath";
1313
import { invokeCallbacks } from "@/lib/actions/helpers/invokeCallbacks";
1414
import type { ContributionState } from "@/lib";
1515

@@ -18,7 +18,7 @@ import type { ContributionState } from "@/lib";
1818
*/
1919
export interface PropertyRef extends ContribRef, CallbackRef, InputRef {
2020
/** The property name as path. */
21-
propertyPath: PropertyPath;
21+
propertyPath: ObjPath;
2222
}
2323

2424
export function handleHostStoreChange<S extends object = object>(
@@ -89,11 +89,11 @@ function _getHostStorePropertyRefs(): PropertyRef[] {
8989
}
9090

9191
function hasPropertyChanged<S extends object = object>(
92-
propertyPath: PropertyPath,
92+
propertyPath: ObjPath,
9393
currState: S,
9494
prevState: S,
9595
): boolean {
96-
const currValue = getValue(currState, propertyPath);
97-
const prevValue = getValue(prevState, propertyPath);
96+
const currValue = propertyPath(currState, propertyPath);
97+
const prevValue = propertyPath(prevState, propertyPath);
9898
return !Object.is(currValue, prevValue);
9999
}

dashi/src/lib/actions/helpers/getInputValues.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { ContributionState } from "@/lib/types/state/contribution";
33
import type { ComponentState } from "@/lib/types/state/component";
44
import { isSubscriptable } from "@/lib/utils/isSubscriptable";
55
import { isContainerState } from "@/lib/actions/helpers/isContainerState";
6-
import { getValue } from "@/lib/utils/getValue";
6+
import { getValue } from "@/lib/utils/objPath";
77

88
export function getInputValues<S extends object = object>(
99
inputs: Input[],

dashi/src/lib/utils/getValue.test.ts

Lines changed: 0 additions & 23 deletions
This file was deleted.

dashi/src/lib/utils/getValue.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { describe, it, expect } from "vitest";
2+
3+
import { getValue, setValue } from "./objPath";
4+
5+
describe("Test that getValue()", () => {
6+
it("works on 0th level", () => {
7+
const obj = { a: 1, b: "x" };
8+
expect(getValue(obj, [])).toEqual({ a: 1, b: "x" });
9+
});
10+
11+
it("works on 1st level", () => {
12+
const obj = { a: 1, b: "x" };
13+
expect(getValue(obj, ["a"])).toEqual(1);
14+
expect(getValue(obj, ["b"])).toEqual("x");
15+
});
16+
17+
it("works on 2nd level", () => {
18+
const obj = { a: [1, 2, 3], b: { c: "x" } };
19+
expect(getValue(obj, ["a", "1"])).toEqual(2);
20+
expect(getValue(obj, ["a", 2])).toEqual(3);
21+
expect(getValue(obj, ["b", "c"])).toEqual("x");
22+
});
23+
});
24+
25+
describe("Test that setValue()", () => {
26+
it("works on 0th level", () => {
27+
const obj = { a: 1, b: "x" };
28+
const obj2 = setValue(obj, [], 13);
29+
expect(obj2).toBe(obj);
30+
});
31+
32+
it("works on 1st level object - change", () => {
33+
const obj = { a: 1, b: "x" };
34+
const obj2 = setValue(obj, ["a"], 2);
35+
expect(obj2).not.toBe(obj);
36+
expect(obj2).toEqual({ a: 2, b: "x" });
37+
});
38+
39+
it("works on 1st level object - create", () => {
40+
const obj = { b: "x" };
41+
const obj2 = setValue(obj, ["a"], 11);
42+
expect(obj2).not.toBe(obj);
43+
expect(obj2).toEqual({ a: 11, b: "x" });
44+
});
45+
46+
it("works on 1st level object - no change", () => {
47+
const obj = { a: 1, b: "x" };
48+
const obj2 = setValue(obj, ["a"], 1);
49+
expect(obj2).toBe(obj);
50+
expect(obj2).toEqual({ a: 1, b: "x" });
51+
});
52+
53+
it("works on 1st level array - change", () => {
54+
const obj = { a: [1, 2, 3], b: "x" };
55+
const obj2 = setValue(obj, ["a", 2], 15);
56+
expect(obj2).not.toBe(obj);
57+
expect(obj2).toEqual({ a: [1, 2, 15], b: "x" });
58+
});
59+
60+
it("works on 1st level array - create", () => {
61+
const obj = { b: "x" };
62+
const obj2 = setValue(obj, ["a", 0], 14);
63+
expect(obj2).not.toBe(obj);
64+
expect(obj2).toEqual({ a: [14], b: "x" });
65+
});
66+
67+
it("works on 1st level array - no change", () => {
68+
const obj = { a: [1, 2, 3], b: "x" };
69+
const obj2 = setValue(obj, ["a", 1], 2);
70+
expect(obj2).toBe(obj);
71+
expect(obj2).toEqual({ a: [1, 2, 3], b: "x" });
72+
});
73+
});

dashi/src/lib/utils/objPath.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
export type ObjPath = (string | number)[];
2+
3+
export function getValue(obj: object, path: ObjPath): unknown {
4+
let value: unknown = obj;
5+
for (const key of path) {
6+
if (typeof value === "object") {
7+
value = (value as unknown as Record<string, unknown>)[key];
8+
} else {
9+
return undefined;
10+
}
11+
}
12+
return value;
13+
}
14+
15+
export function setValue<S extends object>(
16+
obj: S,
17+
path: ObjPath | string,
18+
value: unknown,
19+
): S | undefined {
20+
return _setValue(obj, toObjPath(path), value);
21+
}
22+
23+
export function _setValue<S extends object>(
24+
obj: S,
25+
path: ObjPath,
26+
value: unknown,
27+
): S | undefined {
28+
if (path.length === 1) {
29+
const key = path[0];
30+
if (typeof obj === "object") {
31+
const oldValue = obj[key];
32+
if (value === oldValue) {
33+
return obj;
34+
}
35+
const newObj = Array.isArray(obj)
36+
? ([...obj] as unknown[])
37+
: ({ ...obj } as Record<string, unknown>);
38+
newObj[key] = value;
39+
return newObj as S;
40+
} else if (obj === undefined) {
41+
const newObj =
42+
typeof key === "number"
43+
? ([] as unknown[])
44+
: ({} as Record<string, unknown>);
45+
newObj[key] = value;
46+
return newObj as S;
47+
}
48+
} else if (path.length > 1) {
49+
if (typeof obj === "object") {
50+
const key = path[0];
51+
const subObj = obj[key];
52+
const newSubObj = setValue(subObj, path.slice(1), value);
53+
if (subObj !== newSubObj) {
54+
const newObj = Array.isArray(obj)
55+
? ([...obj] as unknown[])
56+
: ({ ...obj } as Record<string, unknown>);
57+
newObj[key] = newSubObj;
58+
return newObj as S;
59+
}
60+
}
61+
}
62+
return obj;
63+
}
64+
65+
function toObjPath(property: ObjPath | string): ObjPath {
66+
if (Array.isArray(property)) {
67+
return property as ObjPath;
68+
}
69+
if (property === "") {
70+
return [];
71+
}
72+
const objPath: ObjPath = property.split(".");
73+
for (let i = 0; i < objPath.length; i++) {
74+
const index = Number(objPath[i]);
75+
if (Number.isInteger(index)) {
76+
objPath[i] = index;
77+
}
78+
}
79+
return objPath;
80+
}

0 commit comments

Comments
 (0)