diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index c84e76c1b4..5e01aa412e 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -6,6 +6,10 @@ description: All notable changes will be documented in this file. ## [Unreleased] +## Added + +- Added `factory` component for `asChild` prop. + ## [0.1.0] - 2024-11-27 ### Added diff --git a/packages/svelte/src/lib/components/avatar/avatar-fallback.svelte b/packages/svelte/src/lib/components/avatar/avatar-fallback.svelte index e9f76c2e71..5a564ef623 100644 --- a/packages/svelte/src/lib/components/avatar/avatar-fallback.svelte +++ b/packages/svelte/src/lib/components/avatar/avatar-fallback.svelte @@ -1,13 +1,14 @@ -
- {@render props.children?.()} -
+ diff --git a/packages/svelte/src/lib/components/avatar/avatar-image.svelte b/packages/svelte/src/lib/components/avatar/avatar-image.svelte index 91a499aa16..b7c1966552 100644 --- a/packages/svelte/src/lib/components/avatar/avatar-image.svelte +++ b/packages/svelte/src/lib/components/avatar/avatar-image.svelte @@ -1,17 +1,18 @@ - + diff --git a/packages/svelte/src/lib/components/avatar/avatar-root-provider.svelte b/packages/svelte/src/lib/components/avatar/avatar-root-provider.svelte index 5227e042ba..f7d6ea171d 100644 --- a/packages/svelte/src/lib/components/avatar/avatar-root-provider.svelte +++ b/packages/svelte/src/lib/components/avatar/avatar-root-provider.svelte @@ -15,6 +15,7 @@ import { createSplitProps } from '$lib/utils/create-split-props' import { mergeProps } from '@zag-js/svelte' import { AvatarProvider } from './use-avatar-context' + import { Ark } from '../factory' const props: AvatarRootProviderProps = $props() const [{ value: avatar }, localProps] = createSplitProps()(props, ['value']) @@ -24,6 +25,4 @@ const mergedProps = $derived(mergeProps(avatar().getRootProps(), localProps)) -
- {@render props.children?.()} -
+ diff --git a/packages/svelte/src/lib/components/avatar/avatar-root.svelte b/packages/svelte/src/lib/components/avatar/avatar-root.svelte index fe2a33b01a..5d00365ba4 100644 --- a/packages/svelte/src/lib/components/avatar/avatar-root.svelte +++ b/packages/svelte/src/lib/components/avatar/avatar-root.svelte @@ -1,9 +1,9 @@ -
- {@render props.children?.()} -
+ diff --git a/packages/svelte/src/lib/components/factory/examples/basic.svelte b/packages/svelte/src/lib/components/factory/examples/basic.svelte new file mode 100644 index 0000000000..aae4166beb --- /dev/null +++ b/packages/svelte/src/lib/components/factory/examples/basic.svelte @@ -0,0 +1,31 @@ + + + + {#snippet render(props)} + + Ark UI + + {/snippet} + diff --git a/packages/svelte/src/lib/components/factory/factory.svelte b/packages/svelte/src/lib/components/factory/factory.svelte new file mode 100644 index 0000000000..b4f2907cb4 --- /dev/null +++ b/packages/svelte/src/lib/components/factory/factory.svelte @@ -0,0 +1,22 @@ + + +{#if asChild} + {@render render?.(propsFn)} +{:else} + + {@render children?.()} + +{/if} diff --git a/packages/svelte/src/lib/components/factory/factory.test.ts b/packages/svelte/src/lib/components/factory/factory.test.ts new file mode 100644 index 0000000000..cb49531281 --- /dev/null +++ b/packages/svelte/src/lib/components/factory/factory.test.ts @@ -0,0 +1,40 @@ +import { render, screen } from '@testing-library/svelte' +import user from '@testing-library/user-event' +import { describe, expect, it, vi } from 'vitest' +import ComponentUnderTest from './examples/basic.svelte' + +describe('Ark Factory', () => { + it('should render only the child', () => { + render(ComponentUnderTest) + + expect(() => screen.getByTestId('parent')).toThrow() + expect(screen.getByTestId('child')).toBeVisible() + }) + + it('should override existing props', () => { + render(ComponentUnderTest) + const child = screen.getByTestId('child') + expect(child.id).toBe('child') + // biome-ignore lint/complexity/useLiteralKeys: + expect(child.dataset['part']).toBe('child') + }) + + it('should merge styles and classes', () => { + render(ComponentUnderTest) + const child = screen.getByTestId('child') + expect(child).toHaveStyle({ background: 'red' }) + expect(child).toHaveStyle({ color: 'rgb(0, 0, 255)' }) + expect(child).toHaveClass('child parent') + expect(screen.getByText('Ark UI')).toBeVisible() + }) + + it('should merge events', async () => { + const onClickParent = vi.fn() + const onClickChild = vi.fn() + render(ComponentUnderTest, { onClickParent, onClickChild }) + await user.click(screen.getByTestId('child')) + + expect(onClickParent).toHaveBeenCalled() + expect(onClickChild).toHaveBeenCalled() + }) +}) diff --git a/packages/svelte/src/lib/components/factory/index.ts b/packages/svelte/src/lib/components/factory/index.ts new file mode 100644 index 0000000000..47d8a9c1c5 --- /dev/null +++ b/packages/svelte/src/lib/components/factory/index.ts @@ -0,0 +1 @@ +export { default as Ark } from './factory.svelte' diff --git a/packages/svelte/src/lib/components/index.ts b/packages/svelte/src/lib/components/index.ts index 886c6ec3a0..1f7ac46059 100644 --- a/packages/svelte/src/lib/components/index.ts +++ b/packages/svelte/src/lib/components/index.ts @@ -1 +1,2 @@ export * from './avatar' +export * from './factory' diff --git a/packages/svelte/src/lib/types.ts b/packages/svelte/src/lib/types.ts index bf034f9fe9..ea0cb0d974 100644 --- a/packages/svelte/src/lib/types.ts +++ b/packages/svelte/src/lib/types.ts @@ -1,9 +1,19 @@ -import type { SvelteHTMLElements } from 'svelte/elements' +import type { Snippet } from 'svelte' +import type { HTMLAttributes, SvelteHTMLElements } from 'svelte/elements' export type Assign = Omit & U export type Optional = Pick, K> & Omit export type CollectionItem = string | object -export type HTMLProps = SvelteHTMLElements[T] export type Accessor = () => T + +export type HTMLTag = keyof SvelteHTMLElements +export type PropsFn = (props?: HTMLProps) => HTMLAttributes + +export type HTMLProps = SvelteHTMLElements[T] +export type PolymorphicProps = { + asChild?: boolean + children?: Snippet + render?: Snippet<[PropsFn]> +}