storybook: Addons: error Converting circular structure to JSON for Angular mat-table
Describe the bug When MatTableDataSource is used in addons arguments, the story is broken
const dataSource = new MatTableDataSource(ELEMENT_DATA);
export const Primary = Template.bind({});
Primary.args = {
testDataSource: dataSource
}
vendors~main.a935f15a2179a6eff5fd.manager.bundle.js:2 TypeError: Converting circular structure to JSON
--> starting at object with constructor 'MapSubscriber'
| property '_parentOrParents' -> object with constructor 'Subscriber'
| property '_subscriptions' -> object with constructor 'Array'
--- index 0 closes the circle
at JSON.stringify (<anonymous>)
at ObjectControl (main.991fa6e71cce7c92d381.manager.bundle.js:1)
at oh (vendors~main.a935f15a2179a6eff5fd.manager.bundle.js:2)
at Rj (vendors~main.a935f15a2179a6eff5fd.manager.bundle.js:2)
at Qj (vendors~main.a935f15a2179a6eff5fd.manager.bundle.js:2)
at Kj (vendors~main.a935f15a2179a6eff5fd.manager.bundle.js:2)
at yj (vendors~main.a935f15a2179a6eff5fd.manager.bundle.js:2)
at vendors~main.a935f15a2179a6eff5fd.manager.bundle.js:2
at exports.unstable_runWithPriority (vendors~main.a935f15a2179a6eff5fd.manager.bundle.js:2)
at cg (vendors~main.a935f15a2179a6eff5fd.manager.bundle.js:2)
To Reproduce
- Generate angular component with Angular material table <table mat-table [dataSource]=“dataSource”>
- Create a story for the component where args will contain a data for mat-table dataSource as new MatTableDataSource(ELEMENT_DATA) => story load is broken
Repo: https://github.com/Tatsianacs/storybook--bug
System Environment Info:
System: OS: Windows Server 2016 10.0.14393 CPU: (6) x64 Intel® Core™ i7-5820K CPU @ 3.30GHz Binaries: Node: 16.13.0 - C:\Program Files\nodejs\node.EXE Yarn: 3.1.1 - ~\node_modules.bin\yarn.CMD npm: 8.1.0 - C:\Program Files\nodejs\npm.CMD Browsers: Chrome: 95.0.4638.69 npmPackages: @storybook/addon-actions: ^6.4.2 => 6.4.2 @storybook/addon-essentials: ^6.4.2 => 6.4.2 @storybook/addon-links: ^6.4.2 => 6.4.2 @storybook/angular: ^6.4.2 => 6.4.2 @storybook/builder-webpack5: ^6.4.2 => 6.4.2 @storybook/manager-webpack5: ^6.4.2 => 6.4.2
Additional context It doesn’t work at least for Controls and accessibility addons, it worked for Knobs and nothing is broken if addon is disabled OR dataSource is removed from the table (workaround:
dataSource: {
table: {
disable: true,
}
}
About this issue
- Original URL
- State: open
- Created 3 years ago
- Reactions: 1
- Comments: 23 (8 by maintainers)
Hi ! I had this annoying issue as well with my Form stories…
But I found a trick which maintains a fully fonctionnal FormGroup (with valueChanges working properly) in stories 🎉
Storybook tries to stringify any args you pass to a component. We can just tell our formGroups to be stringified by our rules. By setting toJSON() on our objects, we can prevent Storybook from trying to serialize a circular structure.
Note that this would work for ANY object that circles when stringified
Untested:
It is not clear what the complete workaround/fix for this is. Neither one of the workarounds mentioned worked for me: Workaround 1:
Workaround 2:
I’ve tested using the code from your https://github.com/Tatsianacs/storybook--bug demo, Angular & Material v14, Storybook v7.
Here is a Stackblitz link: https://stackblitz.com/edit/angular-material-storybook.
Could you or anyone please share more details about how you solved the issue? Thank you 🙏
@valentinpalkovic I’m not too sure if the FormGroup issue is exactly the same as with MatTable. Maybe good to continue on this thread which is related about FormGroup: https://github.com/storybookjs/storybook/discussions/15602#discussioncomment-5730841
After having a chat internally, we figured out a workaround: https://stackblitz.com/edit/github-3znyoe-aebjsf?file=src%2Fstories%2Finput.component.stories.ts
So instead of using the native mapping functionality of Storybook, you can use the
render
property to do the mapping manually:The main issue is that the
FormControl
instances get only instantiated once. Even if you switch the control values in the ArgsTable forcontrol,
the sameFormControl
instance is used, which was instantiated initially. A new instance is NOT created. That’s a limitation of the current implementation of Storybook’s mapping capabilities. It seems that as soon as an input element gets rendered, the FormControl instance changes internally and creates a self-reference. If you now switch from Marty to Jennifer and back, the same FormControl instance, which got manipulated in the meantime, is passed to the input component.To circumvent this issue, we want to pass a new instance every time the
control
property changes:So the
control
parameter equalsMarty
,Jennifer
orDoc
, and as soon as the control is changing, therender
function gets called. Here you are able to map your properties without any limitations. As I said, you want to pass a freshFormControl
instance to the input component.Let’s take a look at the
mapping
object:This object maps the
control
string values to callback functions, which return a fresh FormControl instance, every time the mapping key is called. So callingmapping['Marty']
creates a new FormControl instance. So in principlemapping[value]
are factory functions, which return instances ofFormControl
. Now hopefully, this line makes sense:If
controlKey
is e.g.Marty,
thenmapping['Marty']?.()
returns a fresh instance of FormControl.I hope all of this makes sense and the workaround works sufficiently for you.