Skip to content

Commit 81ce305

Browse files
committed
Fix #104
1 parent a9f0ec0 commit 81ce305

File tree

5 files changed

+44
-12
lines changed

5 files changed

+44
-12
lines changed

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jsx-dom",
3-
"version": "8.1.3",
3+
"version": "8.1.4",
44
"description": "JSX to document.createElement.",
55
"main": "index.js",
66
"module": "index.js",
@@ -67,7 +67,6 @@
6767
"eslint-plugin-react": "7.34.1",
6868
"eslint-plugin-react-hooks": "4.6.0",
6969
"fs-extra": "^11.2.0",
70-
"happy-dom": "^14.3.2",
7170
"husky": "9.0.11",
7271
"jsdom": "^24.0.0",
7372
"lint-staged": "^15.2.2",

src/jsx-dom.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export function createFactory(tag: string) {
111111
return createElement.bind(null, tag)
112112
}
113113

114-
export function Fragment(attr: { children: JSX.Element | JSX.Element[] }) {
114+
export function Fragment(attr: { children: (JSX.Element | JSX.Element)[] }) {
115115
const fragment = document.createDocumentFragment()
116116
appendChild(attr.children, fragment)
117117
return fragment
@@ -134,7 +134,11 @@ export class Component {
134134
function initComponentClass(Class: ComponentClass, attr, children) {
135135
attr = { ...attr, children }
136136
const instance = new Class(attr)
137-
return instance.render()
137+
const node = instance.render()
138+
if ("ref" in attr) {
139+
attachRef(attr.ref, instance)
140+
}
141+
return node
138142
}
139143

140144
// eslint-disable-next-line @typescript-eslint/no-unused-vars

src/ref.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { isObject } from "./util"
22

3-
interface Ref<T = Node> {
3+
interface Ref<T> {
44
current: null | T
55
}
66

7-
export function createRef<T extends Node = Node>(): Ref<T> {
7+
export function createRef<T>(): Ref<T> {
88
return Object.seal({ current: null })
99
}
1010

test/main.test.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,26 @@ describe("jsx-dom", () => {
354354
expect(node.className).toBe("container")
355355
expect(ref.current).toBeInstanceOf(HTMLButtonElement)
356356
})
357+
358+
// #104
359+
it("class component with ref", () => {
360+
class Button extends React.Component<{ className: string }> {
361+
render() {
362+
return <button className={this.props.className} />
363+
}
364+
}
365+
366+
const ref = lib.createRef<Button>()
367+
React.createElement(Button, { className: "", ref })
368+
const node = (
369+
<Button className="container" ref={ref}>
370+
Click me!
371+
</Button>
372+
)
373+
expect(node.className).toBe("container")
374+
console.log(ref)
375+
expect(ref.current).toBeInstanceOf(Button)
376+
})
357377
})
358378

359379
it("supports useImperativeHandle", () => {
@@ -603,10 +623,10 @@ describe("jsx-dom", () => {
603623

604624
it("supports fragments with explicit tag", () => {
605625
const frag = (
606-
<lib.Fragment>
626+
<React.Fragment>
607627
{[2]}
608628
<span>Bonjour</span>
609-
</lib.Fragment>
629+
</React.Fragment>
610630
)
611631
const nodes = frag.childNodes
612632
expect(nodes[0].nodeType).toBe(Node.TEXT_NODE)

types/index.d.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,16 @@ export function createElement<T extends Element>(
144144
): T
145145

146146
// Custom components
147-
export function createElement<P extends {}, T extends Element>(
148-
type: ComponentType<P, T>,
149-
props?: (Attributes & P) | null,
147+
export function createElement<T extends Element, Type extends ComponentClass<any, T>>(
148+
type: Type,
149+
props?:
150+
| (Attributes & PropOfComponent<Type> & { ref?: Ref<InstanceType<Type>> | undefined })
151+
| null,
152+
...children: ReactNode[]
153+
): T
154+
export function createElement<P extends {}, T extends Element, Type extends ComponentClass<P, T>>(
155+
type: Type,
156+
props?: (Attributes & P & { ref?: Ref<InstanceType<Type>> | undefined }) | null,
150157
...children: ReactNode[]
151158
): T
152159

@@ -228,7 +235,7 @@ export interface ComponentClass<P = {}, T extends Element = JSX.Element> {
228235
}
229236

230237
export class Component<P = {}, T extends Element = JSX.Element> {
231-
constructor(props: PropsWithChildren<P>)
238+
constructor(props: PropsWithChildren<P> & { ref?: Ref<any> })
232239
readonly props: PropsWithChildren<P>
233240
render(): T | null
234241
}
@@ -241,6 +248,8 @@ export type ComponentType<P = {}, T extends Element = JSX.Element> =
241248
| ComponentClass<P, T>
242249
| FunctionComponent<P, T>
243250

251+
type PropOfComponent<T> = T extends ComponentType<infer P> ? P : never
252+
244253
//
245254
// React Hooks
246255
// ----------------------------------------------------------------------

0 commit comments

Comments
 (0)