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

feat(react): propagate additional props #192

Merged
merged 4 commits into from
Jan 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .changeset/slow-students-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
'@polymorphic-factory/preact': minor
'@polymorphic-factory/react': minor
'@polymorphic-factory/solid': minor
---

Fixed an issue where the factory options type `polymorphicFactory<P, Options>()` did not propagate
to the factory function `poly("div", options)`. **This is possibly a breaking change for TypeScript
users.**

```tsx
type AdditionalProps = Record<never, never>
type Options = { 'data-custom-option': string }

const poly = polymorphicFactory<AdditionalProps, Options>({
styled: (component, options) => (props) => {
const Component = props.as || component
return <Component data-custom-styled data-options={JSON.stringify(options)} {...props} />
},
})
const CustomDiv = poly('div', { 'data-custom-option': 'hello' })
```
23 changes: 12 additions & 11 deletions packages/preact/src/polymorphic-factory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@ import type { JSX } from 'preact'

type DOMElements = keyof JSX.IntrinsicElements

export type HTMLPolymorphicComponents = {
[Tag in DOMElements]: ComponentWithAs<Tag>
export type HTMLPolymorphicComponents<
Props extends Record<string, unknown> = Record<never, never>,
> = {
[Tag in DOMElements]: ComponentWithAs<Tag, Props>
}

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

type PolymorphFactory = {
<
T extends ElementType,
P extends Record<string, unknown> = Record<never, never>,
Options = never,
>(
type PolymorphFactory<
Props extends Record<string, unknown> = Record<never, never>,
Options = never,
> = {
<T extends ElementType, P extends Record<string, unknown> = Props>(
component: T,
option?: Options,
): ComponentWithAs<T, P>
Expand Down Expand Up @@ -48,9 +49,9 @@ interface PolyFactoryParam<
* <poly.section as="main" /> => // renders main
*/
export function polymorphicFactory<
Component extends ElementType,
Props extends Record<string, unknown>,
Props extends Record<never, never>,
Options = never,
Component extends ElementType = ElementType,
>({ styled = defaultStyled }: PolyFactoryParam<Component, Props, Options> = {}) {
const cache = new Map<Component, ComponentWithAs<Component, Props>>()

Expand All @@ -74,5 +75,5 @@ export function polymorphicFactory<
}
return cache.get(asElement)
},
}) as PolymorphFactory & HTMLPolymorphicComponents
}) as PolymorphFactory<Props, Options> & HTMLPolymorphicComponents<Props>
}
5 changes: 2 additions & 3 deletions packages/preact/test/polymorphic-factory.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe('Polymorphic Factory', () => {
})

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

it('should expect required props', () => {
// @ts-expect-error Property 'customProp' is missing
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _unused = <CustomComponent />
render(<CustomComponent />)
})
})
})
23 changes: 12 additions & 11 deletions packages/react/src/polymorphic-factory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@ import { type ComponentWithAs, forwardRef, type PropsOf } from './forwardRef'

type DOMElements = keyof JSX.IntrinsicElements

export type HTMLPolymorphicComponents = {
[Tag in DOMElements]: ComponentWithAs<Tag>
export type HTMLPolymorphicComponents<
Props extends Record<string, unknown> = Record<never, never>,
> = {
[Tag in DOMElements]: ComponentWithAs<Tag, Props>
}

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

type PolymorphFactory = {
<
T extends ElementType,
P extends Record<string, unknown> = Record<never, never>,
Options = never,
>(
type PolymorphFactory<
Props extends Record<string, unknown> = Record<never, never>,
Options = never,
> = {
<T extends ElementType, P extends Record<string, unknown> = Props>(
component: T,
option?: Options,
): ComponentWithAs<T, P>
Expand Down Expand Up @@ -48,9 +49,9 @@ interface PolyFactoryParam<
* <poly.section as="main" /> => // renders main
*/
export function polymorphicFactory<
Component extends ElementType,
Props extends Record<string, unknown>,
Props extends Record<never, never>,
Options = never,
Component extends ElementType = ElementType,
>({ styled = defaultStyled }: PolyFactoryParam<Component, Props, Options> = {}) {
const cache = new Map<Component, ComponentWithAs<Component, Props>>()

Expand All @@ -74,5 +75,5 @@ export function polymorphicFactory<
}
return cache.get(asElement)
},
}) as PolymorphFactory & HTMLPolymorphicComponents
}) as PolymorphFactory<Props, Options> & HTMLPolymorphicComponents<Props>
}
7 changes: 3 additions & 4 deletions packages/react/test/polymorphic-factory.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { render, screen } from '@testing-library/react'
import { type HTMLPolymorphicProps, polymorphicFactory } from '../src'
import { HTMLPolymorphicProps, polymorphicFactory } from '../src'

describe('Polymorphic Factory', () => {
describe('with default styled function', () => {
Expand All @@ -26,7 +26,7 @@ describe('Polymorphic Factory', () => {
})

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

it('should expect required props', () => {
// @ts-expect-error Property 'customProp' is missing
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _unused = <CustomComponent />
render(<CustomComponent />)
})
})
})
23 changes: 12 additions & 11 deletions packages/solid/src/polymorphic-factory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,21 @@ export type ComponentWithAs<T extends ValidComponent, Props = Record<never, neve
Assign<Assign<ComponentProps<T>, Props>, { as?: ElementType }>
>

export type HTMLPolymorphicComponents = {
[Tag in DOMElements]: ComponentWithAs<Tag>
export type HTMLPolymorphicComponents<
Props extends Record<string, unknown> = Record<never, never>,
> = {
[Tag in DOMElements]: ComponentWithAs<Tag, Props>
}

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

type PolymorphFactory = {
<
T extends ElementType,
P extends Record<string, unknown> = Record<never, never>,
Options = never,
>(
type PolymorphFactory<
Props extends Record<string, unknown> = Record<never, never>,
Options = never,
> = {
<T extends ElementType, P extends Record<string, unknown> = Props>(
component: T,
option?: Options,
): ComponentWithAs<T, P>
Expand Down Expand Up @@ -74,9 +75,9 @@ interface PolyFactoryParam<
* <poly.section as="main" /> => // renders main
*/
export function polymorphicFactory<
Component extends ElementType,
Props extends Record<string, unknown>,
Props extends Record<never, never>,
Options = never,
Component extends ElementType = ElementType,
>({ styled = defaultStyled }: PolyFactoryParam<Component, Props, Options> = {}) {
const cache = new Map<Component, ComponentWithAs<Component, Props>>()

Expand All @@ -100,5 +101,5 @@ export function polymorphicFactory<
}
return cache.get(asElement)
},
}) as PolymorphFactory & HTMLPolymorphicComponents
}) as PolymorphFactory<Props, Options> & HTMLPolymorphicComponents<Props>
}
5 changes: 2 additions & 3 deletions packages/solid/test/polymorphic-factory.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe('Polymorphic Factory', () => {
})

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

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