parts can have a custom render()

This commit is contained in:
Chris Wanstrath 2026-01-24 09:47:22 -08:00
parent e772e0e711
commit 6b6c9d3772
2 changed files with 83 additions and 2 deletions

View File

@ -196,7 +196,12 @@ function makeComponent(baseName: string, rootDef: TagDef, rootProps: Record<stri
classNames.push(variantKey === true ? variantName : `${variantName}-${variantKey}`) classNames.push(variantKey === true ? variantName : `${variantName}-${variantKey}`)
} }
return <Tag class={classNames.join(' ')} {...baseAttrs} {...props}>{children}</Tag> const finalProps = { class: classNames.join(' '), ...baseAttrs, ...props }
if (partName && def.render)
return def.render({ props: finalProps, children })
return <Tag {...finalProps}>{children}</Tag>
} }
} }
@ -315,4 +320,4 @@ function tagName(base: string): string {
} }
// shortcut so you only have to import one thing, if you want // shortcut so you only have to import one thing, if you want
define.Styles = Styles define.Styles = Styles

View File

@ -558,6 +558,82 @@ describe('custom render function', () => {
expect(html).toContain('<footer') expect(html).toContain('<footer')
expect(html).toContain('Main Content') expect(html).toContain('Main Content')
}) })
test('part can have its own render function', () => {
const Component = define('PartRender', {
parts: {
Icon: {
base: 'span',
color: 'blue',
render: ({ props, children }) => (
<span {...props}> {children}</span>
)
}
},
render: ({ props, parts }) => (
<parts.Root>
<parts.Icon>star</parts.Icon>
</parts.Root>
)
})
const html = renderToString(Component({}))
expect(html).toContain('★ star')
expect(html).toContain('class="PartRender_Icon"')
})
test('part render receives computed class and props', () => {
let receivedProps: any
const Component = define('PartRenderProps', {
parts: {
Item: {
base: 'li',
render: ({ props, children }) => {
receivedProps = props
return <li {...props}>{children}</li>
}
}
},
render: ({ props, parts }) => (
<parts.Root>
<parts.Item data-test="value">item</parts.Item>
</parts.Root>
)
})
renderToString(Component({}))
expect(receivedProps.class).toBe('PartRenderProps_Item')
expect(receivedProps['data-test']).toBe('value')
})
test('part render works with variants', () => {
const Component = define('PartRenderVariant', {
variants: {
size: {
small: { fontSize: 12 },
large: { fontSize: 24 }
}
},
parts: {
Label: {
fontWeight: 'bold',
render: ({ props, children }) => (
<span {...props}>Label: {children}</span>
)
}
},
render: ({ props, parts }) => (
<parts.Root>
<parts.Label>text</parts.Label>
</parts.Root>
)
})
const html = renderToString(Component({ size: 'large' }))
expect(html).toContain('Label: text')
expect(html).toContain('PartRenderVariant_Label')
})
}) })
describe('Styles component', () => { describe('Styles component', () => {