diff --git a/src/component/index.ts b/src/component/index.ts index 216486a..e781c55 100644 --- a/src/component/index.ts +++ b/src/component/index.ts @@ -1,2 +1,4 @@ export { Parallel } from "./parallel.js"; -export type { ParallelProps } from "./parallel.js"; \ No newline at end of file +export type { ParallelProps } from "./parallel.js"; +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