Search code examples
javascriptweb-componentstorybookstenciljs

Storybook web-components: sending array as component property


I'm using Storybook 6.5.9 to render web components (made with Stencil). I have several of them working correctly but now I'm creating a story for a new component that can receive a property that is an array of objects.

The component would be something like this:

import {Component, Host, h, Prop} from '@stencil/core';

@Component({
  tag: 'test-component',
})

export class TestComponent {
  /**
   * Array of options for the group.
   */
  @Prop() items?: String[] = [];

  render() {
    return (
      <Host>
        {
          this.items.map(item => (
            <h1>{item}</h1>
          ))
        }
      </Host>
    )
  }
}
      

And this is the story:

import { Story, Meta } from '@storybook/html';

export default {
  title: 'Components/TestComponent',
  parameters: {
    options: {
      showPanel: true
    },
  }
} as Meta;

const Template: Story = (args) => {
  return `
    <test-component items="${['some', 'thing']}">
    </test-component>
  `;
};

export const TestComponent: Story = Template.bind({});

I have tried setting the items property to a string but the component never gets anything, it's always an empty array.

I'm not getting any errors either in the console. I'm definitely doing something wrong but I don't know what it is... I've been using several other data types for those properties (boolean, string, numbers...) but this is the first time I'm using objects/arrays and I'm not able to get it to work.

Any help will be highly appreciate it.

Thanks!


Solution

  • Properties are passed in as strings in HTML.

    Use JSON.parse(this.items) in your render() method in case this.items is not an array:

    import { Component, Host, h, Prop } from '@stencil/core';
    
    @Component({
      tag: 'my-component',
    })
    export class MyComponent {
      /**
       * Array of options for the group.
       */
      @Prop() items?: string | string[] = '';
    
      render() {
        return (
          <Host>
            {(Array.isArray(this.items) ? this.items : JSON.parse(this.items)).map(item => (
              <h1>{item}</h1>
            ))}
          </Host>
        );
      }
    }
    

    For that to work, you need to pass in your items as valid JSON, meaning you have to use single attribute quotes and double quotes for the "strings" in the "array":

    const Template: Story = (args) => `
        <test-component items='${["some", "thing"]}'>
        </test-component>
    `;
    

    If you are receiving [object Object] in your component, try to stringify your prop value before passing:

    const Template: Story = (args) => `
        <test-component items='${JSON.stringify(["some", "thing"])}'>
        </test-component>
    `;