Skip to content
This repository was archived by the owner on Oct 23, 2023. It is now read-only.

Commit bc0f72a

Browse files
authored
feat(react,solid,preact): propagate additional props (#192)
* feat(react): propagate additional props * feat(solid): propagate additional props * feat(preact): propagate additional props * docs: update changeset
1 parent fcae6c9 commit bc0f72a

7 files changed

+65
-43
lines changed

.changeset/slow-students-peel.md

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
'@polymorphic-factory/preact': minor
3+
'@polymorphic-factory/react': minor
4+
'@polymorphic-factory/solid': minor
5+
---
6+
7+
Fixed an issue where the factory options type `polymorphicFactory<P, Options>()` did not propagate
8+
to the factory function `poly("div", options)`. **This is possibly a breaking change for TypeScript
9+
users.**
10+
11+
```tsx
12+
type AdditionalProps = Record<never, never>
13+
type Options = { 'data-custom-option': string }
14+
15+
const poly = polymorphicFactory<AdditionalProps, Options>({
16+
styled: (component, options) => (props) => {
17+
const Component = props.as || component
18+
return <Component data-custom-styled data-options={JSON.stringify(options)} {...props} />
19+
},
20+
})
21+
const CustomDiv = poly('div', { 'data-custom-option': 'hello' })
22+
```

packages/preact/src/polymorphic-factory.tsx

+12-11
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,21 @@ import type { JSX } from 'preact'
33

44
type DOMElements = keyof JSX.IntrinsicElements
55

6-
export type HTMLPolymorphicComponents = {
7-
[Tag in DOMElements]: ComponentWithAs<Tag>
6+
export type HTMLPolymorphicComponents<
7+
Props extends Record<string, unknown> = Record<never, never>,
8+
> = {
9+
[Tag in DOMElements]: ComponentWithAs<Tag, Props>
810
}
911

1012
export type HTMLPolymorphicProps<T extends ElementType> = Omit<PropsOf<T>, 'ref'> & {
1113
as?: ElementType
1214
}
1315

14-
type PolymorphFactory = {
15-
<
16-
T extends ElementType,
17-
P extends Record<string, unknown> = Record<never, never>,
18-
Options = never,
19-
>(
16+
type PolymorphFactory<
17+
Props extends Record<string, unknown> = Record<never, never>,
18+
Options = never,
19+
> = {
20+
<T extends ElementType, P extends Record<string, unknown> = Props>(
2021
component: T,
2122
option?: Options,
2223
): ComponentWithAs<T, P>
@@ -48,9 +49,9 @@ interface PolyFactoryParam<
4849
* <poly.section as="main" /> => // renders main
4950
*/
5051
export function polymorphicFactory<
51-
Component extends ElementType,
52-
Props extends Record<string, unknown>,
52+
Props extends Record<never, never>,
5353
Options = never,
54+
Component extends ElementType = ElementType,
5455
>({ styled = defaultStyled }: PolyFactoryParam<Component, Props, Options> = {}) {
5556
const cache = new Map<Component, ComponentWithAs<Component, Props>>()
5657

@@ -74,5 +75,5 @@ export function polymorphicFactory<
7475
}
7576
return cache.get(asElement)
7677
},
77-
}) as PolymorphFactory & HTMLPolymorphicComponents
78+
}) as PolymorphFactory<Props, Options> & HTMLPolymorphicComponents<Props>
7879
}

packages/preact/test/polymorphic-factory.test.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ describe('Polymorphic Factory', () => {
2626
})
2727

2828
describe('with custom styled function', () => {
29-
const customPoly = polymorphicFactory({
29+
const customPoly = polymorphicFactory<Record<never, never>, { customOption: string }>({
3030
styled: (component, options) => (props) => {
3131
const Component = props.as || component
3232
return <Component data-custom-styled data-options={JSON.stringify(options)} {...props} />
@@ -84,8 +84,7 @@ describe('Polymorphic Factory', () => {
8484

8585
it('should expect required props', () => {
8686
// @ts-expect-error Property 'customProp' is missing
87-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
88-
const _unused = <CustomComponent />
87+
render(<CustomComponent />)
8988
})
9089
})
9190
})

packages/react/src/polymorphic-factory.tsx

+12-11
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,21 @@ import { type ComponentWithAs, forwardRef, type PropsOf } from './forwardRef'
33

44
type DOMElements = keyof JSX.IntrinsicElements
55

6-
export type HTMLPolymorphicComponents = {
7-
[Tag in DOMElements]: ComponentWithAs<Tag>
6+
export type HTMLPolymorphicComponents<
7+
Props extends Record<string, unknown> = Record<never, never>,
8+
> = {
9+
[Tag in DOMElements]: ComponentWithAs<Tag, Props>
810
}
911

1012
export type HTMLPolymorphicProps<T extends ElementType> = Omit<PropsOf<T>, 'ref'> & {
1113
as?: ElementType
1214
}
1315

14-
type PolymorphFactory = {
15-
<
16-
T extends ElementType,
17-
P extends Record<string, unknown> = Record<never, never>,
18-
Options = never,
19-
>(
16+
type PolymorphFactory<
17+
Props extends Record<string, unknown> = Record<never, never>,
18+
Options = never,
19+
> = {
20+
<T extends ElementType, P extends Record<string, unknown> = Props>(
2021
component: T,
2122
option?: Options,
2223
): ComponentWithAs<T, P>
@@ -48,9 +49,9 @@ interface PolyFactoryParam<
4849
* <poly.section as="main" /> => // renders main
4950
*/
5051
export function polymorphicFactory<
51-
Component extends ElementType,
52-
Props extends Record<string, unknown>,
52+
Props extends Record<never, never>,
5353
Options = never,
54+
Component extends ElementType = ElementType,
5455
>({ styled = defaultStyled }: PolyFactoryParam<Component, Props, Options> = {}) {
5556
const cache = new Map<Component, ComponentWithAs<Component, Props>>()
5657

@@ -74,5 +75,5 @@ export function polymorphicFactory<
7475
}
7576
return cache.get(asElement)
7677
},
77-
}) as PolymorphFactory & HTMLPolymorphicComponents
78+
}) as PolymorphFactory<Props, Options> & HTMLPolymorphicComponents<Props>
7879
}

packages/react/test/polymorphic-factory.test.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { render, screen } from '@testing-library/react'
2-
import { type HTMLPolymorphicProps, polymorphicFactory } from '../src'
2+
import { HTMLPolymorphicProps, polymorphicFactory } from '../src'
33

44
describe('Polymorphic Factory', () => {
55
describe('with default styled function', () => {
@@ -26,7 +26,7 @@ describe('Polymorphic Factory', () => {
2626
})
2727

2828
describe('with custom styled function', () => {
29-
const customPoly = polymorphicFactory({
29+
const customPoly = polymorphicFactory<Record<never, never>, { customOption?: string }>({
3030
styled: (component, options) => (props) => {
3131
const Component = props.as || component
3232
return <Component data-custom-styled data-options={JSON.stringify(options)} {...props} />
@@ -84,8 +84,7 @@ describe('Polymorphic Factory', () => {
8484

8585
it('should expect required props', () => {
8686
// @ts-expect-error Property 'customProp' is missing
87-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
88-
const _unused = <CustomComponent />
87+
render(<CustomComponent />)
8988
})
9089
})
9190
})

packages/solid/src/polymorphic-factory.tsx

+12-11
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,21 @@ export type ComponentWithAs<T extends ValidComponent, Props = Record<never, neve
2626
Assign<Assign<ComponentProps<T>, Props>, { as?: ElementType }>
2727
>
2828

29-
export type HTMLPolymorphicComponents = {
30-
[Tag in DOMElements]: ComponentWithAs<Tag>
29+
export type HTMLPolymorphicComponents<
30+
Props extends Record<string, unknown> = Record<never, never>,
31+
> = {
32+
[Tag in DOMElements]: ComponentWithAs<Tag, Props>
3133
}
3234

3335
export type HTMLPolymorphicProps<T extends ElementType> = Omit<ComponentProps<T>, 'ref'> & {
3436
as?: ElementType
3537
}
3638

37-
type PolymorphFactory = {
38-
<
39-
T extends ElementType,
40-
P extends Record<string, unknown> = Record<never, never>,
41-
Options = never,
42-
>(
39+
type PolymorphFactory<
40+
Props extends Record<string, unknown> = Record<never, never>,
41+
Options = never,
42+
> = {
43+
<T extends ElementType, P extends Record<string, unknown> = Props>(
4344
component: T,
4445
option?: Options,
4546
): ComponentWithAs<T, P>
@@ -74,9 +75,9 @@ interface PolyFactoryParam<
7475
* <poly.section as="main" /> => // renders main
7576
*/
7677
export function polymorphicFactory<
77-
Component extends ElementType,
78-
Props extends Record<string, unknown>,
78+
Props extends Record<never, never>,
7979
Options = never,
80+
Component extends ElementType = ElementType,
8081
>({ styled = defaultStyled }: PolyFactoryParam<Component, Props, Options> = {}) {
8182
const cache = new Map<Component, ComponentWithAs<Component, Props>>()
8283

@@ -100,5 +101,5 @@ export function polymorphicFactory<
100101
}
101102
return cache.get(asElement)
102103
},
103-
}) as PolymorphFactory & HTMLPolymorphicComponents
104+
}) as PolymorphFactory<Props, Options> & HTMLPolymorphicComponents<Props>
104105
}

packages/solid/test/polymorphic-factory.test.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ describe('Polymorphic Factory', () => {
2828
})
2929

3030
describe('with custom styled function', () => {
31-
const customPoly = polymorphicFactory({
31+
const customPoly = polymorphicFactory<Record<never, never>, { customOption: string }>({
3232
styled: (originalComponent, options) => (props) => {
3333
const [local, others] = splitProps(props, ['as'])
3434
const component = local.as || originalComponent
@@ -95,8 +95,7 @@ describe('Polymorphic Factory', () => {
9595

9696
it('should expect required props', () => {
9797
// @ts-expect-error Property 'customProp' is missing
98-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
99-
const _unused = <CustomComponent />
98+
render(() => <CustomComponent />)
10099
})
101100
})
102101
})

0 commit comments

Comments
 (0)