diff --git a/src/index.ts b/src/index.ts index 29d1e21..3b08398 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,11 @@ import { effect, Signal, signal } from "@preact/signals-core"; import { reconcile } from "./reconciler/index"; import { ShadowCache } from "./reconciler/utils"; -import type { CustomEvents, ForbiddenHTMLProperties, ReadonlyKeys, ShadowElement } from "./types"; -import { parentsCacheSymbol, PlusnewErrorEvent, active } from "./utils"; +import type { CustomEvents, PropertyDescriptor, ShadowElement } from "./types"; +import { parentsCacheSymbol, active } from "./utils"; -export type { ShadowElement } from "./types"; -export { active, WebComponent } from "./utils"; +export type { ShadowElement, PropertyDescriptor } from "./types"; +export { active, WebComponent, type BasePropsType, type PropType } from "./utils"; export function mount(render: () => ShadowElement, parent: HTMLElement): () => void { const shadowResult: ShadowCache = new ShadowCache(false); @@ -30,28 +30,18 @@ export function mount(render: () => ShadowElement, parent: HTMLElement): () => v }; } -export function createComponent( +export function define(name: string): ClassDecorator { + return (Component) => { + customElements.define(name, Component as any); + + return Component; + }; +} + +export function createComponent HTMLElement>( name: string, - Component: { new (): T }, -): { - new ( - properties: Omit< - { [K in keyof T]?: T[K] } & { - [ - K in keyof T as undefined extends T[K] ? never : K extends keyof HTMLElement ? never : K - ]-?: T[K]; - }, - | ReadonlyKeys - | ForbiddenHTMLProperties - | "render" - | "connectedCallback" - | "disconnectedCallback" - > & { - children?: ShadowElement; - onplusnewerror?: (evt: PlusnewErrorEvent) => void; - }, - ): T; -} { + Component: T, +): T { customElements.define(name, Component as any); return name as any; @@ -141,24 +131,16 @@ export function dispatchEvent( - decoratorTarget: ClassAccessorDecoratorTarget, - accessor: ClassAccessorDecoratorContext, - ): ClassAccessorDecoratorResult => { +export function prop(): () => PropertyDescriptor { + return () => { + const signalValue = signal() as Signal; + return { - set: function (value) { - if (accessor.access.has(this)) { - (decoratorTarget.get.call(this) as Signal).value = value; - } else { - decoratorTarget.set.call(this, signal(value) as U); - } - }, - get: function () { - return (decoratorTarget.get.call(this) as Signal).value; + get: () => { + return signalValue.value; }, - init(value) { - return signal(value) as U; + set: (value) => { + signalValue.value = value; }, }; }; diff --git a/src/types.ts b/src/types.ts index 021f9be..be65706 100644 --- a/src/types.ts +++ b/src/types.ts @@ -6,12 +6,24 @@ export function Fragment(props: { children: ShadowElement }) { return props.children; } +export interface PropertyDescriptor { + configurable?: boolean; + enumerable?: boolean; + value?: T; + writable?: boolean; + get?(): T; + set?(v: T): void; +} + +export type PropertyDescriptorType> = + T extends PropertyDescriptor ? R : never; + // type Expect = T; type IsEqual = (() => T extends CheckA ? 1 : 2) extends () => T extends CheckB ? 1 : 2 ? Then : Else; -export type ReadonlyKeys = { +type ReadonlyKeys = { [P in keyof T]-?: IsEqual<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, never, P>; }[keyof T]; diff --git a/src/utils.ts b/src/utils.ts index 7a08956..96e12b4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,11 @@ import { batch, effect, untracked } from "@preact/signals-core"; import { ShadowCache } from "./reconciler/utils"; -import type { ShadowElement } from "./types"; +import type { + ForbiddenHTMLProperties, + PropertyDescriptor, + PropertyDescriptorType, + ShadowElement, +} from "./types"; import { reconcile } from "./reconciler"; const ERROR = "plusnewerror"; @@ -35,100 +40,140 @@ const shadowCache = Symbol("shadowCache"); const eventListenerSymbol = Symbol("eventListner"); export const parentsCacheSymbol = Symbol("parentsCache"); -export abstract class WebComponent extends HTMLElement { - abstract render(): ShadowElement; +export type BasePropsType = Omit, ForbiddenHTMLProperties | "children"> & { + children?: ShadowElement; +}; + +export type PropType PropertyDescriptor }> = { + [ + Prop in keyof T as undefined extends PropertyDescriptorType> ? Prop : never + ]?: Exclude>, undefined>; +} & { + [ + Prop in keyof T as undefined extends PropertyDescriptorType> ? never : Prop + ]: PropertyDescriptorType>; +}; - connectedCallback(this: WebComponent, opt?: { shadowRootInit?: Partial }) { - let shadowRoot: null | ShadowRoot = null; - if (this.shadowRoot === null) { - shadowRoot = this.attachShadow({ mode: "open", ...opt?.shadowRootInit }); +interface IComponent extends HTMLElement { + connectedCallback(opt?: { shadowRootInit: Partial }): ShadowRoot; + disconnectedCallback(): void; +} - (this as any)[parentsCacheSymbol] = new Map(); - (this as any)[shadowCache] = new ShadowCache(false); - } else { - shadowRoot = this.shadowRoot; +export function WebComponent PropertyDescriptor } = {}>( + props?: T, +): abstract new (props: PropType & BasePropsType) => PropType & IComponent { + abstract class Component extends HTMLElement implements IComponent { + constructor(_props: PropType & BasePropsType) { + super(); + if (props !== undefined) { + Object.defineProperties( + this, + Object.fromEntries( + Object.entries(props).map(([key, propertyDescriptorFactory]) => [ + key, + propertyDescriptorFactory(), + ]), + ), + ); + } } - (this as any)[disconnect] = effect(() => { - batch(() => { - const previousActiveElement = active.parentElement; - let result: ShadowElement; - try { - active.parentElement = this; - result = this.render(); - active.parentElement = previousActiveElement; - } catch (error) { - active.parentElement = previousActiveElement; - untracked(() => dispatchError(this, error)); - - return; - } + abstract render(): ShadowElement; - reconcile({ - parentElement: this.shadowRoot as ShadowRoot, - previousSibling: null, - shadowCache: (this as any)[shadowCache], - shadowElement: result, + connectedCallback(opt?: { shadowRootInit?: Partial }) { + let shadowRoot: null | ShadowRoot = null; + if (this.shadowRoot === null) { + shadowRoot = this.attachShadow({ mode: "open", ...opt?.shadowRootInit }); + + (this as any)[parentsCacheSymbol] = new Map(); + (this as any)[shadowCache] = new ShadowCache(false); + } else { + shadowRoot = this.shadowRoot; + } + + (this as any)[disconnect] = effect(() => { + batch(() => { + const previousActiveElement = active.parentElement; + let result: ShadowElement; + try { + active.parentElement = this; + result = this.render(); + active.parentElement = previousActiveElement; + } catch (error) { + active.parentElement = previousActiveElement; + untracked(() => dispatchError(this, error)); + + return; + } + + reconcile({ + parentElement: this.shadowRoot as ShadowRoot, + previousSibling: null, + shadowCache: (this as any)[shadowCache], + shadowElement: result, + }); }); }); - }); - - return shadowRoot; - } - disconnectedCallback(this: WebComponent) { - if (disconnect in this) { - (this as any)[disconnect](); + return shadowRoot; } - if (parentsCacheSymbol in this) { - (this as any)[parentsCacheSymbol].clear(); - } - if (shadowCache in this) { - (this as any)[shadowCache].unmount(); - } - } - addEventListener( - eventName: string, - listener: (event: Event) => unknown, - options?: boolean | AddEventListenerOptions, - ) { - if (eventListenerSymbol in this === false) { - (this as any)[eventListenerSymbol] = {}; - } - if (eventName in (this as any)[eventListenerSymbol] === false) { - (this as any)[eventListenerSymbol][eventName] = new WeakMap(); + disconnectedCallback() { + if (disconnect in this) { + (this as any)[disconnect](); + } + if (parentsCacheSymbol in this) { + (this as any)[parentsCacheSymbol].clear(); + } + if (shadowCache in this) { + (this as any)[shadowCache].unmount(); + } } - const listenerOverwrite = (evt: Event) => { - if (typeof options === "object" && options !== null && options?.once === true) { - (this as any)[eventListenerSymbol]?.[eventName]?.delete(listener); + addEventListener( + eventName: string, + listener: (event: Event) => unknown, + options?: boolean | AddEventListenerOptions, + ) { + if (eventListenerSymbol in this === false) { + (this as any)[eventListenerSymbol] = {}; + } + if (eventName in (this as any)[eventListenerSymbol] === false) { + (this as any)[eventListenerSymbol][eventName] = new WeakMap(); } - const result = listener(evt); + const listenerOverwrite = (evt: Event) => { + if (typeof options === "object" && options !== null && options?.once === true) { + (this as any)[eventListenerSymbol]?.[eventName]?.delete(listener); + } - if (result instanceof Promise && active.eventPromises !== null) { - active.eventPromises.push(result); - } - }; + const result = listener(evt); - (this as any)[eventListenerSymbol][eventName].set(listener, listenerOverwrite); + if (result instanceof Promise && active.eventPromises !== null) { + active.eventPromises.push(result); + } + }; - super.addEventListener(eventName, listenerOverwrite, options); - } + (this as any)[eventListenerSymbol][eventName].set(listener, listenerOverwrite); - removeEventListener(this: HTMLElement, eventName: string, listener: (event: Event) => void) { - if ( - eventListenerSymbol in this === true && - eventName in (this as any)[eventListenerSymbol] === true - ) { - const listenerOverwrite = (this as any)[eventListenerSymbol][eventName].get(listener); + super.addEventListener(eventName, listenerOverwrite, options); + } - if (listenerOverwrite !== undefined) { - (this as any)[eventListenerSymbol][eventName].delete(listener); + removeEventListener(this: HTMLElement, eventName: string, listener: (event: Event) => void) { + if ( + eventListenerSymbol in this === true && + eventName in (this as any)[eventListenerSymbol] === true + ) { + const listenerOverwrite = (this as any)[eventListenerSymbol][eventName].get(listener); - super.removeEventListener(eventName, listenerOverwrite); + if (listenerOverwrite !== undefined) { + (this as any)[eventListenerSymbol][eventName].delete(listener); + + super.removeEventListener(eventName, listenerOverwrite); + } } } } + + return Component as any; } diff --git a/test/async.test.tsx b/test/async.test.tsx index 5cd908a..a92620b 100644 --- a/test/async.test.tsx +++ b/test/async.test.tsx @@ -1,5 +1,12 @@ import { expect } from "@esm-bundle/chai"; -import { createComponent, mount, dispatchEvent, WebComponent } from "@plusnew/webcomponent"; +import { + createComponent, + mount, + dispatchEvent, + WebComponent, + prop, + define, +} from "@plusnew/webcomponent"; import { signal } from "@preact/signals-core"; describe("webcomponent", () => { @@ -17,28 +24,24 @@ describe("webcomponent", () => { it("async event dispatch", async () => { const { promise, resolve } = Promise.withResolvers(); - const Component = createComponent( - "test-async-dispatch", - class Component extends WebComponent { - onfoo: (evt: CustomEvent) => void; - - #loading = signal(false); - render(this: Component) { - return ( - { - this.#loading.value = true; - try { - await Promise.all(dispatchEvent(this, "foo", { detail: null })); - } catch (_err) {} - this.#loading.value = false; - }} - /> - ); - } - }, - ); + @define("test-async-dispatch") + class Component extends WebComponent({ onfoo: prop<(evt: CustomEvent) => void>() }) { + #loading = signal(false); + render(this: Component) { + return ( + { + this.#loading.value = true; + try { + await Promise.all(dispatchEvent(this, "foo", { detail: null })); + } catch (_err) {} + this.#loading.value = false; + }} + /> + ); + } + } mount(() => promise} />, container); diff --git a/test/base.test.tsx b/test/base.test.tsx index 0b0ef4b..b428bf3 100644 --- a/test/base.test.tsx +++ b/test/base.test.tsx @@ -1,6 +1,5 @@ import { expect } from "@esm-bundle/chai"; -import { createComponent, mount, prop, WebComponent } from "@plusnew/webcomponent"; -import type { Signal } from "@preact/signals-core"; +import { define, mount, prop, WebComponent } from "@plusnew/webcomponent"; import { signal } from "@preact/signals-core"; describe("webcomponent", () => { @@ -16,18 +15,14 @@ describe("webcomponent", () => { }); it("creates basic component and updating its props", () => { - const Component = createComponent( - "test-base", - class Component extends WebComponent { - @prop() accessor foo: string; + @define("test-base") + class Component extends WebComponent({ foo: prop(), bar: prop() }) { + #baz = signal("baz"); - #baz = signal("baz"); - - render() { - return `${this.foo}-${this.#baz.value}`; - } - }, - ); + render() { + return `${this.foo}-${this.#baz.value}`; + } + } // @ts-expect-error component with no props given, should be an error ; @@ -42,7 +37,7 @@ describe("webcomponent", () => { expect(container.childNodes.length).to.equal(1); - const component = container.childNodes[0] as HTMLElement; + const component = container.childNodes[0] as InstanceType; expect(component.tagName).to.equal("TEST-BASE"); expect(component.className).to.equal("some-class"); expect(component.childNodes.length).to.equal(0); @@ -54,16 +49,14 @@ describe("webcomponent", () => { }); it("crates array based on given number", () => { - const Component = createComponent( - "test-array", - class Component extends WebComponent { - @prop() accessor amount: number; - - render() { - return [...Array(this.amount).keys()].map((value) =>
{value.toString()}
); - } - }, - ); + @define("test-array") + class Component extends WebComponent({ + amount: prop(), + }) { + render() { + return [...Array(this.amount).keys()].map((value) =>
{value.toString()}
); + } + } mount(() => , container); @@ -92,16 +85,12 @@ describe("webcomponent", () => { }); it("crates element if needed", () => { - const Component = createComponent( - "test-placeholder", - class Component extends WebComponent { - @prop() accessor show: boolean; - - render() { - return this.show === true &&
; - } - }, - ); + @define("test-placeholder") + class Component extends WebComponent({ show: prop() }) { + render() { + return this.show === true &&
; + } + } mount(() => , container); @@ -133,25 +122,21 @@ describe("webcomponent", () => { let containerRenderCount = 0; let nestedRenderCount = 0; - const Component = createComponent( - "test-container", - class Component extends WebComponent { - render() { - containerRenderCount++; - return ; - } - }, - ); - - const NestedComponent = createComponent( - "test-nest", - class Component extends WebComponent { - render() { - nestedRenderCount++; - return `${foo.value}`; - } - }, - ); + @define("test-container") + class Component extends WebComponent() { + render() { + containerRenderCount++; + return ; + } + } + + @define("test-nest") + class NestedComponent extends WebComponent() { + render() { + nestedRenderCount++; + return `${foo.value}`; + } + } mount(() => , container); @@ -178,41 +163,31 @@ describe("webcomponent", () => { let containerRenderCounter = 0; let nestedRenderCounter = 0; - const NestedComponent = createComponent( - "test-counter-constructor", - class Component extends WebComponent { - #counter: Signal; - constructor() { - super(); - - this.#counter = signal(0); - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - this.#counter.value; - } - render() { - nestedRenderCounter += 1; - return ( - - ); - } - }, - ); - - const Component = createComponent( - "test-container-rerender", - class Component extends WebComponent { - render() { - containerRenderCounter += 1; - return ; - } - }, - ); + @define("test-counter-constructor") + class NestedComponent extends WebComponent() { + #counter = signal(0); + + render() { + nestedRenderCounter += 1; + return ( + + ); + } + } + + @define("test-container-rerender") + class Component extends WebComponent() { + render() { + containerRenderCounter += 1; + return ; + } + } mount(() => , container); diff --git a/test/context.test.tsx b/test/context.test.tsx index a369646..98a82aa 100644 --- a/test/context.test.tsx +++ b/test/context.test.tsx @@ -1,10 +1,10 @@ import { expect } from "@esm-bundle/chai"; -import { mount, createComponent, findParent, WebComponent } from "@plusnew/webcomponent"; +import { mount, createComponent, findParent, WebComponent, define } from "@plusnew/webcomponent"; import { signal } from "@preact/signals-core"; const Provider = createComponent( "test-provider", - class Component extends WebComponent { + class Component extends WebComponent() { readonly foo = signal("bar"); render() { @@ -15,7 +15,7 @@ const Provider = createComponent( const Consumer = createComponent( "test-consumer", - class Component extends WebComponent { + class Component extends WebComponent() { render() { try { return findParent(Provider).foo.value; @@ -69,14 +69,12 @@ describe("webcomponent", () => { }); it("finds context inline", () => { - const Component = createComponent( - "test-inline", - class Component extends WebComponent { - render() { - return {findParent(Provider).foo.value}; - } - }, - ); + @define("test-inline") + class Component extends WebComponent() { + render() { + return {findParent(Provider).foo.value}; + } + } mount(() => , container); @@ -95,22 +93,20 @@ describe("webcomponent", () => { }); it("finds context in event", () => { - const Component = createComponent( - "test-event", - class Component extends WebComponent { - render() { - return ( - - { - expect(findParent(Provider).foo.value).to.equal("bar"); - }} - /> - - ); - } - }, - ); + @define("test-event") + class Component extends WebComponent() { + render() { + return ( + + { + expect(findParent(Provider).foo.value).to.equal("bar"); + }} + /> + + ); + } + } mount(() => , container); @@ -136,7 +132,7 @@ describe("webcomponent", () => { it("no context", () => { const Injection = createComponent( "test-injection", - class Component extends WebComponent { + class Component extends WebComponent() { render() { return ( diff --git a/test/error.test.tsx b/test/error.test.tsx index 998e34d..cca021e 100644 --- a/test/error.test.tsx +++ b/test/error.test.tsx @@ -1,5 +1,5 @@ import { expect } from "@esm-bundle/chai"; -import { createComponent, mount, WebComponent } from "@plusnew/webcomponent"; +import { define, mount, WebComponent } from "@plusnew/webcomponent"; import { signal } from "@preact/signals-core"; function error(): never { @@ -19,26 +19,24 @@ describe("webcomponent", () => { }); it("creates broken component and should display error", () => { - const Component = createComponent( - "test-broken", - class Component extends WebComponent { - #hasError = signal(false); - render() { - return this.#hasError.value ? ( - "error" - ) : ( -
{ - this.#hasError.value = true; - evt.preventDefault(); - }} - > - {error()} -
- ); - } - }, - ); + @define("test-broken") + class Component extends WebComponent() { + #hasError = signal(false); + render() { + return this.#hasError.value ? ( + "error" + ) : ( +
{ + this.#hasError.value = true; + evt.preventDefault(); + }} + > + {error()} +
+ ); + } + } mount(() => , container); @@ -55,7 +53,7 @@ describe("webcomponent", () => { // const Component = webcomponent( // "test-later-broken", - // class Component extends WebComponent { + // class Component extends WebComponent(){ // render() { // if (foo.value === true) { // return "good"; diff --git a/test/events.test.tsx b/test/events.test.tsx index 665ebbd..c4f9035 100644 --- a/test/events.test.tsx +++ b/test/events.test.tsx @@ -1,5 +1,12 @@ import { expect } from "@esm-bundle/chai"; -import { createComponent, dispatchEvent, mount, prop, WebComponent } from "@plusnew/webcomponent"; +import { + createComponent, + define, + dispatchEvent, + mount, + prop, + WebComponent, +} from "@plusnew/webcomponent"; import { signal } from "@preact/signals-core"; describe("webcomponent", () => { @@ -16,31 +23,26 @@ describe("webcomponent", () => { it("creates basic component and updating its props", () => { let counter = 0; - const Component = createComponent( - "test-base", - class Component extends WebComponent { - render() { - return ( - { - counter++; - expect(evt.detail).to.equal("mep"); - }} - /> - ); - } - }, - ); - - const NestedComponent = createComponent( - "test-nested", - class NestedComponent extends WebComponent { - onfoo: (evt: CustomEvent) => void; - render(this: NestedComponent) { - return - ); - return derefence(this.foo); - } - }, - ); + @define("test-dereference-container") + class Component extends WebComponent() { + #counter = signal(0); + render() { + return ( + { + counter++; + expect(evt.detail).to.eql(this.#counter.value + 1); + this.#counter.value = evt.detail; + }} + /> + ); + } + } + + @define("test-deference") + class NestedComponent extends WebComponent({ + foo: prop(), + onfoo: prop<(value: CustomEvent) => void>(), + }) { + render(this: NestedComponent) { + const derefence = (value: number) => ( + + ); + return derefence(this.foo); + } + } mount(() => , container); diff --git a/test/fragment.test.tsx b/test/fragment.test.tsx index 3ae8a80..41fcaed 100644 --- a/test/fragment.test.tsx +++ b/test/fragment.test.tsx @@ -1,5 +1,5 @@ import { expect } from "@esm-bundle/chai"; -import { createComponent, mount, prop, WebComponent } from "@plusnew/webcomponent"; +import { define, mount, WebComponent } from "@plusnew/webcomponent"; import { signal } from "@preact/signals-core"; describe("fragment", () => { @@ -16,23 +16,20 @@ describe("fragment", () => { it("creates basic component with fragment", () => { const baz = signal("foo"); - const Component = createComponent( - "test-base", - class Component extends WebComponent { - @prop() accessor foo: string; - - render() { - return ( - <> -
{baz.value}
- - - ); - } - }, - ); - - mount(() => , container); + + @define("test-base") + class Component extends WebComponent() { + render() { + return ( + <> +
{baz.value}
+ + + ); + } + } + + mount(() => , container); expect(container.childNodes.length).to.equal(1); diff --git a/test/host.test.tsx b/test/host.test.tsx index bb60deb..f483908 100644 --- a/test/host.test.tsx +++ b/test/host.test.tsx @@ -1,5 +1,5 @@ import { expect } from "@esm-bundle/chai"; -import { createComponent, mount, WebComponent } from "@plusnew/webcomponent"; +import { define, mount, WebComponent } from "@plusnew/webcomponent"; import { signal } from "@preact/signals-core"; describe("webcomponent", () => { @@ -17,14 +17,13 @@ describe("webcomponent", () => { xit("adds property and removes it", async () => { const add = signal(false); - const Component = createComponent( - "test-property", - class Component extends WebComponent { - render() { - return add.value ? : ; - } - }, - ); + define("test-property"); + class Component extends WebComponent() { + render() { + return add.value ? : ; + } + } + mount(() => , container); expect(container.childNodes.length).to.equal(1); @@ -44,20 +43,18 @@ describe("webcomponent", () => { }); it("registers click event", async () => { - const Component = createComponent( - "test-click", - class Component extends WebComponent { - #baz = signal(0); - - render() { - return ( - - ); - } - }, - ); + @define("test-click") + class Component extends WebComponent() { + #baz = signal(0); + + render() { + return ( + + ); + } + } mount(() => , container); @@ -89,21 +86,19 @@ describe("webcomponent", () => { }); it("registers input event and updating", async () => { - const Component = createComponent( - "test-input-update", - class Component extends WebComponent { - #baz = signal("foo"); - - render() { - return ( - (this.#baz.value = (evt.currentTarget as HTMLInputElement).value)} - value={this.#baz.value} - /> - ); - } - }, - ); + @define("test-input-update") + class Component extends WebComponent() { + #baz = signal("foo"); + + render() { + return ( + (this.#baz.value = (evt.currentTarget as HTMLInputElement).value)} + value={this.#baz.value} + /> + ); + } + } mount(() => , container); @@ -128,16 +123,14 @@ describe("webcomponent", () => { }); it("registers input event without updating", async () => { - const Component = createComponent( - "test-input-reject", - class Component extends WebComponent { - #baz = signal("foo"); + @define("test-input-reject") + class Component extends WebComponent() { + #baz = signal("foo"); - render() { - return null} value={this.#baz.value} />; - } - }, - ); + render() { + return null} value={this.#baz.value} />; + } + } mount(() => , container); @@ -165,24 +158,22 @@ describe("webcomponent", () => { const backgroundColor = signal(null); const fontColor = signal(null); - const Component = createComponent( - "test-style", - class Component extends WebComponent { - render() { - return backgroundColor.value === null && fontColor.value === null ? ( - - ) : ( - - ); - } - }, - ); + @define("test-style") + class Component extends WebComponent() { + render() { + return backgroundColor.value === null && fontColor.value === null ? ( + + ) : ( + + ); + } + } mount(() => , container); diff --git a/test/svg.test.tsx b/test/svg.test.tsx index ee15ad4..a9980f2 100644 --- a/test/svg.test.tsx +++ b/test/svg.test.tsx @@ -1,5 +1,5 @@ import { expect } from "@esm-bundle/chai"; -import { createComponent, mount, WebComponent } from "@plusnew/webcomponent"; +import { define, mount, WebComponent } from "@plusnew/webcomponent"; describe("svg", () => { let container: HTMLElement; @@ -14,24 +14,22 @@ describe("svg", () => { }); it("async event dispatch", async () => { - const Component = createComponent( - "test-svg", - class Component extends WebComponent { - render(this: Component) { - return ( - - - - ); - } - }, - ); + @define("test-svg") + class Component extends WebComponent() { + render(this: Component) { + return ( + + + + ); + } + } mount(() => , container); expect(container.childNodes.length).to.equal(1); - const component = container.childNodes[0] as HTMLElement; + const component = container.childNodes[0] as InstanceType; const element = component.shadowRoot?.childNodes[0] as SVGElement; expect(element.tagName).to.eql("svg"); diff --git a/tsconfig.json b/tsconfig.json index 1b73525..c8948d1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,7 @@ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "ES2024" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + "target": "ESNEXT" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ "jsx": "react-jsx" /* Specify what JSX code is generated. */, // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */