Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions packages/runtime/src/internal/bindListeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ export function listenerFactory(
case "context.replace":
case "context.refresh":
case "context.load":
case "context.set":
handleContextAction(
event,
method,
Expand All @@ -236,6 +237,7 @@ export function listenerFactory(
case "state.update":
case "state.refresh":
case "state.load":
case "state.set":
handleTplStateAction(
event,
method,
Expand Down Expand Up @@ -687,7 +689,7 @@ function batchUpdate(

function handleContextAction(
event: Event,
method: "assign" | "replace",
method: "assign" | "replace" | "refresh" | "load" | "set",
args: unknown[] | undefined,
batch: boolean,
callback: BrickEventHandlerCallback | undefined,
Expand Down Expand Up @@ -717,7 +719,7 @@ function handleContextAction(

function handleTplStateAction(
event: Event,
method: "update" | "refresh" | "load",
method: "update" | "refresh" | "load" | "set",
args: unknown[] | undefined,
batch: boolean,
callback: BrickEventHandlerCallback | undefined,
Expand Down
38 changes: 38 additions & 0 deletions packages/runtime/src/internal/compute/setRealProperties.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { setValueForStyle } from "./setRealProperties.js";

describe("setValueForStyle", () => {
let mockStyle: CSSStyleDeclaration;

beforeEach(() => {
mockStyle = {
setProperty: jest.fn(),
cssFloat: "",
} as unknown as CSSStyleDeclaration;
});

it("should set custom CSS property using setProperty when key starts with --", () => {
setValueForStyle(mockStyle, "--custom-color", "red");

expect(mockStyle.setProperty).toHaveBeenCalledWith("--custom-color", "red");
});

it('should set cssFloat property when key is "float"', () => {
setValueForStyle(mockStyle, "float", "left");

expect(mockStyle.cssFloat).toBe("left");
});

it("should set regular style property directly", () => {
setValueForStyle(mockStyle, "color", "blue");

expect((mockStyle as any).color).toBe("blue");
});

it("should set multiple regular properties", () => {
setValueForStyle(mockStyle, "fontSize", "16px");
setValueForStyle(mockStyle, "margin", "10px");

expect((mockStyle as any).fontSize).toBe("16px");
expect((mockStyle as any).margin).toBe("10px");
});
});
20 changes: 19 additions & 1 deletion packages/runtime/src/internal/compute/setRealProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ export function setRealProperties(
for (const [k, v] of Object.entries(
propValue as Record<string, unknown>
)) {
(brick[propName] as unknown as Record<string, unknown>)[k] = v;
if (propName === "style") {
setValueForStyle(brick.style, k, v);
} else {
(brick[propName] as unknown as Record<string, unknown>)[k] = v;
}
}
break;
case "constructor":
Expand All @@ -25,3 +29,17 @@ export function setRealProperties(
}
}
}

export function setValueForStyle(
style: CSSStyleDeclaration,
key: string,
value: unknown
) {
if (key.startsWith("--")) {
style.setProperty(key, value as string);
} else if (key === "float") {
style.cssFloat = value as string;
} else {
(style as unknown as Record<string, unknown>)[key] = value;
}
}
37 changes: 37 additions & 0 deletions packages/runtime/src/internal/data/DataStore.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,43 @@ describe("DataStore", () => {
expect(mockCallRealTimeDataInspectHooks).toHaveBeenCalledTimes(2);
});

test("context.set", async () => {
setRealTimeDataInspectRoot({});
const ctxStore = new DataStore("CTX");
const runtimeContext = {
ctxStore,
} as Partial<RuntimeContext> as RuntimeContext;
ctxStore.define(
[
{
name: "primitive",
value: "any",
onChange: {
action: "context.set",
args: ["count", "<% (count) => count + 1 %>"],
},
},
{
name: "count",
value: 0,
},
],
runtimeContext
);
await ctxStore.waitForAll();
expect(ctxStore.getValue("primitive")).toEqual("any");
expect(ctxStore.getValue("count")).toBe(0);

const newValue = { amount: 42 };
ctxStore.updateValue("primitive", newValue, "set");
expect(ctxStore.getValue("primitive")).toEqual({ amount: 42 });
expect(ctxStore.getValue("count")).toBe(1);

ctxStore.updateValue("primitive", newValue, "set");
expect(ctxStore.getValue("primitive")).toEqual({ amount: 42 });
expect(ctxStore.getValue("count")).toBe(1);
});

test("state and onChange", async () => {
jest.useFakeTimers();
const tplStateStoreId = "tpl-state-1";
Expand Down
17 changes: 15 additions & 2 deletions packages/runtime/src/internal/data/DataStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export class DataStore<T extends DataStoreType = "CTX"> {
updateValue(
name: string,
value: unknown,
method: "assign" | "replace" | "refresh" | "load",
method: "assign" | "replace" | "refresh" | "load" | "set",
callback?: BrickEventHandlerCallback,
callbackRuntimeContext?: RuntimeContext
): void {
Expand Down Expand Up @@ -276,7 +276,7 @@ export class DataStore<T extends DataStoreType = "CTX"> {

if (method === "replace") {
item.value = value;
} else {
} else if (method === "assign") {
if (isObject(item.value)) {
Object.assign(item.value, value);
} else {
Expand All @@ -286,6 +286,19 @@ export class DataStore<T extends DataStoreType = "CTX"> {
);
item.value = value;
}
} else {
// method === "set"
// `context.set` and `state.set` is similar to React's setState,
// which accepts either a value or an updater function.
// And if the new value is the same as the current one, do nothing.
let nextValue = value;
if (typeof value === "function") {
nextValue = (value as (prevState: unknown) => unknown)(item.value);
}
if (Object.is(item.value, nextValue)) {
return;
}
item.value = nextValue;
}

if (this.batchUpdate) return;
Expand Down
2 changes: 2 additions & 0 deletions packages/types/src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -980,11 +980,13 @@ export interface BuiltinBrickEventHandler {
| "context.replace"
| "context.refresh"
| "context.load"
| "context.set"

// Update template state
| "state.update"
| "state.refresh"
| "state.load"
| "state.set"

// Find related tpl and dispatch event.
| "tpl.dispatchEvent"
Expand Down
Loading