'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 |