'Why does mat-tree toggle throws error when used to create story file?

I have created a tree component using mat-tree in a storybook module, and when I use it inside a component it works just fine. The problem is when I use it to create a story in the storybook. Same data is used in both cases.

The first error that it throws is:

'A valid data sorce must be provided'

When clicked on toggle icon, it gives the following error:

Error: this._tree.treeControl.toggle is not a function     at MatTreeNodeToggle._toggle

Could it be related to some missing dependency?

Here is the tree component:

</mat-tree>
  <div class="box">
    <mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding>
      <button mat-icon-button matTreeNodeToggle [attr.aria-label]="'Toggle ' + node.title">
            <mat-icon>
                  {{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}
            </mat-icon>
          </button>
          <span fxFlex class="nb-product-title" [innerHTML]="node.title || ''"></span>
        </mat-tree-node>
      </div>
    </mat-tree>

The component:

    export class TreeComponent implements OnInit {
  @Input()
  treeData?: TreeData[];
private _transformer = (node: TreeData, level: number) => {
    return {
      expandable: !!node.children && node.children.length > 0,
      level: level,
      title: node.title,
      type: node.type || '',
      template: node.template || '',
      status: node.status || '',
      assignee: node.assignee || '',
      src: node.src || '',
    };
  };

  treeControl = new FlatTreeControl<TreeFlatNode>(
    node => node.level,
    node => node.expandable
  );

  treeFlattener = new MatTreeFlattener(
    this._transformer,
    node => node.level,
    node => node.expandable,
    node => node.children
  );

  dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);

  ngOnInit() {
    // @ts-ignore
    this.dataSource.data = this.treeData;
  }

  hasChild = (_: number, node: TreeFlatNode) => node.expandable;
}

The story where I use this tree component:

     imports: [
        CommonModule,
        FlexModule,
        SharedModule,
        MatTreeModule,
        IconModule,
        MatChipsModule,
        ChipModule,
        MatIconModule,
        IconButtonModule,
        IconTextButtonModule,
        PaginationNavigationModule,
        ProfileModule,
        ButtonModule,
        HttpClientModule,
      ],
    }),
  ],
} as Meta;

const TREE_DATA: TreeData[] = [
  {
    title: 'some title text',
    type: 'radio',
    template: 'Live',
    status: 'Reduced',
    assignee: '',
    children: [
      {
        title: 'fj',
        type: 'home',
        template: 'Audio',
        status: 'Draft',
        assignee: 'string',
      },
      {
        title: 'fj',
        type: 'tv',
        template: 'Picture',
        status: 'Ready',
        assignee: 'string',
      },
    ],
  },
  {
    title: 'string',
    type: 'Web',
    template: 'Live',
    status: 'Draft',
    assignee: 'Tes',
  },
];

const Template: Story<TreeComponent> = (args: TreeComponent) => ({
  component: TreeComponent,
  props: args,
  template: '<app-tree' + ' [treeData]="treeData">' + '</app-tree>',
});

export const Tree = Template.bind({});
Tree.args = {
  treeData: TREE_DATA,
};


Solution 1:[1]

You need to reply to the functions into the component to the storybook page.

export class TreeComponent implements OnInit {
  @Input()
  treeData?: TreeData[];
_transformer = (node: TreeData, level: number) => {
    return {
      expandable: !!node.children && node.children.length > 0,
      level: level,
      title: node.title,
      type: node.type || '',
      template: node.template || '',
      status: node.status || '',
      assignee: node.assignee || '',
      src: node.src || '',
    };
  };

  treeControl = new FlatTreeControl<TreeFlatNode>(
    node => node.level,
    node => node.expandable
  );

  treeFlattener = new MatTreeFlattener(
    this._transformer,
    node => node.level,
    node => node.expandable,
    node => node.children
  );

  dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);

  ngOnInit() {
    // @ts-ignore
    this.dataSource.data = this.treeData;
  }

  hasChild = (_: number, node: TreeFlatNode) => node.expandable;
}

like this into the storybook page js

const TREE_DATA: TreeData[] = [
  {
    title: 'some title text',
    type: 'radio',
    template: 'Live',
    status: 'Reduced',
    assignee: '',
    children: [
      {
        title: 'fj',
        type: 'home',
        template: 'Audio',
        status: 'Draft',
        assignee: 'string',
      },
      {
        title: 'fj',
        type: 'tv',
        template: 'Picture',
        status: 'Ready',
        assignee: 'string',
      },
    ],
  },
  {
    title: 'string',
    type: 'Web',
    template: 'Live',
    status: 'Draft',
    assignee: 'Tes',
  },
];

 const _transformer = (node: TreeData, level: number) => {
        return {
          expandable: !!node.children && node.children.length > 0,
          level: level,
          title: node.title,
          type: node.type || '',
          template: node.template || '',
          status: node.status || '',
          assignee: node.assignee || '',
          src: node.src || '',
        };
      };
    
     const treeControl = new FlatTreeControl<TreeFlatNode>(
        node => node.level,
        node => node.expandable
      );
    
     const treeFlattener = new MatTreeFlattener(
        this._transformer,
        node => node.level,
        node => node.expandable,
        node => node.children
      );
const hasChild = (_: number, node: TreeFlatNode) => node.expandable;

const dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);

export const Tree = Template.bind({});
Tree.args = {
  treeData: TREE_DATA,
_transformer: _transformer,
treeControl: treeControl,
treeFlattener: treeFlattener,
hasChild: hasChild,
dataSource: dataSource
};

Solution 2:[2]

It was actually a bug in storybook: https://github.com/storybookjs/storybook/issues/17004

Workaround for now is: by disabling the docs addon in main.js

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Cesar Garcia
Solution 2 Meenakshi