import { Store } from "../data/Store"; import { createFunctionalComponent } from "./createFunctionalComponent"; import { bind } from "./bind"; import { expr } from "./expr"; import { createTestRenderer, act } from "../util/test/createTestRenderer"; import assert from "assert"; import { Rescope } from "./Rescope"; import { LabelsLeftLayout } from "./layout/LabelsLeftLayout"; import { LabeledContainer } from "../widgets/form/LabeledContainer"; import { Repeater } from "./Repeater"; import { FirstVisibleChildLayout } from "./layout/FirstVisibleChildLayout"; import { useStoreMethods } from "../hooks"; import { AccessorChain, createAccessorModelProxy } from "../data/createAccessorModelProxy"; describe("createFunctionalComponent generic type safety", () => { interface User { id: number; name: string; } interface GenericListProps { items: AccessorChain; onSelect?: (item: T) => void; } const GenericList = createFunctionalComponent( ({ items, onSelect }: GenericListProps) => (
) ); it("infers generic type parameter from AccessorChain", () => { let model = createAccessorModelProxy<{ users: User[] }>(); const widget = ( { // user should be inferred as User const id: number = user.id; const name: string = user.name; // @ts-expect-error - nonExistent does not exist on User const x = user.nonExistent; }} /> ); assert.ok(widget); }); it("accepts PureContainerConfig props like visible", () => { let model = createAccessorModelProxy<{ users: User[] }>(); const widget = ( ); assert.ok(widget); }); }); describe("createFunctionalComponent type safety", () => { interface MyComponentProps { title: string; count: number; optional?: boolean; } const TypedComponent = createFunctionalComponent( ({ title, count, optional }) => (
{optional &&
} ) ); it("rejects non-existing properties", () => { const widget = ( ); assert.ok(widget); }); it("rejects wrong property type", () => { const widget = ( ); assert.ok(widget); }); it("rejects missing required properties", () => { const widget = ( {/* @ts-expect-error - count is required but missing */} ); assert.ok(widget); }); }); describe("createFunctionalComponent", () => { it("allows spread", async () => { const SuperDiv = createFunctionalComponent(({ ...props }) => { return (
); }); let props = { text: "Spread", style: "background: red;", }; const widget = ( ); let store = new Store(); const component = await createTestRenderer(store, widget); let tree = component.toJSON(); assert.deepEqual(tree, { type: "div", children: ["Spread"], props: { className: "test", style: { background: "red", }, }, }); }); it("visible and Rescope behave as expected", async () => { const RootRescope = createFunctionalComponent(({}) => { return (
); }); const widget = ( ); let store = new Store({ data: { x: { y: "OK", }, }, }); const component = await createTestRenderer(store, widget); let tree = component.toJSON(); assert.deepEqual(tree, { type: "div", children: ["OK"], props: {}, }); }); it("visible and multiple items behave as expected", async () => { const FComponent = createFunctionalComponent(({}) => { return (
1
2
3
); }); const widget = ( ); let store = new Store(); const component = await createTestRenderer(store, widget); let tree = component.toJSON(); assert.deepEqual(tree, [ { type: "div", children: ["1"], props: {}, }, { type: "div", children: ["3"], props: {}, }, ]); }); it("respects inner layouts", async () => { const FComponent = createFunctionalComponent(({}) => { return ( ); }); const widget = (
); let store = new Store(); const component = await createTestRenderer(store, widget); let tree = component.toJSON(); assert.deepEqual(tree, { type: "div", props: {}, children: [ { type: "table", props: { className: "cxb-labelsleftlayout", style: undefined, }, children: [ { type: "tbody", props: {}, children: [ { type: "tr", props: {}, children: [ { type: "td", props: { className: "cxe-labelsleftlayout-label", style: undefined, }, children: [ { type: "label", props: { className: "cxb-label", }, children: ["Test"], }, ], }, { type: "td", props: { className: "cxe-labelsleftlayout-field", }, children: null, }, ], }, { type: "tr", props: {}, children: [ { type: "td", props: { className: "cxe-labelsleftlayout-label", style: undefined, }, children: [ { type: "label", props: { className: "cxb-label", }, children: ["Test"], }, ], }, { type: "td", props: { className: "cxe-labelsleftlayout-field", }, children: null, }, ], }, ], }, ], }, ], }); }); it("can use refs for data bindings", async () => { const X = createFunctionalComponent(({}) => { let { ref } = useStoreMethods(); let x = ref("x", "OK"); return (
); }); const widget = ( ); let store = new Store(); const component = await createTestRenderer(store, widget); let tree = component.toJSON(); assert.deepEqual(tree, { type: "div", children: ["OK"], props: {}, }); }); it("adds children at the right place", async () => { const X = createFunctionalComponent(({ children }: { children: any }) => (
{children}