diff --git a/packages/forest/__tests__/forest.spec.ts b/packages/forest/__tests__/forest.spec.ts index 67468052f..94522e1b8 100644 --- a/packages/forest/__tests__/forest.spec.ts +++ b/packages/forest/__tests__/forest.spec.ts @@ -260,3 +260,38 @@ test("hard patch with selector replaces selected state", () => { compTree!.nodes["comp1"]!.state["breakpoints"]["991"]["styles"]["height"] ).toBe("20px"); }); + +test("unset event", () => { + const forest = createForest(forestDef); + const compTree = forest.tree(componentTreeDef.modulePath); + forest.handleEvents({ + name: "TEST_EVENTS", + events: [ + { + type: `CREATE$$${componentTreeDef.modulePath}`, + id: "comp1", + meta: {}, + state: { + parent: { id: "body", index: 0 }, + }, + }, + { + type: `PATCH$$${componentTreeDef.modulePath}`, + id: "comp1", + slice: { + alias: "comp1Alias", + }, + }, + { + type: `UNSET$$${componentTreeDef.modulePath}`, + id: "comp1", + selector: ["parent", "id"], + }, + ], + meta: { agent: "server-sent" }, + }); + expect(compTree?.nodes).toBeDefined(); + expect(compTree!.nodes["comp1"]!.state).toHaveProperty("parent"); + expect(compTree!.nodes["comp1"]!.state.parent).toHaveProperty("index"); + expect(compTree!.nodes["comp1"]!.state.parent).not.toHaveProperty("id"); +}); diff --git a/packages/forest/src/forest.ts b/packages/forest/src/forest.ts index 242987e9f..af2683ee6 100644 --- a/packages/forest/src/forest.ts +++ b/packages/forest/src/forest.ts @@ -15,6 +15,7 @@ import { TreeNode, HardPatchEvent, TreeDef, + UnsetEvent, } from "./types"; function mergeStateCustomizer(obj: any, src: any) { @@ -318,6 +319,31 @@ export function createForest(def: { trees: TreeDef[] }): Forest { }); } } + if (event.type.startsWith("UNSET")) { + const unsetEvent = event as UnsetEvent; + const treeId = unsetEvent.type.slice("UNSET$$".length); + const tree = treeMap[treeId]!; + const selector = unsetEvent.selector; + // store old state + const oldState = JSON.parse( + JSON.stringify(tree.nodes[unsetEvent.id]!.state) + ); + let curr = tree.nodes[unsetEvent.id]!.state; + for (let i = 0; i < selector.length; i++) { + if (i === selector.length - 1) { + delete curr[selector[i]!]; + break; + } + curr = curr[selector[i]!]; + } + // emit change event + forestUpdateSubscribers.forEach((cb) => { + cb( + { type: "change", id: unsetEvent.id, treeId, oldState }, + { name, meta } + ); + }); + } } function handleEvents(data: { diff --git a/packages/forest/src/types.ts b/packages/forest/src/types.ts index 70579cab0..e755e83aa 100644 --- a/packages/forest/src/types.ts +++ b/packages/forest/src/types.ts @@ -77,13 +77,19 @@ export type HardPatchEvent = { selector?: string[]; } & EventDto; +export type UnsetEvent = { + id: string; + selector: string[]; +} & EventDto; + export type AnyEvent = | CreateEvent | PatchEvent | DeleteEvent | LinkEvent | UnlinkEvent - | HardPatchEvent; + | HardPatchEvent + | UnsetEvent; export type TreeDefReturnType = { validateCreate: (event: CreateEvent) => boolean;