<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Luca</title>
    <description>The latest articles on DEV Community by Luca (@lucabro81).</description>
    <link>https://dev.to/lucabro81</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F403660%2Fafb12e80-b8c6-417e-8d7e-37a572860d4b.jpeg</url>
      <title>DEV Community: Luca</title>
      <link>https://dev.to/lucabro81</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lucabro81"/>
    <language>en</language>
    <item>
      <title>Vue (2.x), Storybook (5.x), Web Components and nothing else</title>
      <dc:creator>Luca</dc:creator>
      <pubDate>Sat, 03 Oct 2020 17:08:38 +0000</pubDate>
      <link>https://dev.to/lucabro81/vue-2-x-storybook-5-x-web-components-e-nothing-else-5ak6</link>
      <guid>https://dev.to/lucabro81/vue-2-x-storybook-5-x-web-components-e-nothing-else-5ak6</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/lucabro81/vue-2-x-storybook-5-x-web-components-e-nient-altro-4l29"&gt;Versione italiana&lt;/a&gt;&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
Intro

&lt;ul&gt;
&lt;li&gt;What is Vue.js?&lt;/li&gt;
&lt;li&gt;What are Web Components?&lt;/li&gt;
&lt;li&gt;What is Storybook?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Definition of the problem

&lt;ul&gt;
&lt;li&gt;Create a test project&lt;/li&gt;
&lt;li&gt;Add Storybook&lt;/li&gt;
&lt;li&gt;Create a web component&lt;/li&gt;
&lt;li&gt;Problem with styles&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Hypothesis on the solution&lt;/li&gt;

&lt;li&gt;

Implementation of a solution

&lt;ul&gt;
&lt;li&gt;vue_config.js&lt;/li&gt;
&lt;li&gt;Include the web component in the story&lt;/li&gt;
&lt;li&gt;Register the component&lt;/li&gt;
&lt;li&gt;Integrate the interface in the stories&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusions and credits&lt;/li&gt;

&lt;/ul&gt;




&lt;h1&gt;
  
  
  Intro &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;h3&gt;
  
  
  What is &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue.js&lt;/a&gt;? &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Let's see what the docs say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Vue (pronounced /vjuː/, like view) is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable. The core library is focused on the view layer only, and is easy to pick up and integrate with other libraries or existing projects. On the other hand, Vue is also perfectly capable of powering sophisticated Single-Page Applications when used in combination with modern tooling and supporting libraries.&lt;br&gt;
[...]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, Vue is a frameworks used to build frontend projects. It's pretty easy to use and the template code requested is minimal, it's however quite performant, indeed it was able to gain a respectable place near giants like React and Angular.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are &lt;a href="https://www.webcomponents.org/" rel="noopener noreferrer"&gt;Web Components&lt;/a&gt;? &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;We've read a lot about web components in the last few years, and a lot we will read about it in the next future, so I will bring only a little synthesis: web components, in short, are none other than frontend components that, once registered by the browser and therefore recognized by it, can be used like normal tags with their attributes, parameters and peculiar behaviour.&lt;br&gt;
They can be defined via js vanilla classes or a framework that supports them, specifically, as it's easly to guess, in this post we will talk about web components defined through Vue.js.&lt;/p&gt;
&lt;h3&gt;
  
  
  What is &lt;a href="https://storybook.js.org/" rel="noopener noreferrer"&gt;Storybook&lt;/a&gt;? &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Storybook is an excellent tool useful when we have to test visually UI components, it's compatible with all major frameworks js and it can be used with js vanilla. All we have to do is to specify which component we have to render, to provide some mock data and let storybook instantiate our component in its own iframe and that's it. The criticality with vue arises from the difficulty of being able to instantiate simple web components without using other dependencies.&lt;/p&gt;


&lt;h1&gt;
  
  
  Definition of the problem &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;h3&gt;
  
  
  Create a test project &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Create web components with Vue it's not a problem, &lt;a href="https://cli.vuejs.org/guide/build-targets.html#web-component" rel="noopener noreferrer"&gt;there's a powerful cli that permit to specify an appropriate target for this task&lt;/a&gt; and, with some tricks, it's possible to test them even with the develop server.&lt;/p&gt;

&lt;p&gt;Let's go now a little more in details, the procedure to define a web components with Vue is definitely trivial, let's start from a normal Vue project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vue create vue-webcomponent-storybook-test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;my configuration was &lt;strong&gt;typescript, babel, scss (dart-sass) e basic linter on save&lt;/strong&gt;. &lt;br&gt;
What we will obtain will be a tree like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── dist
├── node_modules
├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── assets
│   │   └── logo.png
│   ├── components
│   │   └── HelloWorld.vue
│   ├── App.vue
│   ├── main.ts
│   ├── shims-tsx.d.ts
│   └── shims-vue.d.ts
├── .gitignore
├── babel.config.js
├── package.json
├── README.md
├── tsconfig.json
├── vue.config.js
└── yarn.lock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If everything went smoothly, from the terminal, running &lt;code&gt;yarn serve&lt;/code&gt;, we'll see our Vue app with the test component &lt;code&gt;HelloWorld.vue&lt;/code&gt; make a fine show of its self on &lt;code&gt;http://localhost:8080/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fczzjw7civ3cvw2ksxrg3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fczzjw7civ3cvw2ksxrg3.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Add Storybook &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The next step is to install &lt;a href="https://github.com/storybookjs/vue-cli-plugin-storybook" rel="noopener noreferrer"&gt;Storybook&lt;/a&gt; via the Vue plugin manager, also this operation like the last one it's not difficult at all:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vue add storybook
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Storybook will add some files and folders:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── config
│   └── storybook
│       └── storybook.js
├── dist
├── node_modules
├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── assets
│   │   └── logo.png
│   ├── components
│   │   ├── Helloworld.vue
│   │   └── MyButton.vue
│   ├── stories
│   │   ├── index.stories.js
│   │   └── index.stories.mdx
│   ├── App.vue
│   ├── main.ts
│   ├── shims-tsx.d.ts
│   └── shims-vue.d.ts
├── .gitignore
├── babel.config.js
├── package.json
├── README.md
├── tsconfig.json
├── vue.config.js
└── yarn.lock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We can safely delete the component in &lt;code&gt;src/components/MyButton.vue&lt;/code&gt; and the story in &lt;code&gt;src/stories/index.stories.mdx&lt;/code&gt;, they will no longer needed for our project. &lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;src/stories/index.stories.js&lt;/code&gt; file we create a story form the component &lt;code&gt;App.vue&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Now, running the task &lt;code&gt;storybook:serve&lt;/code&gt;, a test server will start and it will permit to run Storybook and testing our component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run storybook:serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fap8xjr30hwfcxkhavltf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fap8xjr30hwfcxkhavltf.png" alt="Storybook first run"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(At the time of writing it seems that starting storybook with yarn is not possible).&lt;/p&gt;
&lt;h3&gt;
  
  
  Create a web component &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Now we have to wrap our component (we will work with the default root component, &lt;code&gt;App.vue&lt;/code&gt;, this will permit us to see how inclusion of others style's components works, however what we're talking about is replicable with any other component) in a class that extends &lt;code&gt;HTMLElement&lt;/code&gt;. This operation will not be done by us, but through an api provided by Vue. At the end of this step the &lt;code&gt;main.ts&lt;/code&gt; file will appear like this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;&lt;code&gt;customElements.define&lt;/code&gt;(&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/customElements" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/API/Window/customElements&lt;/a&gt;) is part of the js api that actually permit to register the component to the browser with the tag name &lt;code&gt;my-web-component&lt;/code&gt;.&lt;br&gt;
A little side note, if you're, like me, using typescript, you may need to add to the file &lt;code&gt;shim-vue.d.ts&lt;/code&gt; the definition of the module &lt;code&gt;@vue/web-component-wrapper&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;declare module '@vue/web-component-wrapper';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In this way you'll avoid the error &lt;code&gt;Could not find a declaration file for module '@vue/web-component-wrapper'.&lt;/code&gt; that on ide like IntelliJ and similar, may appear. it's odd that there isn't a d.ts pre-intalled that solve the problem.&lt;/p&gt;

&lt;p&gt;At this point in the &lt;code&gt;index.html&lt;/code&gt; of our project (in &lt;code&gt;public/index.html&lt;/code&gt;) we've to get rid of the predefined root component (il div con &lt;code&gt;id="app"&lt;/code&gt;) and replace it with the newly registered one. Our index will therefore be:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;h3&gt;
  
  
  Problem with styles &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Running now the &lt;code&gt;yarn serve&lt;/code&gt; command we're going to see our component work like a charm, right?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxkeey38gwtb42324rmjr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxkeey38gwtb42324rmjr.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well nope...&lt;/p&gt;

&lt;p&gt;I mean yes, but actually no... where the hell are my styles????&lt;/p&gt;

&lt;p&gt;The trouble is that Vue included the styles in the tag &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the page like always, but the component it's closed in a &lt;strong&gt;shadow dom&lt;/strong&gt; (&lt;a href="https://w3c.github.io/webcomponents/spec/shadow/" rel="noopener noreferrer"&gt;https://w3c.github.io/webcomponents/spec/shadow/&lt;/a&gt;), a sort of event horizon through which it's difficult (not impossible, something passes after all) to pass information.&lt;/p&gt;

&lt;p&gt;And with Storybook instead? Well, things are that the problem remains. Modifying the &lt;code&gt;index.stories.js&lt;/code&gt; like this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And registering the component before using it (Storybook seem to not use how we include in the &lt;code&gt;main.ts&lt;/code&gt;), it is possible render it, but styles are not present:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8hctdcrn5gui2nxmchr4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8hctdcrn5gui2nxmchr4.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Hypothesis on the solution &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;A possible solution is described &lt;a href="https://github.com/vuejs/vue-web-component-wrapper/issues/12#issuecomment-385141573" rel="noopener noreferrer"&gt;here&lt;/a&gt;, it seems that the option &lt;code&gt;shadowMode&lt;/code&gt; of &lt;a href="https://vue-loader.vuejs.org/" rel="noopener noreferrer"&gt;vue-loader&lt;/a&gt; is &lt;code&gt;false&lt;/code&gt; by &lt;a href="https://github.com/vuejs/vue-loader/blob/bcee5b9127800070fe5797fe4be466641b88acc8/docs/options.md#shadowmode" rel="noopener noreferrer"&gt;default&lt;/a&gt;, hence the strange behaviour previously seen. At this point set &lt;code&gt;true&lt;/code&gt; that property may be solve the problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  vue_config.js &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;All we have to do now is the &lt;a href="https://cli.vuejs.org/config/#vue-config-js" rel="noopener noreferrer"&gt;vue_config.js&lt;/a&gt; in the root of the project, if not exists yet, we have to create it.&lt;/p&gt;

&lt;p&gt;To know what to fill our file with it's necessary to inspect the webpack configuration of the project, with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vue inspect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The results it seems to this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;If we look closely at this output, we can notice some interesting comments, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* config.module.rule('css').oneOf('vue').use('vue-style-loader') */
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;illustrating the api needed to generate that particular piece of configuration, this api, indeed, it's part of &lt;code&gt;webpack-chain&lt;/code&gt; (&lt;a href="https://github.com/neutrinojs/webpack-chain" rel="noopener noreferrer"&gt;https://github.com/neutrinojs/webpack-chain&lt;/a&gt;) tool used to facilitate the drafting of configuration files for webpack. Since it's already installed in the project, we can use for our purposes.&lt;/p&gt;

&lt;p&gt;Obviously the parts of the configuration that interested us, are those where the property &lt;code&gt;shadowMode: false&lt;/code&gt; appears, below the extract of the interested parts:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;What we put in the &lt;code&gt;vue_config.js&lt;/code&gt; will be intercepted from webpack anche integrated in the transpiling process, at the end, with the help of the documentation, we will obtain something like this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;this script add &lt;code&gt;shadowMode:false&lt;/code&gt; everywhere is needed and permit webpack to proceed with the compilation process, finally we'll obtain a web component correctly rendered with its own styles incapsulated:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fupz6otlx1t19p159axo1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fupz6otlx1t19p159axo1.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Include the web component in the story (&lt;a&gt;&lt;/a&gt;)
&lt;/h3&gt;

&lt;p&gt;if we run storybook now, we can see our component correctly rendered, however here the storybook api doesn't help us: how we can pass data to the component? What if these data are complex objects? How it's possible to interface with the component through the api exposed by the knob addon?&lt;/p&gt;

&lt;p&gt;Ok let's proceed with order:&lt;/p&gt;

&lt;h3&gt;
  
  
  Register the component &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This is easy, each component must be registered as we said before, one possibility is to implement a function that checks if the component is already registered and if not proceed accordingly, something like:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Really simple, elements that are not registered yet has &lt;code&gt;HTMLElement()&lt;/code&gt; constructor, it's sufficient check it and that's it.&lt;/p&gt;

&lt;p&gt;Subsequelty, the component must be registered:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Here too, nothing new, the procedure is the same seen before, only closed in a function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integrate the interface in the stories &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Now we need to make sure we can use the &lt;code&gt;addon-knobs&lt;/code&gt; to be able to pass data to our component and make it reactive to the changes that we can make during tests. &lt;br&gt;
My solution was to build a function that returns a component and subsequentialy retrieves its reference to pass any data:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Let's try to understrand what this script actually do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const webComponentWrapper = ({props, template}) =&amp;gt; {
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In input an object is expected, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;props: {
  test: [
    ['test', true, 'GROUP-ID1'],
    boolean
  ],
},
template: '&amp;lt;test-component&amp;gt;&amp;lt;/test-component&amp;gt;'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;formed by the property props which it will be another object, its element will have as keys the name of the property of the component and as value an array where the first element will be an array formed by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;property name (yes, too much redudancy),&lt;/li&gt;
&lt;li&gt;value that will be considered&lt;/li&gt;
&lt;li&gt;and the label that we want to assign to that specific knob. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second value, instead, will be the function of the addon-knobs that will be used to process that specific data type (in this case &lt;code&gt;boolean&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;&lt;code&gt;template&lt;/code&gt; is a string that rapresent the component and what it contains.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
const id = generateRandomNumber(0, 10 ** 16);
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here generate a random id that will be pass to the component and used to retrieve its reference, I've create a specific function, but you can use a timestamp without any problem.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
for (const key in props) {
  if (Object.hasOwnProperty.call(props, key)) {

    const old = key + 'Old' + id;
    const value = key + 'Value' + id;

    props[old] = null;
    props[value] = () =&amp;gt; (props[old] !== null) ? props[old] : props[key][0][1];
  }
}
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's start working on the data to pass to the component: first of all we retrieve the property &lt;code&gt;props&lt;/code&gt; and scroll through its contents, for each element, we decorate it with two others properties (the &lt;code&gt;old&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt; variables), to the first we give &lt;code&gt;null&lt;/code&gt;, to the second a function that will returns the old value (&lt;code&gt;old&lt;/code&gt;) or the default one passed with the properties in &lt;code&gt;props&lt;/code&gt; (be patient it's painful for me as it is for you), to understand the value &lt;code&gt;true&lt;/code&gt; in &lt;code&gt;['test', true, 'GROUP-ID1']&lt;/code&gt; that we talk about above, depending on weather the old value exists or not.&lt;/p&gt;

&lt;p&gt;Every time, in Storybook, we select a specific component it will be reinitialized, in thi way, instead, we can pass each time the last value used in knobs, otherwise returning to a component previously visited we would lose the modifications made during tests and will see every time the first passed value.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;return () =&amp;gt; {
  setTimeout(() =&amp;gt; {

    const root = document.getElementById(id.toString());
    const old = 'Old' + id;
    const value = 'Value' + id;

    for (const key in props) {

      if (Object.prototype.hasOwnProperty.call(props, key) &amp;amp;&amp;amp; !key.includes(old) &amp;amp;&amp;amp; !key.includes(value)) {

        const knobsParams = props[key][0];
        const knobsFunction = props[key][1];
        const tagElem = props[key][2];

        knobsParams[1] = props[key + value]();
        props[key + old] = props[key][1](...knobsParams);

        if (tagElem) {
          const elems = root.getElementsByTagName(tagElem)
          elems.forEach((item) =&amp;gt; {
            item[key] = props[key + old];
          })
        }
        else {
          root[key] = props[key + old];
        }
      }
    }

  });

  return newTemplate;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The returned function is that will be executed by Storybook when a component is selected.&lt;/p&gt;

&lt;p&gt;Before that function returns the template, a timeout without the time parameter is executed, so the handler will return in the event loop (cool video about event loop &lt;a href="https://www.youtube.com/watch?v=8aGhZQkoFbQ&amp;amp;ab_channel=JSConf" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=8aGhZQkoFbQ&amp;amp;ab_channel=JSConf&lt;/a&gt;) as soon as possible, in this case just before the template become a element of the page.&lt;/p&gt;

&lt;p&gt;The component reference, finally, is retrieved using the id previously calculated and the data extracted from the object passed to the main function are passed to the component. As said above, the data are saved in the propery added to &lt;code&gt;props&lt;/code&gt; (here &lt;code&gt;props[key + old] = props[key][1](...knobsParams);&lt;/code&gt;).&lt;/p&gt;




&lt;h1&gt;
  
  
  Conclusioni e credits &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;And that's all guys, putting everything together, you can have a Vue project ready to tests Web Components (not only vue normal classes) with Storybook and the included dev server. &lt;a href="https://github.com/lucabro81/vue-webcomponent-storybook-test" rel="noopener noreferrer"&gt;Here&lt;/a&gt; you can find a repository with a test project complete and working.&lt;/p&gt;

&lt;p&gt;Thanks for reading this far.&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;

&lt;p&gt;Fonti:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/@royprins/get-started-with-vue-web-components-593b3d5b3200" rel="noopener noreferrer"&gt;Get started with Vue web components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vuejsdevelopers.com/2018/05/21/vue-js-web-component/" rel="noopener noreferrer"&gt;Create &amp;amp; Publish Web Components With Vue CLI 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vuejs/vue-web-component-wrapper/issues/12#issuecomment-385141573" rel="noopener noreferrer"&gt;How to define style within a web component?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vue-loader.vuejs.org/#what-is-vue-loader" rel="noopener noreferrer"&gt;What is Vue Loader?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cli.vuejs.org/config/#global-cli-config" rel="noopener noreferrer"&gt;Configuration Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/27334365/how-to-get-list-of-registered-custom-elements/28210364#28210364" rel="noopener noreferrer"&gt;How to get list of registered custom elements&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>vue</category>
      <category>storybook</category>
      <category>webcomponents</category>
      <category>testing</category>
    </item>
    <item>
      <title>Vue (2.x), Storybook (5.x), Web Components e nient'altro</title>
      <dc:creator>Luca</dc:creator>
      <pubDate>Mon, 28 Sep 2020 06:49:56 +0000</pubDate>
      <link>https://dev.to/lucabro81/vue-2-x-storybook-5-x-web-components-e-nient-altro-4l29</link>
      <guid>https://dev.to/lucabro81/vue-2-x-storybook-5-x-web-components-e-nient-altro-4l29</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/lucabro81/vue-2-x-storybook-5-x-web-components-e-nothing-else-5ak6"&gt;English version&lt;/a&gt;&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
Intro

&lt;ul&gt;
&lt;li&gt;Cos'è Vue.js?&lt;/li&gt;
&lt;li&gt;Cosa sono i Web Components?&lt;/li&gt;
&lt;li&gt;Cos'è Storybook?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Definizione problema

&lt;ul&gt;
&lt;li&gt;Creazione progetto di test&lt;/li&gt;
&lt;li&gt;Aggiunta di Storybook&lt;/li&gt;
&lt;li&gt;Creazione di un Web Component&lt;/li&gt;
&lt;li&gt;Problema con gli stili&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Ipotesi sulla soluzione&lt;/li&gt;
&lt;li&gt;
implementazione di una soluzione

&lt;ul&gt;
&lt;li&gt;vue_config.js&lt;/li&gt;
&lt;li&gt;Includere il web component nella story&lt;/li&gt;
&lt;li&gt;Registrare il componente&lt;/li&gt;
&lt;li&gt;Integrare l'interfaccia delle stories&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Conclusioni e credits&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Intro &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Cos'è &lt;a href="https://vuejs.org/"&gt;Vue.js&lt;/a&gt;? &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Vediamo cosa dice la documentazione:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Vue (pronounced /vjuː/, like view) is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable. The core library is focused on the view layer only, and is easy to pick up and integrate with other libraries or existing projects. On the other hand, Vue is also perfectly capable of powering sophisticated Single-Page Applications when used in combination with modern tooling and supporting libraries.&lt;br&gt;
[...]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In atre parole, Vue è framework javascript da utilizzarsi nella realizzazione di frontend. Dalla sua ha la semplicità d'uso e di setup, il template code richiesto è minimo ed è comunque performante, tanto da essere riuscito a ritagliarsi nel tempo un proprio rispettabilissimo spazio accanto a framework decisamente molto più noti ed utilizzati (sì ovviamente sto parlando di Angular e React). Niente di più, niente di meno.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cosa sono i &lt;a href="https://www.webcomponents.org/"&gt;Web Components&lt;/a&gt;? &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Se ne è scritto e tutt'ora se ne scrive (e spero se ne continuerà a scrivere) tantissimo, mi limiterò a fornire una piccola sintesi: i web components, in breve, non sono altro che componenti frontend i quali, una volta registrati dal browser e quindi da questo riconosciuti, possono essere usati come normali tag html con i propri attributi, parametri e comportamento peculiare. &lt;br&gt;
Possono essere definiti tramite classi in js vanilla o usando un framework che li supporti, nello specifico, come è facile intuire, in questo articolo si parlerà di web component definiti utilizzando Vue.js&lt;/p&gt;
&lt;h3&gt;
  
  
  Cos'è &lt;a href="https://storybook.js.org/"&gt;Storybook&lt;/a&gt;? &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Storybook è un eccellente tool per il testing visuale di componenti UI, compatibile con tutti i maggiori framework js ed utilizzabile anche con js vanilla. Tutto quello che si deve fare è specificare quale componente renderizzare, fornire dei mock data e lasciare che storybook istanzi il nostro componente in un proprio iframe e il gioco è fatto. La criticità con vue nasce dalla difficoltà di poter istanziare semplici web components senza utilizzare altre dipendenze.&lt;/p&gt;


&lt;h1&gt;
  
  
  Definizione del problema &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;h3&gt;
  
  
  Creazione progetto di test &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Creare web components con Vue non è un problema, tanto che la sua cli permette di specificare &lt;a href="https://cli.vuejs.org/guide/build-targets.html#web-component"&gt;un target apposito per questo compito&lt;/a&gt; e, con alcuni accorgimenti, è possibile testarli anche con il server di sviluppo.&lt;/p&gt;

&lt;p&gt;Cerchiamo ora di andare un po' più nel dettaglio, la procedura per definire un web component in Vue è decisamente banale, partiamo da un normale progetto Vue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vue create vue-webcomponent-storybook-test
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;la mia configurazione custom è stata &lt;strong&gt;typescript, babel, scss (dart-sass) e basic linter on save&lt;/strong&gt;. &lt;br&gt;
Ciò che si otterrà sarà un'alberatura di questo tipo:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── dist
├── node_modules
├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── assets
│   │   └── logo.png
│   ├── components
│   │   └── HelloWorld.vue
│   ├── App.vue
│   ├── main.ts
│   ├── shims-tsx.d.ts
│   └── shims-vue.d.ts
├── .gitignore
├── babel.config.js
├── package.json
├── README.md
├── tsconfig.json
├── vue.config.js
└── yarn.lock
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Se tutto è andato liscio, da terminale, lanciando &lt;code&gt;yarn serve&lt;/code&gt;, potremo vedere la nostra app con il componente &lt;code&gt;HelloWorld.vue&lt;/code&gt; di test, fare bella mostra di se su &lt;code&gt;http://localhost:8080/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NdU60h9o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/czzjw7civ3cvw2ksxrg3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NdU60h9o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/czzjw7civ3cvw2ksxrg3.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Aggiunta di Storybook &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Il secondo passo consiste nell'installare &lt;a href="https://github.com/storybookjs/vue-cli-plugin-storybook"&gt;Storybook&lt;/a&gt; attraverso il plugin manager di Vue, anche qui l'operazione non è particolarmente impegnativa:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vue add storybook
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Storybook aggiungerà alcuni file e cartelle:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── config
│   └── storybook
│       └── storybook.js
├── dist
├── node_modules
├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── assets
│   │   └── logo.png
│   ├── components
│   │   ├── Helloworld.vue
│   │   └── MyButton.vue
│   ├── stories
│   │   ├── index.stories.js
│   │   └── index.stories.mdx
│   ├── App.vue
│   ├── main.ts
│   ├── shims-tsx.d.ts
│   └── shims-vue.d.ts
├── .gitignore
├── babel.config.js
├── package.json
├── README.md
├── tsconfig.json
├── vue.config.js
└── yarn.lock
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Possiamo eliminare tranquillamente il componente &lt;code&gt;src/components/MyButton.vue&lt;/code&gt; e la story &lt;code&gt;src/stories/index.stories.mdx&lt;/code&gt;, non saranno necessari al nostro progetto. &lt;/p&gt;

&lt;p&gt;All'interno di &lt;code&gt;src/stories/index.stories.js&lt;/code&gt; creiamo una story per il nostro componente &lt;code&gt;App.vue&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Ora lanciando il task &lt;code&gt;storybook:serve&lt;/code&gt;, si avvierà un server di test che permetterà di eseguire storybook e testare il nostor componente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run storybook:serve
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3v243b3j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ap8xjr30hwfcxkhavltf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3v243b3j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ap8xjr30hwfcxkhavltf.png" alt="Storybook first run"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Al momento della scrittura sembra che avviare storybook con yarn non sia possibile).&lt;/p&gt;
&lt;h3&gt;
  
  
  Creazione di un Web Component &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Il secondo passo consiste nel wrappare il nostro componente (lavoreremo con il componente root di default, &lt;code&gt;App.vue&lt;/code&gt;, questo ci permetterà di vedere l'inclusione di altri componenti e come si comportano i loro stili, tutto è ovviamente replicabile per qualunque componente) all'interno di una classe che estende &lt;code&gt;HTMLElement&lt;/code&gt; (link approfondimento), questo non verrà fatto direttamente da noi, ma attraverso una api fornita da Vue. Alla fine di questo step il file &lt;code&gt;main.ts&lt;/code&gt; avrà questo aspetto:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;&lt;code&gt;customElements.define&lt;/code&gt; fa parte dell'api js che materialmente permette di registrare il nostro componente al browser con il tag name &lt;code&gt;my-web-component&lt;/code&gt;.&lt;br&gt;
Una piccola nota, se usate typescript come il sottoscritto, potreste dover aggiungere al file &lt;code&gt;shim-vue.d.ts&lt;/code&gt; la definizione del modulo per &lt;code&gt;@vue/web-component-wrapper&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;declare module '@vue/web-component-wrapper';
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Questo per evitare l'errore &lt;code&gt;Could not find a declaration file for module '@vue/web-component-wrapper'.&lt;/code&gt; che su ide come IntelliJ e simili potrebbe essere apparso, strano non sia presente un d.ts già preinstallato che risolve il problema.&lt;/p&gt;

&lt;p&gt;A questo punto nell'&lt;code&gt;index.html&lt;/code&gt; del nostro progetto (in &lt;code&gt;public/index.html&lt;/code&gt;) dovremo liberarci del root component predefinito (il div con &lt;code&gt;id="app"&lt;/code&gt;) e sostituirlo con il nostro componente appena registrato. Il nostro index, quindi, sarà:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;h3&gt;
  
  
  Problema con gli stili &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Lanciando ora il comando &lt;code&gt;yarn serve&lt;/code&gt; vedremo in nostro componente funzionare alla grande, no?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YpNpAAkS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xkeey38gwtb42324rmjr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YpNpAAkS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xkeey38gwtb42324rmjr.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bhè no...&lt;/p&gt;

&lt;p&gt;Cioè sì... ma in verità non proprio... dove diavolo sono finiti gli stili??&lt;/p&gt;

&lt;p&gt;Il guaio è che Vue ha incluso gli stili nel tag &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; della pagina come farebbe normalmente, ma il nostro componente è rinchiuso dentro ad uno &lt;strong&gt;shadow dom&lt;/strong&gt; (&lt;a href="https://w3c.github.io/webcomponents/spec/shadow/"&gt;https://w3c.github.io/webcomponents/spec/shadow/&lt;/a&gt;), una sorta di orizzonte degli eventi attraverso cui è difficile (non impossibile, qualcosa passa tutto sommato) far passare informazione.&lt;/p&gt;

&lt;p&gt;Con Storybook invece? bhe le cose non migliorano di molto, anzi il problema si ripropone. Modificando il nostro &lt;code&gt;index.stories.js&lt;/code&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Registrando il componente prima di utilizzarlo (storybook attualmente sembra non utilizzare quanto definito nel &lt;code&gt;main.ts&lt;/code&gt;), è possibile renderizzarlo, ma non sono applicati gli stili:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2uNfCN2U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8hctdcrn5gui2nxmchr4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2uNfCN2U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8hctdcrn5gui2nxmchr4.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Ipotesi sulla soluzione &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;Una possibile soluzione è descritta &lt;a href="https://github.com/vuejs/vue-web-component-wrapper/issues/12#issuecomment-385141573"&gt;qui&lt;/a&gt;, a quanto pare l'opzione &lt;code&gt;shodowMode&lt;/code&gt; di &lt;a href="https://vue-loader.vuejs.org/"&gt;vue-loader&lt;/a&gt; è settata a &lt;code&gt;false&lt;/code&gt; di &lt;a href="https://github.com/vuejs/vue-loader/blob/bcee5b9127800070fe5797fe4be466641b88acc8/docs/options.md#shadowmode"&gt;default&lt;/a&gt;, da qui il cursioso comportamente riscontrato. A questo punto mettere a &lt;code&gt;true&lt;/code&gt; quella proprietà dovrebbe risolvere il problema.&lt;/p&gt;

&lt;h3&gt;
  
  
  vue_config.js &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Tutto ciò di cui abbiamo bisogno ora è il file &lt;a href="https://cli.vuejs.org/config/#vue-config-js"&gt;vue.config.js&lt;/a&gt; nella root del nostro progetto; se ancora non esiste, creiamolo.&lt;/p&gt;

&lt;p&gt;Per sapere con cosa riempire il nostro file è necessario ispezionare la configurazione webpack del nostro progetto con il comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vue inspect
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Il risultato assomiglierà a questo:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Se guardiamo attentamente questo output, possiamo notare alcuni commenti interessanti, per esempio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* config.module.rule('css').oneOf('vue').use('vue-style-loader') */
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;che illustrano l'api necessaria a generare quel determinato pezzetto di configurazione, questa api, infatti, è parte di &lt;code&gt;webpack-chain&lt;/code&gt; (&lt;a href="https://github.com/neutrinojs/webpack-chain"&gt;https://github.com/neutrinojs/webpack-chain&lt;/a&gt;) tool utilizzato per facilitare la stesura di file di configurazione per webpack. Visto che è già installato nel nostro progetto, lo possiamo usare a nostro favore.&lt;/p&gt;

&lt;p&gt;Ovviamente le parti della configurazione che interessano a noi sono quelle in cui appare la proprietà &lt;code&gt;shadowmode: false&lt;/code&gt;, qui sotto l'estratto delle parti interessate:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Ora, ciò che mettiamo nel &lt;code&gt;vue_config.js&lt;/code&gt; verrà intercettato da webpack e integrato nel processo di compilazione, e alla fine dovrebbe essere una cosa di questo genere:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;questo script aggiunge &lt;code&gt;shadowMode=false&lt;/code&gt;ovunque sia necessario e permette a webpack di procedere con la compilazione, finalmente quello che si avrà sarà un web component correttamente renderizzato che incapsula tutti i suoi stili:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bBOQ_NDK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/upz6otlx1t19p159axo1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bBOQ_NDK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/upz6otlx1t19p159axo1.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Includere il web component nella story (&lt;a&gt;&lt;/a&gt;)
&lt;/h3&gt;

&lt;p&gt;Se lanciamo storybook ora, vedremo che anche lì il nostro componente sarà corretamente renderizzato, tuttavia l'api di storybook in questo caso non ci aiuta: come facciamo a passare dati al nostro componente in modo efficiente? Se questi dati sono oggetti complessi? Come si può interfacciare il nostro web component con l'api esposta dall'addon knobs?&lt;/p&gt;

&lt;p&gt;Ok andiamo con ordine:&lt;/p&gt;

&lt;h3&gt;
  
  
  Registrare il componente &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Questa è facile, ogni componente deve essere registrato come abbbiamo detto, un a possibilità è implementare una funzione che controlli se già il componetne non sia stato registrato e in caso contrario proceda di conseguenza, qualcosa del genere:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Davvero molto semplice, gli elementi non registrati hanno come constructor &lt;code&gt;HTMLElement()&lt;/code&gt;, è sufficente fare un check e il gioco è fatto.&lt;/p&gt;

&lt;p&gt;Successivamente, il componente va registrato:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;anche qui nulla di nuovo, la procedura è quella vista poco sopra, soltanto chiusa dentro una funzione.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integrare l'interfaccia delle stories &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Ora dobbiamo fare in modo di poter usare l'&lt;code&gt;addon-knobs&lt;/code&gt; per poter passare dati al nostro componente e renderli reattivi ai cambiamenti che possiamo fare durante i test, la mia soluzione è stata costruire una funzione che restituisse un componente e succesivamente ne recuperasse il riferimento per potergli passare eventuali dati:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Cerchiamo di capire cosa questo script effettivamente fa:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const webComponentWrapper = ({props, template}) =&amp;gt; {
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In ingresso ci si aspetta un oggetto, per esempio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;props: {
  test: [
    ['test', true, 'GROUP-ID1'],
    boolean
  ],
},
template: '&amp;lt;test-component&amp;gt;&amp;lt;/test-component&amp;gt;'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;formato dalla proprietà &lt;code&gt;props&lt;/code&gt; che sarà un altro oggetto, i suoi elementi avranno come chiave il nome della proprietà del nostro componente e per valore un array dove il primo elemento sarà un ulteriore array formato da &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;nome proprietà (sì c'è un po' di ridondanza di cui è possibile liberarsi), &lt;/li&gt;
&lt;li&gt;valore che si dovrà considerare &lt;/li&gt;
&lt;li&gt;e l'etichetta che vogliamo dare al gruppo di dati di quello specifico knob. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Il secondo valore, invece, la funzione dell'addon knobs che verrà utilizzata per trattare quello specifico tipo di dato (in questo caso &lt;code&gt;boolean&lt;/code&gt;). &lt;br&gt;
&lt;code&gt;template&lt;/code&gt; invece è una stringa che rappresenta il nostro componente e quello che contiene.&lt;/p&gt;






&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
const id = generateRandomNumber(0, 10 ** 16);
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Qui generiamo un id casuale che verrà utilizzato poi per applicarlo al componente e recuperarne il riferimento, io ho creato una funzione apposta, ma in effetti può essere benissimo un timestamp qualunque.&lt;/p&gt;






&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
for (const key in props) {
  if (Object.hasOwnProperty.call(props, key)) {

    const old = key + 'Old' + id;
    const value = key + 'Value' + id;

    props[old] = null;
    props[value] = () =&amp;gt; (props[old] !== null) ? props[old] : props[key][0][1];
  }
}
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Ora cominciamo a lavorare sui dati da passare al componente: prima di tutto prendiamo la proprietà &lt;code&gt;props&lt;/code&gt; e ne scorriamo il contenuto, per ogni elemento preso in considerazione, lo arricchiamo di altre due proprietà (le variabili &lt;code&gt;old&lt;/code&gt; e &lt;code&gt;value&lt;/code&gt;), alla prima diamo &lt;code&gt;null&lt;/code&gt; alla seconda una funzione che ritornerà il vecchio valore (&lt;code&gt;old&lt;/code&gt;) o quello di 'default' passato assieme alle proprietà (per capirci, il valore &lt;code&gt;true&lt;/code&gt; nel &lt;code&gt;['test', true, 'GROUP-ID1']&lt;/code&gt; di cui abbiamo parlato più su) a seconda che il vecchio valore esista o meno. &lt;/p&gt;

&lt;p&gt;Ogni volta che in Storybook selezioniamo un certo componente questo viene reinizializzato, con questo sistema riusciamo a passare sempre l'ultimo valore usato nei knobs, diversamente ritornando su un componente perderemmo le modifiche fatte durante i nostri test e vedremmo sempre il primo valore passato.&lt;/p&gt;






&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;return () =&amp;gt; {
  setTimeout(() =&amp;gt; {

    const root = document.getElementById(id.toString());
    const old = 'Old' + id;
    const value = 'Value' + id;

    for (const key in props) {

      if (Object.prototype.hasOwnProperty.call(props, key) &amp;amp;&amp;amp; !key.includes(old) &amp;amp;&amp;amp; !key.includes(value)) {

        const knobsParams = props[key][0];
        const knobsFunction = props[key][1];
        const tagElem = props[key][2];

        knobsParams[1] = props[key + value]();
        props[key + old] = props[key][1](...knobsParams);

        if (tagElem) {
          const elems = root.getElementsByTagName(tagElem)
          elems.forEach((item) =&amp;gt; {
            item[key] = props[key + old];
          })
        }
        else {
          root[key] = props[key + old];
        }
      }
    }

  });

  return newTemplate;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;la funzione ritornata è quella che verrà eseguita da Storybook ogni volta che si selezionerà quel determinato componente. &lt;/p&gt;

&lt;p&gt;Prima che questa ritorni il template (nulla più che una stringa del tipo &lt;code&gt;&amp;lt;my-web-component&amp;gt;&amp;lt;/my-web-component&amp;gt;&lt;/code&gt;), si esegue un timeout privo dei milliscondi di durata, questo permette all'handler di rientare nella coda del loop event appena sarà possibile (più informazioni qui), in questo caso appena il template diventa un elemento della pagina.&lt;/p&gt;

&lt;p&gt;Viene recuperato il riferimento del componente tramite l'id calcolato prima, dopo di che vengono recuperati i dati dall'oggetto passato alla funzione e passati al componente. Come detto prima, il dato viene salvato nella proprietà aggiunta prima (qui &lt;code&gt;props[key + old] = props[key][1](...knobsParams);&lt;/code&gt;).&lt;/p&gt;




&lt;h1&gt;
  
  
  Conclusioni e credits &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;E questo è quanto, mettendo tutto assieme, si riesce ad avere un progetto Vue per testare web components (e non solo normali classi Vue) con Storybook e il dev server incluso. &lt;a href="https://github.com/lucabro81/vue-webcomponent-storybook-test"&gt;Qui&lt;/a&gt; trovate un repository con un progetto di test completo e funzionante.&lt;/p&gt;

&lt;p&gt;Fonti:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/@royprins/get-started-with-vue-web-components-593b3d5b3200"&gt;Get started with Vue web components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vuejsdevelopers.com/2018/05/21/vue-js-web-component/"&gt;Create &amp;amp; Publish Web Components With Vue CLI 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vuejs/vue-web-component-wrapper/issues/12#issuecomment-385141573"&gt;How to define style within a web component?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vue-loader.vuejs.org/#what-is-vue-loader"&gt;What is Vue Loader?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cli.vuejs.org/config/#global-cli-config"&gt;Configuration Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/27334365/how-to-get-list-of-registered-custom-elements/28210364#28210364"&gt;How to get list of registered custom elements&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>vue</category>
      <category>storybook</category>
      <category>webcomponents</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
