Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SolidJS framework feedback #20584

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
4f92c5d
Initial SolidJS framework scaffolding
webblocksapp Dec 31, 2022
d6055f7
Updated preset with vite-plugin-solid
webblocksapp Dec 31, 2022
1c7fa8e
Added solid renderer boilerplate
webblocksapp Dec 31, 2022
5b71882
Added solid as repro-template
webblocksapp Jan 1, 2023
f208284
Registered new generator for Solid
webblocksapp Jan 1, 2023
cfc1551
Fixed solid storybook auto migration error
webblocksapp Jan 1, 2023
f9ad586
Configured solid sandbox for further development
webblocksapp Jan 1, 2023
ce9113a
Initial Button component as SolidJS template
webblocksapp Jan 2, 2023
8b64fbd
Solid renderer definition
webblocksapp Jan 2, 2023
e157e03
Working Solid render initial prototype.
webblocksapp Jan 4, 2023
2ccf69d
Changed store structure for story context.
webblocksapp Jan 4, 2023
57d8f45
Added fine grained updates engine.
webblocksapp Jan 11, 2023
b1ed4a0
solid renderer tsup config for dev. WIP
webblocksapp Jan 11, 2023
17a701e
Merge remote-tracking branch 'upstream/next' into solid-js-framework
webblocksapp Jan 11, 2023
01c6384
Clean api definition for intercepting story props.
webblocksapp Jan 12, 2023
80d025f
Refactored reactivity with a decorator. Removed args mapper.
webblocksapp Jan 12, 2023
bf6dc7f
Updated minor patch versions.
webblocksapp Jan 13, 2023
ff32212
Refactored story context mapping.
webblocksapp Jan 13, 2023
8642a22
Added remount logic for globals at render.tsx
webblocksapp Jan 13, 2023
90ad0f1
Merge remote-tracking branch 'upstream/next' into solid-js-framework
webblocksapp Jan 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions code/frameworks/solid-vite/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Storybook for SolidJS
81 changes: 81 additions & 0 deletions code/frameworks/solid-vite/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"name": "@storybook/solid-vite",
"version": "7.0.0-beta.26",
"description": "Storybook for SolidJS and Vite: Develop SolidJS in isolation with Hot Reloading.",
"keywords": [
"storybook"
],
"homepage": "https://github.com/storybookjs/storybook/tree/main/frameworks/solid-vite",
"bugs": {
"url": "https://github.com/storybookjs/storybook/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/storybookjs/storybook.git",
"directory": "frameworks/solid-vite"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
},
"license": "MIT",
"exports": {
".": {
"node": "./dist/index.js",
"require": "./dist/index.js",
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts"
},
"./preset": {
"require": "./dist/preset.js",
"import": "./dist/preset.mjs",
"types": "./dist/preset.d.ts"
},
"./package.json": "./package.json"
},
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"files": [
"dist/**/*",
"template/**/*",
"README.md",
"*.js",
"*.d.ts"
],
"scripts": {
"check": "tsc --noEmit",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
"@storybook/addons": "7.0.0-beta.26",
"@storybook/builder-vite": "7.0.0-beta.26",
"@storybook/channel-postmessage": "7.0.0-beta.26",
"@storybook/channel-websocket": "7.0.0-beta.26",
"@storybook/client-api": "7.0.0-beta.26",
"@storybook/core-server": "7.0.0-beta.26",
"@storybook/node-logger": "7.0.0-beta.26",
"@storybook/preview-web": "7.0.0-beta.26",
"@storybook/solid": "7.0.0-beta.26",
"magic-string": "^0.26.1",
"vite-plugin-solid": "^2.5.0"
},
"devDependencies": {
"@types/node": "^16.0.0",
"typescript": "~4.9.3"
},
"engines": {
"node": "^14.18 || >=16"
},
"publishConfig": {
"access": "public"
},
"bundler": {
"entries": [
"./src/index.ts",
"./src/preset.ts"
],
"platform": "node"
},
"gitHead": "6559b419625c2dcf76bad1a12fcf75e3dd7c4187"
}
1 change: 1 addition & 0 deletions code/frameworks/solid-vite/preset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./dist/preset');
1 change: 1 addition & 0 deletions code/frameworks/solid-vite/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type { StorybookConfig } from '@storybook/builder-vite';
18 changes: 18 additions & 0 deletions code/frameworks/solid-vite/src/preset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { hasVitePlugins, StorybookConfig } from '@storybook/builder-vite';

export const core: StorybookConfig['core'] = {
builder: '@storybook/builder-vite',
renderer: '@storybook/solid',
};

export const viteFinal: StorybookConfig['viteFinal'] = async (config, { presets }) => {
const { plugins = [] } = config;

// Add solid plugin if not present
if (!(await hasVitePlugins(plugins, ['vite-plugin-solid']))) {
const { default: solidPlugin } = await import('vite-plugin-solid');
plugins.push(solidPlugin());
}

return config;
};
10 changes: 10 additions & 0 deletions code/frameworks/solid-vite/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"types": ["node"],
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["src/**/*.test.*", "src/**/__testfixtures__/**"]
}
3 changes: 3 additions & 0 deletions code/lib/cli/src/automigrate/fixes/new-frameworks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ const packagesMap: Record<string, { webpack5?: string; vite?: string }> = {
webpack5: '@storybook/html-webpack5',
vite: '@storybook/html-vite',
},
'@storybook/solid': {
vite: '@storybook/solid',
},
Comment on lines +50 to +52
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove this, as no one will be migrating from an old solid setup because that doesn't exist.

};

interface NewFrameworkRunOptions {
Expand Down
8 changes: 8 additions & 0 deletions code/lib/cli/src/generators/SOLID/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { baseGenerator } from '../baseGenerator';
import type { Generator } from '../types';

const generator: Generator = async (packageManager, npmOptions, options) => {
await baseGenerator(packageManager, npmOptions, options, 'solid');
};

export default generator;
6 changes: 6 additions & 0 deletions code/lib/cli/src/initiate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import svelteGenerator from './generators/SVELTE';
import svelteKitGenerator from './generators/SVELTEKIT';
import raxGenerator from './generators/RAX';
import serverGenerator from './generators/SERVER';
import solidGenerator from './generators/SOLID';
import type { JsPackageManager } from './js-package-manager';
import { JsPackageManagerFactory, useNpmWarning } from './js-package-manager';
import type { NpmOptions } from './NpmOptions';
Expand Down Expand Up @@ -204,6 +205,11 @@ const installStorybook = (
commandLog('Adding Storybook support to your "Server" app\n')
);

case ProjectType.SOLID:
return solidGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "SolidJS" app\n')
);

case ProjectType.UNSUPPORTED:
paddedLog(`We detected a project type that we don't support yet.`);
paddedLog(
Expand Down
12 changes: 11 additions & 1 deletion code/lib/cli/src/project_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ export type SupportedRenderers =
| 'aurelia'
| 'html'
| 'web-components'
| 'server';
| 'server'
| 'solid';

export const SUPPORTED_RENDERERS: SupportedRenderers[] = [
'react',
Expand All @@ -53,6 +54,7 @@ export const SUPPORTED_RENDERERS: SupportedRenderers[] = [
'svelte',
'rax',
'aurelia',
'solid'
];

export enum ProjectType {
Expand Down Expand Up @@ -82,6 +84,7 @@ export enum ProjectType {
RAX = 'RAX',
AURELIA = 'AURELIA',
SERVER = 'SERVER',
SOLID = 'SOLID',
}

export enum CoreBuilder {
Expand Down Expand Up @@ -284,6 +287,13 @@ export const supportedTemplates: TemplateConfiguration[] = [
return dependencies.every(Boolean);
},
},
{
preset: ProjectType.SOLID,
dependencies: ['solid-js'],
matcherFunction: ({ dependencies }) => {
return dependencies.every(Boolean);
},
},
];

// A TemplateConfiguration that matches unsupported frameworks
Expand Down
12 changes: 12 additions & 0 deletions code/lib/cli/src/repro-templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,17 @@ const baseTemplates = {
builder: '@storybook/builder-webpack5',
},
},
'solid-vite/default-ts': {
name: 'SolidJS Vite (TS)',
script: 'npx degit solidjs/templates/ts .',
expected: {
framework: '@storybook/solid-vite',
renderer: '@storybook/solid',
builder: '@storybook/builder-vite',
},
// TODO: remove this once solid-vite framework is released
inDevelopment: true,
},
'vue3-vite/default-js': {
name: 'Vue3 Vite (JS)',
script: 'yarn create vite . --template vue',
Expand Down Expand Up @@ -412,6 +423,7 @@ export const pr: TemplateKey[] = [
'vue3-vite/default-ts',
'vue-cli/vue2-default-js',
'lit-vite/default-ts',
'solid-vite/default-ts',
'svelte-vite/default-ts',
'svelte-kit/skeleton-ts',
'nextjs/default-ts',
Expand Down
2 changes: 2 additions & 0 deletions code/lib/cli/src/versions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ export default {
'@storybook/router': '7.0.0-beta.26',
'@storybook/server': '7.0.0-beta.26',
'@storybook/server-webpack5': '7.0.0-beta.26',
'@storybook/solid-vite': '7.0.0-beta.26',
'@storybook/solid': '7.0.0-beta.26',
'@storybook/source-loader': '7.0.0-beta.26',
'@storybook/store': '7.0.0-beta.26',
'@storybook/svelte': '7.0.0-beta.26',
Expand Down
1 change: 1 addition & 0 deletions code/lib/core-common/src/utils/get-storybook-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const rendererPackages: Record<string, string> = {
'@storybook/preact': 'preact',
'@storybook/rax': 'rax',
'@storybook/server': 'server',
'@storybook/solid': 'solid',
};

const logger = console;
Expand Down
17 changes: 13 additions & 4 deletions code/lib/preview-api/src/modules/preview-web/render/StoryRender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,17 @@ export class StoryRender<TRenderer extends Renderer> implements Render<TRenderer
if (!this.story) throw new Error('cannot render when not prepared');
if (!canvasElement) throw new Error('cannot render when canvasElement is unset');

const { id, componentId, title, name, tags, applyLoaders, unboundStoryFn, playFunction } =
this.story;
const {
id,
componentId,
title,
name,
tags,
applyLoaders,
unboundStoryFn,
playFunction,
prepareContext: prepareStoryContext,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's no reason to rename this to prepareStoryContext, just keep prepareContext

} = this.story;

if (forceRemount && !initial) {
// NOTE: we don't check the cancel actually worked here, so the previous
Expand All @@ -181,15 +190,15 @@ export class StoryRender<TRenderer extends Renderer> implements Render<TRenderer
return;
}

const renderStoryContext: StoryContext<TRenderer> = {
const renderStoryContext: StoryContext<TRenderer> = prepareStoryContext({
...loadedContext!,
// By this stage, it is possible that new args/globals have been received for this story
// and we need to ensure we render it with the new values
...this.storyContext(),
abortSignal,
// We should consider parameterizing the story types with TRenderer['canvasElement'] in the future
canvasElement: canvasElement as any,
};
});
const renderContext: RenderContext<TRenderer> = {
componentId,
title,
Expand Down
39 changes: 22 additions & 17 deletions code/lib/preview-api/src/modules/store/csf/prepareStory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,23 +71,10 @@ export function prepareStory<TRenderer extends Renderer>(
};

const undecoratedStoryFn: LegacyStoryFn<TRenderer> = (context: StoryContext<TRenderer>) => {
const mappedArgs = Object.entries(context.args).reduce((acc, [key, val]) => {
const mapping = context.argTypes[key]?.mapping;
acc[key] = mapping && val in mapping ? mapping[val] : val;
return acc;
}, {} as Args);

const includedArgs = Object.entries(mappedArgs).reduce((acc, [key, val]) => {
const argType = context.argTypes[key] || {};
if (includeConditionalArg(argType, mappedArgs, context.globals)) acc[key] = val;
return acc;
}, {} as Args);

const includedContext = { ...context, args: includedArgs };
const { passArgsFirst: renderTimePassArgsFirst = true } = context.parameters;
return renderTimePassArgsFirst
? (render as ArgsStoryFn<TRenderer>)(includedContext.args, includedContext)
: (render as LegacyStoryFn<TRenderer>)(includedContext);
? (render as ArgsStoryFn<TRenderer>)(context.args, context)
: (render as LegacyStoryFn<TRenderer>)(context);
};

// Currently it is only possible to set these globally
Expand All @@ -109,8 +96,13 @@ export function prepareStory<TRenderer extends Renderer>(
if (!render) throw new Error(`No render function available for storyId '${id}'`);

const decoratedStoryFn = applyHooks<TRenderer>(applyDecorators)(undecoratedStoryFn, decorators);
const unboundStoryFn = (context: StoryContext<TRenderer>) => {
const unboundStoryFn = (context: StoryContext<TRenderer>) => decoratedStoryFn(context);

// This function is invoked at StoryRender.ts, with this
// the context is prepared before invoking the render function.
Comment on lines +101 to +102
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// This function is invoked at StoryRender.ts, with this
// the context is prepared before invoking the render function.
// prepareContext is invoked at StoryRender.render()
// the context is prepared before invoking the render function, instead of here directly
// to ensure args don't loose there special properties set by the renderer
// eg. reactive proxies set by frameworks like SolidJS or Vue

const prepareContext = (context: StoryContext<TRenderer>) => {
let finalContext: StoryContext<TRenderer> = context;

if (global.FEATURES?.argTypeTargetsV7) {
const argsByTarget = groupArgsByTarget(context);
finalContext = {
Expand All @@ -121,7 +113,19 @@ export function prepareStory<TRenderer extends Renderer>(
};
}

return decoratedStoryFn(finalContext);
const mappedArgs = Object.entries(finalContext.args).reduce((acc, [key, val]) => {
const mapping = finalContext.argTypes[key]?.mapping;
acc[key] = mapping && val in mapping ? mapping[val] : val;
return acc;
}, {} as Args);

const includedArgs = Object.entries(mappedArgs).reduce((acc, [key, val]) => {
const argType = finalContext.argTypes[key] || {};
if (includeConditionalArg(argType, mappedArgs, finalContext.globals)) acc[key] = val;
return acc;
}, {} as Args);

return { ...finalContext, args: includedArgs };
};

const play = storyAnnotations?.play || componentAnnotations.play;
Expand Down Expand Up @@ -150,6 +154,7 @@ export function prepareStory<TRenderer extends Renderer>(
unboundStoryFn,
applyLoaders,
playFunction,
prepareContext,
};
}

Expand Down
1 change: 1 addition & 0 deletions code/lib/types/src/modules/story.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export type PreparedStory<TRenderer extends Renderer = Renderer> =
context: StoryContextForLoaders<TRenderer>
) => Promise<StoryContextForLoaders<TRenderer> & { loaded: StoryContext<TRenderer>['loaded'] }>;
playFunction?: (context: StoryContext<TRenderer>) => Promise<void> | void;
prepareContext: (context: StoryContext<TRenderer>) => StoryContext<TRenderer>;
};

export type PreparedMeta<TRenderer extends Renderer = Renderer> = Omit<
Expand Down
7 changes: 7 additions & 0 deletions code/renderers/solid/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"rules": {
"import/extensions": "off",
"react/react-in-jsx-scope": "off",
"import/no-extraneous-dependencies": "off"
}
}
1 change: 1 addition & 0 deletions code/renderers/solid/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Storybook SolidJS Renderer
7 changes: 7 additions & 0 deletions code/renderers/solid/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const path = require('path');
const baseConfig = require('../../jest.config.browser');

module.exports = {
...baseConfig,
displayName: __dirname.split(path.sep).slice(-2).join(path.posix.sep),
};
Loading