Skip to content

Commit

Permalink
New(React): Optional generic type
Browse files Browse the repository at this point in the history
  • Loading branch information
BenSeage committed Jan 4, 2023
1 parent 6809955 commit 9f5e6af
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 30 deletions.
27 changes: 9 additions & 18 deletions packages/react/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type MasterComponentProps<K extends IntrinsicElementsKeys | React.ComponentType<
type MasterExoticComponent<K extends IntrinsicElementsKeys | React.ComponentType<any>, E extends object = object> = React.ForwardRefExoticComponent<MasterComponentProps<K, E> & React.RefAttributes<K>> & { tag: K, params: TagParams };

type ParamsType<K extends IntrinsicElementsKeys | React.ComponentType<any>, E extends object = object> = Array<((props: MasterComponentProps<K, E>) => baseLoopType | undefined) | baseLoopType>;

type ReturnType<K extends IntrinsicElementsKeys | React.ComponentType<any>, E extends object = object> = <F extends TemplateStringsArray | MasterExoticComponent<any> | baseType>(
firstParam: F,
...params: F extends MasterExoticComponent<any, any>
Expand All @@ -27,25 +28,15 @@ type ReturnType<K extends IntrinsicElementsKeys | React.ComponentType<any>, E ex
: MasterExoticComponent<K, E>)

const style: {
[key in IntrinsicElementsKeys]: <F extends TemplateStringsArray | MasterExoticComponent<any, any> | baseType, E extends object = object>(
firstParam: F,
...params: F extends MasterExoticComponent<any, any>
? never
: ParamsType<key, E>
) => (F extends MasterExoticComponent<any, any>
? ReturnType<key, E>
: MasterExoticComponent<key, E>)
[key in IntrinsicElementsKeys]: (<F extends MasterExoticComponent<any, any>, E extends object = object>(firstParam: F) => F extends MasterExoticComponent<any, infer ME> ? ReturnType<key, ME & E> : never)
& (<E extends object = object>(firstParam: TemplateStringsArray | baseType, ...params: ParamsType<key, E>) => MasterExoticComponent<key, E>)
} & {
<F extends MasterExoticComponent<any>, E extends object = object>(firstParam: F): F extends MasterExoticComponent<infer K, infer ME> ? ReturnType<K, ME & E> : never
} & {
<E extends object = object>(firstParam: TemplateStringsArray | baseType, ...params: ParamsType<'div', E>): MasterExoticComponent<'div', E>
} & {
<F extends TemplateStringsArray | MasterExoticComponent<any> | React.ComponentType<any> | baseType, E extends object = object>(
firstParam: F,
...params: F extends MasterExoticComponent<any, any>
? never
: ParamsType<'div', E>
): (F extends MasterExoticComponent<infer K, infer E>
? ReturnType<K, E>
: F extends React.ComponentType<any>
? ReturnType<F>
: MasterExoticComponent<'div', E>)
//@ts-ignore
<F extends React.ComponentType<any>, E extends object = object>(firstParam: F, ...params: F extends React.ComponentType<infer RE> ? ParamsType<'div', RE & E> : never): F extends React.ComponentType<infer RE> ? ReturnType<React.ComponentType<RE & E>> :never
} = new Proxy(
((firstParam, ...params) => {
return (Array.isArray(firstParam) && 'raw' in firstParam || typeof firstParam !== 'object' || !('render' in firstParam))
Expand Down
24 changes: 12 additions & 12 deletions packages/react/tests/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import ReactDOMServer from 'react-dom/server'

const { renderToStaticMarkup } = ReactDOMServer

const Button = style.button<TemplateStringsArray, { $color: string }>`
const Button = style.button<{ $color: string }>`
inline-flex center-content
${['font:14', 'font:semibold']}
${{ test: true, test2: false, test3: true }}
Expand All @@ -18,22 +18,22 @@ test('Basic', () => {
})

test('Extend', () => {
const ExtendButton = style(Button)`bg:${({ $color }: any) => $color}-54:hover`
const ExtendButton = style(Button)`bg:${({ $color }) => $color}-54:hover`
expect(renderToStaticMarkup(<ExtendButton $color="blue">Extend</ExtendButton>))
.toBe('<button class="inline-flex center-content font:14 font:semibold test test3 fg:white px:18 h:40 r:4 bg:blue bg:blue-54:hover">Extend</button>')

const AButton = style.a<typeof Button, { $color: string }>(Button)``
const AButton = style.a(Button)``
expect(renderToStaticMarkup(<AButton $color="purple">Tag Extend</AButton>))
.toBe('<a class="inline-flex center-content font:14 font:semibold test test3 fg:white px:18 h:40 r:4 bg:purple">Tag Extend</a>')

const CustomComponent = forwardRef((props: { $type: string }, ref: any) => <a ref={ref} {...props}></a>)
const ExtendCustomComponent = style(CustomComponent)`inline-flex center-content font:14 font:semibold ${(props) => props.$type}`
expect(renderToStaticMarkup(<ExtendCustomComponent $type="CustomType">Extend Custom Component</ExtendCustomComponent>))
.toBe('<a class="inline-flex center-content font:14 font:semibold CustomType">Extend Custom Component</a>')
const ExtendCustomComponent = style<typeof CustomComponent, { $newType: string }>(CustomComponent)`inline-flex center-content font:14 font:semibold ${(props) => props.$type} ${(props) => props.$newType}`
expect(renderToStaticMarkup(<ExtendCustomComponent $newType="NewType" $type="CustomType">Extend Custom Component</ExtendCustomComponent>))
.toBe('<a class="inline-flex center-content font:14 font:semibold CustomType NewType">Extend Custom Component</a>')
})

test('Prop composition', () => {
const Button = style.button`font:semibold rounded
const Button = style.button<{ $intent: string, $size: string }>`font:semibold rounded
${{
intent: {
primary: 'bg:blue-50 fg:white bg:blue-60:hover',
Expand All @@ -47,9 +47,9 @@ test('Prop composition', () => {
${{ intent: 'primary', size: 'md', $: 'uppercase' }}
${({ $intent, $size }) => $intent && $size && 'font:italic'}
`

expect(renderToStaticMarkup(<Button $intent="primary" $size="md" />))
.toBe('<button class="font:semibold rounded font:italic uppercase bg:blue-50 fg:white bg:blue-60:hover font:16 py:2 px:4"></button>')

expect(renderToStaticMarkup(<Button intent="secondary" />))
.toBe('<button intent="secondary" class="font:semibold rounded bg:white fg:gray-80 b:gray-40 bg:gray-50:hover"></button>')

Expand All @@ -75,7 +75,7 @@ test('Prop composition', () => {
})

test('Alternative syntax', () => {
const Div = style(
const Div = style<{ $intent: string, $size: string }>(
'font:semibold rounded',
{
intent: {
Expand All @@ -88,12 +88,12 @@ test('Alternative syntax', () => {
}
},
{ intent: 'primary', size: 'md', $: 'uppercase' },
({ $intent, $size }) => $intent && $size && 'font:italic'
({ $intent, $size }) => $intent && $size && `font:italic`
)
expect(renderToStaticMarkup(<Div $intent="primary" $size="md" />))
.toBe('<div class="font:semibold rounded font:italic uppercase bg:blue-50 fg:white bg:blue-60:hover font:16 py:2 px:4"></div>')

const Button = style.button(
const Button = style.button<{ $intent: string, $size: string }>(
'font:semibold rounded',
{
intent: {
Expand Down Expand Up @@ -121,7 +121,7 @@ test('Alternative syntax', () => {
expect(renderToStaticMarkup(<Button disabled $intent="secondary" $size="lg" />))
.toBe('<button disabled="" class="font:semibold rounded font:italic b:2|solid|red bg:white fg:gray-80 b:gray-40 bg:gray-50:hover"></button>')

const ExtendButton = style(Button)(
const ExtendButton = style<typeof Button, { $intent: string, $size: string }>(Button)(
{
intent: {
primary: 'bg:blue-70 fg:black bg:blue-80:hover'
Expand Down

0 comments on commit 9f5e6af

Please sign in to comment.