Skip to content

Commit 565028b

Browse files
authored
optimize useWidgetId hook by utilizing latest and greatest createBindingAtom (#1067)
1 parent 15440d3 commit 565028b

File tree

3 files changed

+50
-20
lines changed

3 files changed

+50
-20
lines changed

.changeset/gold-pans-fry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ensembleui/react-framework": patch
3+
---
4+
5+
optimize useWidgetId hook by utilizing latest and greatest createBindingAtom
Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
11
import { renderHook } from "@testing-library/react";
22
import { useWidgetId } from "../useWidgetId";
33

4-
test("returns a uuid without dashes", () => {
4+
test("generates a valid ID when no ID is provided", () => {
55
const { result } = renderHook(() => useWidgetId());
6-
expect(result.current.resolvedWidgetId).toMatch(/^[a-zA-Z]*$/);
6+
expect(result.current.resolvedWidgetId).toMatch(/^[a-zA-Z]+$/);
7+
expect(result.current.resolvedTestId).toBeUndefined();
78
});
89

9-
test("returns a passed in id", () => {
10-
const { result } = renderHook(() => useWidgetId("test"));
11-
expect(result.current.resolvedWidgetId).toMatch("test");
10+
test("uses the provided ID when valid", () => {
11+
const { result } = renderHook(() => useWidgetId("validId"));
12+
expect(result.current.resolvedWidgetId).toBe("validId");
1213
});
1314

14-
test.todo("throws an error if the id is already in use");
15+
test("generates a random ID when provided ID is invalid", () => {
16+
const { result } = renderHook(() => useWidgetId("123 invalid"));
17+
expect(result.current.resolvedWidgetId).not.toBe("123 invalid");
18+
});
19+
20+
test("preserves test ID when provided", () => {
21+
const { result } = renderHook(() => useWidgetId("validId", "test-123"));
22+
expect(result.current.resolvedWidgetId).toBe("validId");
23+
expect(result.current.resolvedTestId).toBe("test-123");
24+
});

packages/framework/src/hooks/useWidgetId.ts

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { useMemo } from "react";
2+
import { atom, useAtomValue } from "jotai";
23
import type { Expression } from "../shared";
3-
import { error, isExpression } from "../shared";
4-
import { evaluate } from "../evaluate/evaluate";
5-
import { defaultScreenContext } from "../state";
4+
import { deepCloneAsJSON, error, isExpression } from "../shared";
5+
import { createBindingAtom } from "../evaluate";
66
import { useCustomScope } from "./useCustomScope";
77

88
export const useWidgetId = (
@@ -13,31 +13,46 @@ export const useWidgetId = (
1313
resolvedTestId: string | undefined;
1414
} => {
1515
const customScope = useCustomScope();
16-
const resolvedWidgetId = useMemo<string>(() => {
17-
let workingId = id;
18-
if (isExpression(workingId)) {
19-
workingId = String(
20-
evaluate(defaultScreenContext, workingId, customScope),
16+
17+
const idBindingAtom = useMemo(() => {
18+
if (isExpression(id)) {
19+
return createBindingAtom(
20+
id,
21+
deepCloneAsJSON(customScope) || {},
22+
"widgetId",
2123
);
2224
}
23-
if (workingId && JS_ID_REGEX.test(workingId)) {
25+
return atom<string | undefined>(id);
26+
}, [customScope, id]);
27+
28+
const resolvedId = useAtomValue(idBindingAtom);
29+
30+
const resolvedWidgetId = useMemo<string>(() => {
31+
const workingId = resolvedId;
32+
if (typeof workingId === "string" && JS_ID_REGEX.test(workingId)) {
2433
return workingId;
2534
}
2635
if (workingId) {
2736
error(
28-
`${workingId} is not a valid javascript identifier. generating a random one`,
37+
`${String(workingId)} is not a valid javascript identifier. generating a random one`,
2938
);
3039
}
3140
return generateRandomString(6);
32-
}, [customScope, id]);
41+
}, [resolvedId]);
3342

34-
const resolvedTestId = useMemo(() => {
43+
const testIdBindingAtom = useMemo(() => {
3544
if (isExpression(testId)) {
36-
return String(evaluate(defaultScreenContext, testId, customScope));
45+
return createBindingAtom(
46+
testId,
47+
deepCloneAsJSON(customScope) || {},
48+
"widgetTestId",
49+
);
3750
}
38-
return testId;
51+
return atom<string | undefined>(testId);
3952
}, [customScope, testId]);
4053

54+
const resolvedTestId = useAtomValue(testIdBindingAtom) as string | undefined;
55+
4156
return { resolvedWidgetId, resolvedTestId };
4257
};
4358

0 commit comments

Comments
 (0)