Search code examples
angularstorybookform-controlangular-storybook

Use Reactive Forms with storybook templates


I am developping some components and custom form elements that I want to have on Storybook.

To be complete, I want the stories to use FormControl and FormGroup to show some real use case.

I have the following story :

export const Dropdown: Story = {
  args: {
    label: 'Dropdown',
    placeholder: 'Placeholder',
  },
  render: (args) => ({
    props: {
      ...args,
      formGroup: new FormGroup({
        dropdown: new FormControl("item2", Validators.required),
      }),
    },
    template: `
    <form [formGroup]="formGroup">
      <gai-dropdown ${argsToTemplate(args)} style="width: 200px" formControlName="dropdown">
          <gai-option value="item1" label="Item 1"/>
          <gai-option value="item2" label="Item 2"/>
          <gai-option value="item3" label="Item 3"/>
          <gai-option value="item4" label="Item 4">
             <ng-template>Avec template</ng-template>
          </gai-option>
      </gai-dropdown>
    </form>
    `
  }),
};

And the binding does work because the component displays as follows :

Dropdown

But if I interact with the dropdown to change the value, clear it or anything, the FormControl doesn't change.

No changes

I am at a loss here. Everything works perfectly when using the component outside of Storybook.

In my dropdown component I register the onChange callback like this :

  registerOnChange(fn: any): void {
    this.onChangeForm = fn;
  }

And I had declared onChangeForm like this : onChangeForm: any = () => {}.

I migrated it to a property with a getter and setter and it now works. I have no idea why though.


Solution

  • It turns out it was a weird interaction with Storybook Controls. The onChange method was overriden by Storybook controls for some reason.

    Renaming the method to something random and not onSomething made it work. So I ended up excluding the onChange and onTouched method from the controls like this :

    parameters: {
        controls: {
          exclude: ['onBlur', 'onChange', 'onTouched'],
        }
      },
    

    EDIT: It was actually the "Actions" addon that was configured to register actions for every method starting with "on". It was configured in my preview.js like this actions: { argTypesRegex: "^on[A-Z].*" } and was responsible for overwriting my methods.