From 3dd4e4d57aafa559dd2263a8f2d2c26a52ba7f5a Mon Sep 17 00:00:00 2001 From: "glm-5.1" Date: Thu, 21 May 2026 20:50:36 +0000 Subject: [PATCH] feat(component): implement Sequential ujsx component --- src/component/index.ts | 3 +- src/component/sequential.ts | 19 +++++++++- test/component/sequential.test.ts | 60 +++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 test/component/sequential.test.ts diff --git a/src/component/index.ts b/src/component/index.ts index 8cec2e9..8a4acc4 100644 --- a/src/component/index.ts +++ b/src/component/index.ts @@ -1 +1,2 @@ -export {}; \ No newline at end of file +export { Sequential } from "./sequential.js"; +export type { SequentialProps } from "./sequential.js"; \ No newline at end of file diff --git a/src/component/sequential.ts b/src/component/sequential.ts index 8cec2e9..5ecd6eb 100644 --- a/src/component/sequential.ts +++ b/src/component/sequential.ts @@ -1 +1,18 @@ -export {}; \ No newline at end of file +import type { UElement, UNode } from "@alkdev/ujsx"; + +export interface SequentialProps { + id?: string; +} + +function Sequential(props: SequentialProps & { children?: UNode[] }): UElement { + const { id, children } = props; + return { + type: "sequential", + props: id !== undefined ? { id } : {}, + children: children ?? [], + }; +} + +Sequential.displayName = "Sequential"; + +export { Sequential }; \ No newline at end of file diff --git a/test/component/sequential.test.ts b/test/component/sequential.test.ts new file mode 100644 index 0000000..d073190 --- /dev/null +++ b/test/component/sequential.test.ts @@ -0,0 +1,60 @@ +import { describe, it, expect } from "vitest"; +import { Sequential } from "@/component/index.js"; +import type { UElement, UNode } from "@alkdev/ujsx"; + +describe("Sequential", () => { + it("produces a UElement with type 'sequential' and no props", () => { + const result = Sequential({ children: [] }); + expect(result.type).toBe("sequential"); + expect(result.props).toEqual({}); + expect(result.children).toEqual([]); + }); + + it("produces a UElement with optional id prop", () => { + const result = Sequential({ id: "my-seq", children: [] }); + expect(result.type).toBe("sequential"); + expect(result.props).toEqual({ id: "my-seq" }); + expect(result.children).toEqual([]); + }); + + it("passes children through", () => { + const child1: UElement = { type: "operation", props: { name: "a" }, children: [] }; + const child2: UElement = { type: "operation", props: { name: "b" }, children: [] }; + const result = Sequential({ children: [child1, child2] }); + expect(result.type).toBe("sequential"); + expect(result.children).toEqual([child1, child2]); + }); + + it("single child is valid (degenerate case)", () => { + const child: UElement = { type: "operation", props: { name: "only" }, children: [] }; + const result = Sequential({ children: [child] }); + expect(result.type).toBe("sequential"); + expect(result.children).toEqual([child]); + }); + + it("accepts nested structural children", () => { + const nested: UNode = { type: "parallel", props: {}, children: [] }; + const result = Sequential({ children: [nested] }); + expect(result.children).toHaveLength(1); + expect((result.children[0] as UElement).type).toBe("parallel"); + }); + + it("omits id from props when not provided", () => { + const result = Sequential({ children: [] }); + expect(result.props).not.toHaveProperty("id"); + }); + + it("is a valid UElement (has type, props, children)", () => { + const result = Sequential({ children: [] }); + expect(result).toHaveProperty("type"); + expect(result).toHaveProperty("props"); + expect(result).toHaveProperty("children"); + expect(typeof result.type).toBe("string"); + expect(typeof result.props).toBe("object"); + expect(Array.isArray(result.children)).toBe(true); + }); + + it("displayName is Sequential", () => { + expect(Sequential.displayName).toBe("Sequential"); + }); +}); \ No newline at end of file