diff --git a/src/dom-builder.ts b/src/dom-builder.ts index ad4fb1e..4b59237 100644 --- a/src/dom-builder.ts +++ b/src/dom-builder.ts @@ -1,4 +1,10 @@ -import { Behavior, Future, isBehavior, Stream } from "@funkia/hareactive"; +import { + Behavior, + Future, + isBehavior, + Stream, + isStream +} from "@funkia/hareactive"; import { behaviorFromEvent, streamFromEvent, @@ -90,16 +96,17 @@ export type ClassDescription = export interface ClassDescriptionArray extends Array {} export type Attributes = { - [name: string]: (Showable | boolean) | Behavior; + [name: string]: + | (Showable | boolean) + | Stream + | Behavior; }; type _InitialProperties = { streams?: StreamDescriptions; behaviors?: BehaviorDescriptions; style?: Style; - props?: { - [name: string]: Showable | Behavior; - }; + props?: Attributes; attrs?: Attributes; actionDefinitions?: ActionDefinitions; actions?: Actions; @@ -166,7 +173,7 @@ const styleSetter = (element: HTMLElement) => (key: string, value: string) => (element.style[key] = value); function handleObject( - object: { [key: string]: A | Behavior } | undefined, + object: { [key: string]: A | Behavior | Stream } | undefined, element: HTMLElement, createSetter: (element: HTMLElement) => (key: string, value: A) => void ): void { @@ -176,6 +183,8 @@ function handleObject( const value = object[key]; if (isBehavior(value)) { render((newValue) => setter(key, newValue), value); + } else if (isStream(value)) { + value.subscribe((newValue) => setter(key, newValue)); } else { setter(key, value); } diff --git a/src/utils.ts b/src/utils.ts index 7685ba9..d1bc419 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,4 @@ -import { isBehavior } from "@funkia/hareactive"; +import { isBehavior, isStream } from "@funkia/hareactive"; function arrayConcat(arr1: A[], arr2: A[]): A[] { const result = []; @@ -12,7 +12,12 @@ function arrayConcat(arr1: A[], arr2: A[]): A[] { } function isObject(item: any): item is Object { - return typeof item === "object" && !Array.isArray(item) && !isBehavior(item); + return ( + typeof item === "object" && + !Array.isArray(item) && + !isBehavior(item) && + !isStream(item) + ); } export function mergeObj(a: A, b: B): A & B { diff --git a/test/component.spec.ts b/test/component.spec.ts index 785ebdd..4123e90 100644 --- a/test/component.spec.ts +++ b/test/component.spec.ts @@ -34,7 +34,7 @@ describe("component specs", () => { let result: number | undefined = undefined; const c = performComponent(() => (result = 12)); assert.strictEqual(result, undefined); - const { output, available } = testComponent(c); + const { output } = testComponent(c); assert.strictEqual(result, 12); assert.strictEqual(output, 12); }); @@ -98,7 +98,7 @@ describe("component specs", () => { newFoo: "foo", newBar: "bar" }); - const { dom, available, output } = testComponent(comp); + const { available, output } = testComponent(comp); expect(output.newFoo).to.equal(1); expect(output.newBar).to.equal(2); expect((available as any).newFoo).to.be.undefined; diff --git a/test/dom-builder.spec.ts b/test/dom-builder.spec.ts index 7425cbf..a9d3111 100644 --- a/test/dom-builder.spec.ts +++ b/test/dom-builder.spec.ts @@ -308,6 +308,14 @@ describe("dom-builder", () => { push("/bar", hrefB); expect(aElm).to.have.attribute("href", "/bar"); }); + it("sets attributes from streams", () => { + const hrefS = sinkStream(); + const { dom } = testComponent(element("a", { attrs: { href: hrefS } })()); + const aElm = dom.firstChild; + expect(aElm).to.not.have.attribute("href"); + push("/bar", hrefS); + expect(aElm).to.have.attribute("href", "/bar"); + }); it("sets boolean attributes correctly", () => { const { dom } = testComponent( element("a", { attrs: { contenteditable: true } })() @@ -355,6 +363,30 @@ describe("dom-builder", () => { push("there", htmlB); expect(aElm.innerHTML).to.equal("there"); }); + it("sets properties from stream", () => { + const value = sinkStream(); + const { dom } = testComponent(element("input", { props: { value } })()); + const inputElm = dom.firstChild! as HTMLInputElement; + assert.strictEqual(inputElm.value, ""); + push("bar", value); + assert.strictEqual(inputElm.value, "bar"); + }); + it("sets properties from stream", () => { + const value = sinkStream(); + const { dom } = testComponent(element("input", { props: { value } })()); + const inputElm = dom.firstChild! as HTMLInputElement; + assert.strictEqual(inputElm.value, ""); + push("bar", value); + assert.strictEqual(inputElm.value, "bar"); + }); + it("sets input value from stream", () => { + const value = sinkStream(); + const { dom } = testComponent(element("input", { value })()); + const inputElm = dom.firstChild! as HTMLInputElement; + assert.strictEqual(inputElm.value, ""); + push("bar", value); + assert.strictEqual(inputElm.value, "bar"); + }); it("sets input value", () => { const b = sinkBehavior("foo"); const { dom } = testComponent(E.input({ value: b }));