Storybook Actions in Action
Tags: webdev, javascript, react, tutorial
Published: 2023-08-25 00:53:59

Let's talk about `console.log`\-driven development. We all do it. So, instead of fighting it, let's *upgrade* it with Storybook Actions.

## **Feature instability and mitigations**

Storybook actions are a feature of Storybook 6 and 7.

There's an [**active design proposal**](https://github.com/storybookjs/storybook/discussions/23649) to make them work like conventional testing mocks. This would be a welcome change, imo.

Knowing that a change is on the horizon, let's focus on the features of the current API that will remain (or migrate easily with codemods).

## **Feature Overview**

In a fresh Storybook 7 installation ([**sandboxes here**](https://storybook.new/)), we find two example components configured to use Storybook actions.

The included Button stories log the common event — `onClick` — when clicked. Logs can then be expanded to show full event details.

![Story for Button that has been clicked. The Actions tab shows a logged onClick common event. This includes the React Event details](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dllhku9ot91k2whebzje.png)

​Heading stories also log events but this time with custom events `onLogin` and `onCreateAccount`. These events are logged when the respective buttons are clicked.

![Story for Heading that has had Login and Sign up buttons clicked. The Actions tab shows a logged onLogin and onCreateAccount custom events. These include the React Event details](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y7uh6brf62eucqcljy22.png)

Actions pair perfectly with [**Storybook interactions**](https://storybook.js.org/docs/react/essentials/interactions#page-top). Interactions are a way to document component states thru simulated user input. Interactions trigger their corresponding actions.

![Story for Page that has had Login and Sign up buttons. The Log in button was clicked via Storybook interactions (play function), using testing-library/dom. The Interactions tab shows that the event was executed. And the Actions tab shows that the callback was called, with the React Event details.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gqnkbzompnkwuip4yq70.png)

## **Add actions with argTypes**

Let's define actions for the example Button component.

* Open the story file and locate component `meta`.
    
* Add an `argTypes` property if one doesn't exist.
    
* Add an `onClick` property (this needs to match the component prop name).
    
* Set the value to `{ action: "clicked" }`. (The logged text can be anything we like.)
    

```tsx
// Button.stories.tsx
// (surronding code omitted)
const meta = {
  title: "Example/Button",
  component: Button,
  tags: ["autodocs"],
  argTypes: {
    backgroundColor: { control: "color" },
    onClick: { action: "onClick" },
  },
} satisfies Meta<typeof Button>;
```

Now click your button in Storybook and watch the Actions log!

![Animated gif of React Events being logged as the Button story is clicked](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6mcfauqqsr70uij731mf.gif)

## **Test actions with interactions**

Let's add an interaction to the example Button component.

* Create or find a new story.
    
* Add a `play` function to the story object.
    
* Within the `canvasElement`, find the button.
    
* then simulate a click event.
    

```tsx
// Button.stories.tsx
// (surronding code omitted)
export const Primary: Story = {
  args: {
    primary: true,
    label: "Button",
  },
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    const button = await canvas.getByRole("button", {
      name: /Button/i,
    });
    await userEvent.click(button);
  },
};
```

Now the story will run the interaction when it loads. Logging both the interaction and the action — in their respective tabs.

![Image of an interaction story that automatically plays a user event over a story. An interaction is logged and an 1 action is showing as logged in the Actions tab.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cxxps2admrdhuxtgag91.png)

## **Lessons learned**

`console.log`\-driven development is something I do when driving out a component implementation. It's a quick, cheap way to ensure that events are hooked up (early in the process).

But log statements has one **huge drawback**: they live in component code. And I'm guilty of pushing a log (or a few dozen) into production.

Storybook actions let you validate implementation in story files, leaving component code alone.