parts can have a custom render()

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

View File

@ -196,7 +196,11 @@ function makeComponent(baseName: string, rootDef: TagDef, rootProps: Record<stri
classNames.push(variantKey === true ? variantName : `${variantName}-${variantKey}`)
}
return <Tag class={classNames.join(' ')} {...baseAttrs} {...props}>{children}</Tag>
const finalProps = { class: classNames.join(' '), ...baseAttrs, ...props, children }
const content = (partName && def.render) ? def.render(finalProps) : children
return <Tag {...finalProps}>{content}</Tag>
}
}

View File

@ -558,6 +558,82 @@ describe('custom render function', () => {
expect(html).toContain('<footer')
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) => <> {props.children}</>
}
},
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"')
expect(html).toContain('<span') // base tag is preserved
})
test('part render receives props including children', () => {
let receivedProps: any
const Component = define('PartRenderProps', {
parts: {
Item: {
base: 'li',
render: (props) => {
receivedProps = props
return <>item: {props.children}</>
}
}
},
render: ({ props, parts }) => (
<parts.Root>
<parts.Item data-test="value">content</parts.Item>
</parts.Root>
)
})
const html = renderToString(Component({}))
expect(receivedProps.class).toBe('PartRenderProps_Item')
expect(receivedProps['data-test']).toBe('value')
expect(receivedProps.children).toBe('content')
expect(html).toContain('<li') // base tag preserved
expect(html).toContain('item: content')
})
test('part render works with variants', () => {
const Component = define('PartRenderVariant', {
variants: {
size: {
small: { fontSize: 12 },
large: { fontSize: 24 }
}
},
parts: {
Label: {
fontWeight: 'bold',
render: (props) => <>Label: {props.children}</>
}
},
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', () => {