Skip to content

Commit

Permalink
streamline & fix types, add more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
xeho91 committed Jun 16, 2024
1 parent b9e4fac commit fb52150
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 76 deletions.
27 changes: 25 additions & 2 deletions src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Component, ComponentProps, Snippet } from 'svelte';
import type { EmptyObject } from 'type-fest';
import type { EmptyObject, Simplify } from 'type-fest';
import { describe, expectTypeOf, it } from 'vitest';

import { defineMeta, type Args, type StoryContext } from '#index';
Expand All @@ -23,7 +23,6 @@ describe(defineMeta.name, () => {
const { Story, meta } = defineMeta({
component: Button,
args: {
lol: 'never',
// FIXME: allow mapping snippets to primitives
children: 'Click me' as unknown as Snippet,
},
Expand Down Expand Up @@ -59,3 +58,27 @@ describe("type helper for snippets 'StoryContext'", () => {
BaseStoryContext<(typeof meta)['args']>
>();
});

describe("component 'Story' destructured from 'defineMeta", () => {
const { Story } = defineMeta({
component: Button,
args: {
// FIXME: allow mapping snippets to primitives
children: 'Click me' as unknown as Snippet,
},
});

type TStoryProps = typeof Story extends __sveltets_2_IsomorphicComponent
? ComponentProps<typeof Story>
: never;

expectTypeOf<ComponentProps<Button>['children']>().not.toBeNullable();
expectTypeOf<Meta<Button>['args']>().toBeNullable();
expectTypeOf<NonNullable<Meta<Button>['args']>['children']>().toBeNullable();
expectTypeOf<TStoryProps>().toHaveProperty('name');
expectTypeOf<TStoryProps['name']>().not.toBeNullable();
expectTypeOf<TStoryProps['args']>().toBeNullable();
expectTypeOf<NonNullable<TStoryProps['args']>>().toHaveProperty('size');
expectTypeOf<NonNullable<TStoryProps['args']>>().toHaveProperty('children');
expectTypeOf<NonNullable<TStoryProps['args']>['children']>().toBeNullable();
});
17 changes: 8 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Args as BaseArgs } from '@storybook/types';
import type { Component, ComponentProps, SvelteComponent } from 'svelte';
import type { ComponentProps } from 'svelte';
import type { EmptyObject } from 'type-fest';

import type { Meta, StoryCmp, StoryContext as BaseStoryContext } from '#types';
Expand All @@ -14,23 +14,22 @@ export function defineMeta<
TCmp = TMeta['component'],
>(
meta: TMeta & {
args?: TCmp extends Component | SvelteComponent | __sveltets_2_IsomorphicComponent
? ComponentProps<TCmp>
: TMeta['args'];
args?: TCmp extends __sveltets_2_IsomorphicComponent ? ComponentProps<TCmp> : TMeta['args'];
}
) {
return {
// @ts-expect-error FIXME: Can anything be done here?
Story: Story as StoryCmp<TOverrideArgs, TMeta>,
meta: meta as TMeta,
meta,
};
}

export type Args<Component extends StoryCmp<any, any>> =
Component extends StoryCmp<infer _TOverrideArgs extends BaseArgs, infer TMeta extends Meta>
export type Args<TStoryCmp extends StoryCmp<any, any>> =
TStoryCmp extends StoryCmp<infer _TOverrideArgs extends BaseArgs, infer TMeta extends Meta>
? TMeta['args']
: never;

export type StoryContext<Component extends StoryCmp<any, any>> =
Component extends StoryCmp<infer _TOverrideArgs extends BaseArgs, infer TMeta extends Meta>
export type StoryContext<TStoryCmp extends StoryCmp<any, any>> =
TStoryCmp extends StoryCmp<infer _TOverrideArgs extends BaseArgs, infer TMeta extends Meta>
? BaseStoryContext<TMeta['args']>
: never;
7 changes: 2 additions & 5 deletions src/parser/extract/svelte/story/attributes.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import type { ComponentProps } from 'svelte';
import type { Attribute, Component } from 'svelte/compiler';

import type { Meta } from '#types';
import type { StoryCmpProps } from '#types';

import type Story from '#runtime/Story.svelte';

type StoryAttributes = Array<keyof ComponentProps<Story<Meta>>>;
type StoryAttributes = Array<keyof StoryCmpProps>;

interface Options<Attributes extends StoryAttributes> {
component: Component;
Expand Down
55 changes: 21 additions & 34 deletions src/runtime/Story.svelte
Original file line number Diff line number Diff line change
@@ -1,46 +1,33 @@
<script
lang="ts"
generics="const TOverrideArgs extends Args = EmptyObject, const TMeta extends Meta = Meta"
>
import type { Args, StoryAnnotations } from '@storybook/types';
<script lang="ts" generics="const TOverrideArgs extends Args, const TMeta extends Meta">
import type { Args } from '@storybook/types';
import type { Component, ComponentProps, Snippet, SvelteComponent } from 'svelte';
import type { EmptyObject } from 'type-fest';
import type { EmptyObject, SetOptional, Simplify } from 'type-fest';
import { useStoriesExtractor } from '#runtime/contexts/extractor.svelte';
import { useStoryRenderer, type StoryRendererContext } from '#runtime/contexts/renderer.svelte';
import { useStoriesTemplate } from '#runtime/contexts/template.svelte';
import { storyNameToExportName } from '#utils/identifier-utils';
import type { Meta, StoryCmpProps, SvelteRenderer } from '#types';
import type { Meta, StoryAnnotations, StoryCmpProps, SvelteRenderer } from '#types';
type Renderer = SvelteRenderer<
TMeta['component'] extends SvelteComponent
? TMeta['component']
: TMeta['component'] extends Component<infer Props>
? SvelteComponent<Props>
: TMeta['args'] extends Args
? SvelteComponent<TMeta['args']>
: SvelteComponent<Args>
>;
type TCmp =
TMeta extends Meta<infer T>
? T extends Component | SvelteComponent | __sveltets_2_IsomorphicComponent
? T
: T extends Args
? Component<T>
: never
: never;
type TArgs = Simplify<ComponentProps<TCmp> & TMeta['args']>;
type TStoryArgs = Simplify<SetOptional<TArgs, Extract<keyof TArgs, keyof TMeta['args']>>>;
type Annotations = StoryAnnotations<
// TODO: Verify if `Renderer` type is defined correctly
Renderer,
// FIXME: ... args (non-required? - what is TArgs supposed to be? from meta - defineMeta?)
TMeta['component'] extends SvelteComponent
? ComponentProps<TMeta['component']>
: TMeta['args'] extends Args
? TMeta['args']
: Args,
// FIXME: ... required args... I don't understand how they're picked (from the type parameters default)
TMeta['component'] extends SvelteComponent
? ComponentProps<TMeta['component']>
: TMeta['args'] extends Args
? TMeta['args']
: Args
>;
type Props = Partial<Annotations> & {
type Props = StoryAnnotations<TCmp, TArgs, TStoryArgs> & {
// TCmp?: TCmp;
// Trenderer?: TRenderer;
// TArgsFromMeta?: TArgsFromMeta;
// TArgs?: TArgs;
// TAnnotations?: StoryAnnotations;
// TStoryArgs?: TStoryArgs;
/**
* The content to render in the story, either as:
* 1. A snippet taking args and storyContext as parameters
Expand Down
8 changes: 3 additions & 5 deletions src/runtime/StoryRenderer.svelte
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
<script lang="ts" generics="const TMeta extends Meta = Meta">
import type { Component, ComponentProps } from 'svelte';
import type { Component } from 'svelte';
import { useStoryRenderer } from '#runtime/contexts/renderer.svelte';
import { emitCode } from '#runtime/emit-code';
import type { Meta, StoryContext } from '#types';
import type Story from './Story.svelte';
import type { Meta, StoryCmpProps, StoryContext } from '#types';
type Props = {
Stories: Component;
exportName: string;
args: ComponentProps<Story<TMeta>>['args'];
args: StoryCmpProps['args'];
storyContext: StoryContext<TMeta['args']>;
};
Expand Down
23 changes: 9 additions & 14 deletions src/runtime/contexts/template.svelte.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { getContext, hasContext, setContext } from 'svelte';

import type { Meta, StoryCmp, StoryCmpProps } from '#types';
import type { StoryCmpProps } from '#types';

const CONTEXT_KEYS = 'storybook-stories-template-snippet-context';

function buildContext<TMeta extends Meta = Meta>() {
function buildContext() {
let template = $state<StoryCmpProps['children']>();

function set(snippet?: typeof template) {
Expand All @@ -19,27 +19,22 @@ function buildContext<TMeta extends Meta = Meta>() {
};
}

type StoriesTemplateContext<TMeta extends Meta = Meta> = ReturnType<typeof buildContext<TMeta>>;
type StoriesTemplateContext = ReturnType<typeof buildContext>;

export function useStoriesTemplate<TMeta extends Meta = Meta>() {
export function useStoriesTemplate() {
if (!hasContext(CONTEXT_KEYS)) {
setContext(CONTEXT_KEYS, buildContext<TMeta>());
setContext(CONTEXT_KEYS, buildContext());
}

return getContext<StoriesTemplateContext<TMeta>>(CONTEXT_KEYS).template;
return getContext<StoriesTemplateContext>(CONTEXT_KEYS).template;
}

type InferMeta<TStory extends StoryCmp> =
TStory extends StoryCmp<infer _TOverrideArgs, infer TMeta extends Meta> ? TMeta : never;

export function setTemplate<TStoryCmp extends StoryCmp = StoryCmp>(
snippet?: StoriesTemplateContext<InferMeta<TStoryCmp>>['template']
): void {
export function setTemplate(snippet?: StoriesTemplateContext['template']): void {
if (!hasContext(CONTEXT_KEYS)) {
setContext(CONTEXT_KEYS, buildContext<InferMeta<TStoryCmp>>());
setContext(CONTEXT_KEYS, buildContext());
}

const ctx = getContext<StoriesTemplateContext<InferMeta<TStoryCmp>>>(CONTEXT_KEYS);
const ctx = getContext<StoriesTemplateContext>(CONTEXT_KEYS);

ctx.set(snippet);
}
7 changes: 2 additions & 5 deletions src/runtime/emit-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@ import { SourceType, SNIPPET_RENDERED } from '@storybook/docs-tools';
import { addons } from '@storybook/preview-api';
import type { StoryObj } from '@storybook/svelte';
import get from 'lodash-es/get';
import type { ComponentProps } from 'svelte';

import type { Meta, StoryContext } from '#types';

import type Story from './Story.svelte';
import type { Meta, StoryCmpProps, StoryContext } from '#types';

type Params = {
args: ComponentProps<Story>['args'];
args: StoryCmpProps['args'];
storyContext: StoryContext<Meta['args']>;
};

Expand Down
18 changes: 16 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {
Args,
ComponentAnnotations,
StrictArgs,
StoryAnnotations as BaseStoryAnnotations,
StoryContext as GenericStoryContext,
WebRenderer,
} from '@storybook/types';
Expand Down Expand Up @@ -50,9 +51,22 @@ export interface SvelteStoryResult<TCmp = any> {

export type StoryContext<TArgs = StrictArgs> = GenericStoryContext<SvelteRenderer, TArgs>;

export type StoryCmp<TOverrideArgs extends Args, TMeta extends Meta> = typeof Story<
export type StoryCmp<TOverrideArgs extends Args = Args, TMeta extends Meta = Meta> = typeof Story<
TOverrideArgs,
TMeta
>;

export type StoryCmpProps = ComponentProps<Story>;
export type StoryCmpProps = ComponentProps<Story<Args, Meta>>;

export type StoryAnnotations<
TCmp = any,
TArgs = Args,
TRequiredArgs = Partial<TArgs>,
> = BaseStoryAnnotations<
// Renderer
SvelteRenderer<TCmp>,
// All of the args - combining the component props and excluding the ones from meta - defineMeta
TArgs,
// Set all of the args specified in 'defineMeta' to be optional for Story
TRequiredArgs
>;

0 comments on commit fb52150

Please sign in to comment.