Trying to test a button that allows form submissions:
import React from 'react';
import { storiesOf, action } from '@kadira/storybook';
import Button from '../button';
storiesOf('Button', module)
.add('submit for a form', () => (
<div className="p4 center">
<form
onSubmit={ () => action('form submitted') }>
<Button type="submit">Submit</Button>
</form>
</div>
));
It seems that onSubmit gets stripped when it loads in React Storybook. I've tried different combinations: wrapping it with a div, making it the root component, using a custom Form component, etc. In all cases the onSubmit is stripped out. Any idea what I'm doing wrong? Or are forms not supported by React Storybook?
You need to pass in just the call to action() not a function that calls it:
<form onSubmit={action('form submitted')}>
This doesn't prevent the default action on the form though, so the iFrame will reload. Not sure how you could handle this outside storing the created action:
const submitAction = action('form submitted');
storiesOf('Button', module)
.add('submit for a form', () => (
<div className="p4 center">
<form
onSubmit={ e => { e.preventDefault(); submitAction(e); }}>
<Button type="submit">Submit</Button>
</form>
</div>
));
I see, thanks for explaining that.
I found a better way.
onSubmit={ e => { e.preventDefault(); action('form submitted')(e); }}>
So. We need a better action with a e.preventDefault integrated.
@hmontes You just inlined a variable, why do you think it's better?
@Hypnosphi Because if i have several buttons in a component (example. A modal with a Close button and a send form button) i can use different actions (action('Modal closed'), action('Submitted Form').
But you only need preventing default on form submit button
Yes. Because "action" don't have a natural way to do that. (I spend hours to find a way to prevent default an event in storybook)
If you have several forms, you may want to create a function like this:
const withPreventDefault = handler => e => {
e.preventDefault();
handler(e);
}
And use it like that:
onSubmit={ withPreventDefault(action('form submitted')) }
Yeah. But that function is INSIDE the component.
The solution is for the Story. If you have a presentational component and a container you want to disable the event OUTSIDE of the presentational component (And in this case. In the story) because the presentational components aren't classes.
Sorry, but I don't get what you're talking about. Please provide an examle
It seems I'm late to the party, but this is still a problem.
<form
onSubmit={(e) => {
e.preventDefault();
action('form submitted')(e);
}}
>
<input type="text" />
<Button type="submit">Submit Button</Button>
</form>
We're not relegating the form submission to the onSubmit event on the form node, and not the onClick event on the Button. If the code doesn't use e.preventDefault(), the iframe then forwards off to the root iframe and borks the Storybook frame. It would be better for the action to prevent default.
@plummer-flex Feel free to add this to your codebase
import {action} from '@storybook/addon-actions`
export const actionWithPreventDefault = name => e => {
e.preventDefault();
action(name)(e);
}
@Hypnosphi I get the adding another wrapper function so as to not use an inline function, but what's the case for not modifying action to handle this on it's own?
Because increasing API surface for each possible usecase is impractical
import {action} from '@storybook/addon-actions`
export const actionWithPreventDefault = name => e => {
e.preventDefault();
action(name)(e);
}
Now that @storybook/addon-actions is part of the essentials, I'm not able to import action anymore (even though it is installed as a dependency already). I checked the new action page here: https://storybook.js.org/docs/react/essentials/actions and looks like this is now handled differently. I was able to add an event for a button click, but still can't find how to prevent default for an onSubmit event using the new way.
Using Storybook v6.1.5
To clarify, I do see a "submitted" event showing in the action panel, but it immediately redirects to a "No Preview" page.
Most helpful comment
You need to pass in just the call to action() not a function that calls it:
This doesn't prevent the default action on the form though, so the iFrame will reload. Not sure how you could handle this outside storing the created action: