Closed maraisr closed 1 year ago
Duplicate to #1870 and #3423. Let's continue a discussion there
New Starter build for Current stencil and SB 5 to be created
@Edd-Strickland want to work on a stencl support version 🎉
I've upgraded the polymer starter with stencil to the latest version of SB need some help in removing polymer and adding in stencil complier now
Hi,
@Edd-Strickland just for information, i have implemented Stencil inside Storybook like you did in your starter, in this project : https://github.com/vogloblinsky/nutrition-web-components
I have used the HTML starter of Storybook.
For now with Storybook & Stencil, i just had to :
The main problem i think is the usage of Webpack by Storybook to handle JavaScript files imported inside a story. The ideal workflow is to only imported the JS file of the Web Component.
Yeah this is what have done previously but with the polymer version however what this means is that by importing as plain static W/C implementations is you need to update each time into your story's which feels limiting.
Hi All, I have created a wrapper that can be installed on a stencil component
type project. Hope it helps. https://github.com/nisheed2440/stencil-storybook-wrapper
looks really good I'll test on Monday. Good work :)
Will this be made into an official part of Storybook? I have a desperate need for this!
@o-t-w We're trying, would you be able to help us?
@ndelangen I would be happy to test things and provide feedback/bug reports.
Would this work with LitElement (and web components in general) or just Stencil?
@nisheed2440 your wrapper seems promising, I will test this soon! But it could be great to have a "native" integration documented by Storybook 👌
@nisheed2440 I have been very busy(sorry everyone) but have had a very small window today to test a very vanilla version of this locally and it's really good. works really well.
going to spend some time on this next week trying to incorporate it into an existing project to see how this might work for existing stencil users / projects.
I have tested it this morning and it works pretty well too! GJ it's really easy to setup. I have installed and tested some addons:
import '@storybook/addon-backgrounds/register';
import '@storybook/addon-knobs/register';
import '@storybook/addon-actions/register';
import '@storybook/addon-notes/register';
Everything works fine, just found one issue with addon-knobs https://github.com/storybooks/storybook/issues/5017 But there is a workaround and this should be fixed pretty soon I think.
I spent the last 2 weeks playing with StencilJS and Storybook and did a livestream where I cover my solution. I feel there is a much better way, but I was able to get HMR, and most plugins to work with little issue. Would love any feedback you guys have on how to improve or import the loaders from the distribution stencil bundle.
https://www.youtube.com/watch?v=XwHtPw3izLE
And here is the repo! ^_^ https://github.com/MadnessLabs/enjin-components
@nisheed2440 Hello, i m using an approach very similar to yours and everything is working expect chromatic. were you able to make chromatic work with stencil/storybook? when i run, it does discover all my stories but all the screenshots are empty. it s probably missing the stencil when trying to render the component screenshot on chromatic server
@nisheed2440 Thank you so much for this really great effort. Hopefully this gives the team here a head start in the right direction. Stencil and Storybooks are ideal for each other.
Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!
Anybody want to pick this up?
My team is using StencilJS + Storybook for our common component library and I'd love to contribute. Maybe a few of us can get this thing back on track...
Seems like there's a lot of interest, e.g. https://twitter.com/dboskovic/status/1120336958008348672
One easy win would be publishing a @storybook/preset-stencil
package which packages @popcorn245 's config into a storybook preset. I still need to finish off the docs for that, but I'm using it for the upcoming Storybook Docs release and it's straightforward & how most SB config will work in the future.
I'd be happy to guide anybody who wants to pick that up.
Hey @shilman, stoked so many people are psyched to pickup on this and implement Stencil with Storybook. That thread has some good things that I have found, but there are many more little bugs like having to return a string of the element in order to use knobs.
A much better implementation would piggy-back off of the Stencil compiler and allow for use of JSX like with React components, but that is MHO.
Also, Stencil One is about to drop with some huge changes so it may be good to put peepers on this Changelog to make sure whoever is working on this is aware of what is coming down the pipeline.
https://github.com/ionic-team/stencil/blob/core-refactor/BREAKING_CHANGES.md
This thread was immensely helpful to me, especially @popcorn245’s config. Personally I was using @stencil/state-tunnel
, which broke that config. Fortunately I was able to get it to work with some minor ~hacks~ tweaks by running:
npm i -D webpack@4.28
And adding this to .storybook/webpack.config.js
:
const { existsSync, readdirSync } = require('fs');
const { resolve } = require('path');
const CopyPlugin = require('copy-webpack-plugin');
module.exports = ({ config }) => {
// 1. Transpile @stencil modules with Babel
const babelLoader = config.module.rules.find(
({ use }) => use && use[0].loader === 'babel-loader'
);
babelLoader.exclude = [/node_modules\/(?!\@stencil).*/];
if (babelLoader.use[0].options) {
babelLoader.use[0].options.plugins = ['@babel/plugin-syntax-dynamic-import'];
}
// 2. Load JS & CSS from our components
config.entry.push(resolve(__dirname, '..', 'dist', 'MYCOMPONENTNAME.js'));
config.entry.push(resolve(__dirname, '..', 'dist', 'MYCOMPONENTNAME.css'));
const components = resolve(__dirname, '..', 'dist', 'collection', 'components');
readdirSync(components).map(file => {
jsFilePath = resolve(components, file, `${file}.js`);
try {
if (existsSync(jsFilePath)) config.entry.push(jsFilePath);
} catch (err) {
console.error(err);
}
cssFilePath = resolve(components, file, `${file}.css`);
try {
if (existsSync(cssFilePath)) config.entry.push(cssFilePath);
} catch (err) {
console.error(err);
}
});
// 3. Fix dynamic imports for Storybook
// IMPORTANT: webpack must be at 4.28 due to a bug. See here: https://github.com/webpack/webpack/issues/8656
config.plugins.push(
new CopyPlugin([
{
from: resolve(__dirname, '..', 'dist'),
to: resolve(__dirname, '..', 'node_modules', '@storybook', 'core', 'dist', 'public'),
},
])
);
return config;
};
Starting to experiment with this also and (as mentioned somewhere else) using concurrently
seems to work just fine for me (for now). I created a quick starter project that includes everything you need to get up and running with both stencil and storybook. Already using the latest stencil release.
Check it out here: stencil-storybook-starter
@fvaldes33 Nice! Starred it. I actually just updated to Stencil One beta and my config looks similar—I basically could use the stock webpack setup entirely.
The only difference for me was using stencil build --watch
(prod, not dev) because the build times are so fast and it’s easier to consume the prod version in Stencil (especially with global styles and other imports).
@fvaldes33 how are you able to reference the build/components.js in your preview-head.html like that? I have to supply the full path e.g. http://localhost:3333/build/components.js. But I would like to not have to do that.
(I'm not using your starter, but i'm using the stencil component starter with a fresh storybook/html install)
EDIT: realized i was starting storybook on port 6006 instead of in the www folder. problem solved!
Looks like lots of us have similar solutions out there (including me https://github.com/jagreehal/stencil-boilerplate), but I'd really like hot/live updates when I edit a Stencil component. Currently I have to manually refresh the browser to reload Storybook.
Is there a bullet list of requirements to complete this? I'd be happy to pitch in if I knew what needed to be built.
What is the current state? Can we contribute? I would love to see this!
I suggested contributing a preset above.
If somebody wanted to put together a preset based on the patterns above, I'd be happ to help on the Storybook side. I'm not familiar with the Stencil side.
I just posted my project out there for anybody who wants it. Here are the features:
@Props
on your componentsLet me know what you think: https://github.com/DesignByOnyx/stencil-storybook-starter
@DesignByOnyx This looks great. Tweet about this and mention @storybookjs
and I'll RT from the storybook account. And if you want to write a post about it, I'd be happy to work with you to get it publicized. I think there's a pretty large demand here.
Amazing job @DesignByOnyx ! It seems that this fits perfectly to be a preset :tada:
OK, I've tweeted (I don't twitter much). Furthermore I don't have a blog :/, but I'm glad to put something together if someone wants to publish it.
While the project works, I threw it together in a hurry and did not really make it easy to customize. Some of the code in there is really brittle as I am having to load and merge multiple files in order to render each individual component. I'm hoping for some feedback before I spend any time to make this more consumable.
I'm curious to see what a preset would look like. The biggest thing that would be nice is a JSX preset which is not react. This would enable a little bit easier rendering and template generation on top of the storybook-html variety, and it doesn't have much to do with stencil. Several addons would also need to be updated to make this usable, and I'm not sure I'm the best to coordinate that effort. Either way, let me know what I can do to help.
@DesignByOnyx Any chance you can hop on our Discord? https://discordapp.com/invite/UUt2PJb
I'd love to chat more about getting this work out there on the Storybook blog as well as promoting in the stencil community.
I've been playing with @storybook/html
for Stencil and the experience pretty much "just works". You essentially do the following:
concurrently
to start the Storybook server and stencil build --watch
"in parallel"storybook
with the -s dist
flag, so that your Stencil dist
is served as static files.Configure .storybook/preview-head.html
to include a script tag like:
<script type="module" src="/$PACKAGE_NAME/$PACKAGE_NAME.esm.js"></script>
<script nomodule="" src="/$PACKAGE_NAME/$PACKAGE_NAME.js"></script>
And... that's it! The out-of-the-box html
support works for all your web component needs.
What I'd like to see is something like @storybook/stencil
that has the same experience (and code) as the html
package on the story authoring side, but
concurrently
is not requiredscript
tags for youIs there interest in something like that? I'm in the process of selling my company on Stencil and Storybook, and assuming that gains traction, I'll have "work time" to make that story (excuse the pun) really nice around Storybook + Stencil playing together.
The work that @DesignByOnyx has done is really great, but you kind of have to start your Stencil components with that kit and ignore the "normal" documentation for Stencil. If Storybook can provide a package that can be layered on top of the "normal" Stencil starter kit, you can easily add a Storybook config to an existing set of Stencil components.
Thanks for the great summary @alexlafroscia. I think your proposal makes a lot of sense. Does HMR not kick in automatically when Stencil rebuilds? If so, any idea why not?
@igor-dv Is it possible to add to preview-head.html
in a preset?
@Hypnosphi Maybe this is an interesting example for your multi-framework efforts. In this case no decorator is needed (apparently) but an entire compiler is needed.
@alexlafroscia how does an example of story look like in your case?
In the company where I work, we've been playing with Storybook HTML and StencilJS packages for a while. I would be happy to contribute!
@alexlafroscia Very great ideas, indeed it would be nice to have a complete support for this compiler. Here are some other ideas:
This article covers the on-going roadmap in StencilJS: Fall 2019 Stencil Roadmap. Notably:
Public Compiler APIs
Another area we’re also focusing on is ensuring the compiler can work within a browser and used by other tools. We’ve already been working with a few awesome teams such as Stackblitz, CodeSandbox, and WebComponents.dev. At the lowest levels, the compiler already works without running atop a NodeJS environment, so technically this isn’t a major refactor, but more-so just exposing the correct APIs.
We’re also seeing many areas for improvement to ensure the compiler is easy to consume by other NodeJS tools, including Rollup, Parcel, WebPack, and Bazel. If you’re a maintainer of any tooling, whether an online tool or a NodeJS environment, and you’re looking to implement the Stencil compiler, please feel free to reach out and we’ll be happy to help!
May be useful!
Does HMR not kick in automatically when Stencil rebuilds? If so, any idea why not?
@shilman It doesn't kick in because there's no "real" connection, in the setup that I have, between Storybook and Stencil. It's just a simple <script>
tag pointing to the built assets.
how does an example of story look like in your case?
@Hypnosphi They look something like this (a story for the default my-component
that Stencil generates in the initial package they create when you npm init stencil
import { document, console } from 'global';
import { storiesOf } from '@storybook/html';
storiesOf('My Component', module)
.add('without a middle name', () => `
<my-component
first="Alex"
last="LaFroscia"
></my-component>
`)
.add('with a middle name', () => `
<my-component
first="Alex"
middle="Robert"
last="LaFroscia"
></my-component>
`);
Use StencilJS JSX capabilities (based on Preact at the moment) to write stories in a more maintainable way. Using plain old JS or even template literals might be cumbersome...
@darondel I totally agree with the concerns around the developer experience of not having JSX in the Story authoring files. I've used that approach in the past, before @storybook/html
was available, and used the React experience which was OK.
Part of wanting to keep things as close to the "default" html
experience is so that the stories act as documentation on how to actually use them from the HTML perspective -- otherwise they are tied to something like Preact, which at least in my organization, is not being used anywhere else (we are primarily an Ember.js shop).
You mentioned that template tags wouldn't be a great experience, but I think that something like htm
could be a nice option. It also keeps the build process nice and simple, because there is no required build step, but might make it easier to interact with something like Knobs.
I was also thinking that trying to integrate with something like the upcoming DocsPage might be interesting! I'll bet that some of the work that's already been done by @DesignByOnyx could be useful here, so that there's a pathway to reading a Stencil component's "metadata" to generate the documentation information automatically. Probably not a "v1" concern, but something that would be really cool to see for a "v1.1"! I really like your idea of making something like that auto-knobs addon too, that would be really handy!
With DocsPages released today with Storybook 5.2, I did some research into whether it would be possible to get the information about props and such out of Stencil and rendered into Storybook. I think it should be possible, but definitely highlights how it would be useful to have an addon or preset tailored to using Storybook with Stencil to house a bunch of the "glue" needed for that.
I'm going to mess around with things a bit more this week and see if I can put something together.
@alexlafroscia would love to standardize how different frameworks communicate this data. Have seen something interesting from Jetbrains (web_types? Cc @elevatebart ) and also @atanasster is also doing work in this area for caching prop types in JSON files for performance. I think we should unify all of this in 6.0
I'm not familiar with the Jetbrains work -- I'll have to check that out! If you have any specific information that would be helpful to review, I'd love if you could send it my way!
In the case of Stencil, what I think the "best bet" will be is to have the Stencil build process output the JSON docs into either a location that's well-known by a Stencil Storybook addon or is configurable. That object contains all of the information on props expected, events emitted, and even the contents of the readme
file for each component (without the auto-generated props documentation). I think we could build a really compelling story for populating the Storybook DocsPage with the information from that JSON file.
```json { "timestamp": "2019-09-18T14:30:38", "compiler": { "name": "@stencil/core", "version": "1.3.2", "typescriptVersion": "3.5.3" }, "components": [ { "tag": "fluid-banner", "encapsulation": "shadow", "readme": "# fluid-banner\n\nThis is the contents of the README!\n", "docs": "This is the contents of the README!", "docsTags": [], "usage": {}, "props": [], "methods": [], "events": [], "styles": [], "slots": [] }, { "tag": "fluid-button", "encapsulation": "shadow", "readme": "# fluid-button\n\n\n", "docs": "", "docsTags": [], "usage": {}, "props": [ { "name": "destructive", "type": "boolean", "mutable": false, "attr": "destructive", "reflectToAttr": false, "docs": "Whether to display in the `destructive` style", "docsTags": [], "default": "false", "optional": false, "required": false }, { "name": "disabled", "type": "boolean", "mutable": false, "attr": "disabled", "reflectToAttr": false, "docs": "Whether the button should be treated as `disabled`", "docsTags": [], "default": "false", "optional": false, "required": false }, { "name": "plain", "type": "boolean", "mutable": false, "attr": "plain", "reflectToAttr": false, "docs": "Whether to display in the `plain` style", "docsTags": [], "default": "false", "optional": false, "required": false }, { "name": "primary", "type": "boolean", "mutable": false, "attr": "primary", "reflectToAttr": false, "docs": "Whether to display in the `primary` style", "docsTags": [], "default": "false", "optional": false, "required": false }, { "name": "size", "type": "\"large\" | \"medium\" | \"small\"", "mutable": false, "attr": "size", "reflectToAttr": true, "docs": "The size to display the button", "docsTags": [], "default": "\"medium\"", "optional": false, "required": false } ], "methods": [], "events": [], "styles": [], "slots": [] } ] } ```
It's kind of a hack (I have it writing the JSON output into dist/output.json
and then use fetch
to grab the file) but I was able to get the DocsPage rendering for a Storybook component by just overriding the slot props that the DocsPage
component can take.
The Props table isn't perfect, but it's pretty good; the Stencil output provides all the props that the table expects, and then some. Whatever is in the readme.md
for the component will be rendered at the top of the file.
```js import React, { useContext, useEffect, useState } from "react"; import { DocsPage, DocsContext } from "@storybook/addon-docs/blocks"; export const StorybookDocsPage = () => { const docsContext = useContext(DocsContext); const [payload, setPayload] = useState(null); useEffect(function() { fetch("./output.json") .then(res => res.json()) .then(res => setPayload(res)); }); if (!payload) { return null; } const component = payload.components.find(component => docsContext.selectedKind.includes(component.tag) ); // Empty because we will use the whole component README const titleSlot = () => ""; const subtitleSlot = () => ""; const descriptionSlot = () => component.readme; const propsSlot = () => ({ rows: component.props.map(prop => ({ name: prop.name, type: prop.type, description: prop.docs, required: prop.required, defaultValue: prop.default })) }); return React.createElement( DocsPage, { titleSlot, subtitleSlot, descriptionSlot, propsSlot }, null ); }; ```
Update: Going a step further, I defined a whole custom DocsPage
(rather than just overriding the slots) to get a second table with documentation of any custom styles.
```js import { createElement as e, useContext, useEffect, useState } from "react"; import { DocsPage, PropsTable } from "@storybook/components"; import { H2, H3 } from "@storybook/components/html"; import { Anchor, Description, DocsContext, Preview, Story } from "@storybook/addon-docs/blocks"; function useStencilComponent() { const docsContext = useContext(DocsContext); const [payload, setPayload] = useState(null); useEffect(function() { fetch("./output.json") .then(res => res.json()) .then(res => setPayload(res)); }); if (!payload) { return undefined; } return payload.components.find(component => docsContext.selectedKind.includes(component.tag) ); } const DocsStory = ({ id, name, expanded = true, withToolbar = false, parameters }) => e( Anchor, { storyId: id }, expanded && e(H3, null, (parameters && parameters.displayName) || name), expanded && parameters && parameters.docs && parameters.docs.storyDescription && e(Description, { markdown: parameters.docs.storyDescription }, null), e(Preview, { withToolbar }, e(Story, { id, height: "auto" }, null)) ); export const CustomDocsPage = () => { const docsContext = useContext(DocsContext); const component = useStencilComponent(); if (!component) { return null; } const { selectedKind, storyStore } = docsContext; const stories = storyStore.getStoriesForKind(selectedKind); const [primary, ...otherStories] = stories; const propDocs = component.props.length ? [ e(H2, null, "Props"), e( PropsTable, { rows: component.props.map(prop => ({ name: prop.name, type: prop.type, description: prop.docs, required: prop.required, defaultValue: JSON.parse(prop.default) })) }, null ) ] : []; const styleDocs = component.styles.length ? [ e(H2, null, "Styles"), e( PropsTable, { rows: component.styles.map(style => ({ name: style.name, description: style.docs })) }, null ) ] : []; const additionalStories = otherStories.length ? [ e(H2, null, "Stories"), ...otherStories.map(story => e( DocsStory, { key: story.id, ...story, expanded: true, withToolbar: false }, null ) ) ] : []; return e( DocsPage, null, e(Description, { markdown: component.readme }, null), e( DocsStory, { key: primary.id, ...primary, expanded: false, withToolbar: true }, null ), ...propDocs, ...styleDocs, ...additionalStories ); }; ```
The custom page also fixes the fact that each story defaults to 500px
in height, which is waaay too tall 😅
@alexlafroscia This is amazing, tremendous work!
FYI, we're going to generalize the prop table stuff in 5.3. Specifically, frameworks like Vue have the concept of slots & events, so those should be split out into their own tables. Maybe your styles work could use the same mechanism. https://github.com/storybookjs/storybook/issues/8123
The Jetbrains project I was referring to is this one (cc @piotrtomiak): https://github.com/JetBrains/web-types
I haven't looked at it in detail, and don't know that it's right for us. However, if it does meet our use cases and doesn't add too much extra cruft, I'd rather use an existing standard rather than invent our own.
@shilman Thanks for looking at our (JetBrains) effort on bringing some standard for metadata information exchange to web component libraries! Our initial drive was to simply provide good code completion for Vue components in HTML files, but we found out that there is much more to achieve with such a standard, so we designed it having a generic information exchange format in mind (IDEs, tooling, documenation). So far, our focus was on Vue framework, however we've always kept in mind support for Web Components or other frameworks. The web-types
standard is pretty fresh, but we've already heard some positive feedback from Vue community and users. I am actively promoting the specification in Vue community, but it's so great to get some interest from other community!
I think there might be some things missing in the web-types
JSON schema, which would be specific to your framework and those can be added to the spec. Vue specific items are for instance prefixed with vue
. There is also missing whole section for documenting CSS support, which we could work on to include. So, if you feel it's worth giving web-types
a chance feel free to file issues or create PRs for missing features.
The side-effect of documenting your components in web-types
format will be a good code completion when developers would include your library in their project. We are planning to implement such a generic support based on common web-types
features for all frameworks in a very near future. I am pretty sure that with a larger community acceptance of the format other IDEs will follow with support for the format, which would benefit everyone :)
@alexlafroscia fantastic work! The Stencil and Storybook integration (along with https://github.com/storybookjs/storybook/issues/7644) is looking good
Thanks for linking to that issue! I originally went down a similar path (trying to somehow use the existing README files and pull them directly into the DocsPage) but ultimately found it easier just to use the data that Stencil puts in the docs JSON file, since it does not include the table of props and everything (since that data is elsewhere in the JSON file and the structured data is perfect for creating a custom table from).
@alexlafroscia Thanks for sharing your findings. Can I ask how can I debug the CustomDocsPage
?
I've tried adding it with addParameters
and it doesn't seem to be using the custom but the default instead.
The WC itself is loaded in the storybook docs page, even without using the CustomDocsPage
.
Is there a way I can debug the component? I tried adding some logs, but I can't see any.
Thanks in advance.
Describe the solution you'd like I'd like to see Stencil.js support, as I see Storybook to be very component focused, and Stencil being a very component focus framework - these tools would compliment each other very well.
Are you able to assist bring the feature to reality? yes, I can...