storybook: Background addon not working

I have applied background addon to my storybook, it is showing those colors as option for selecting as background but on selecting it is not getting applied to my storybook. My config.js looks like this

import { configure, addDecorator, addParameters } from '@storybook/react';
import BItheme from './theme';
import styles from "@sambego/storybook-styles";

addParameters({
  options: {
    theme: BItheme,
  },
  backgrounds: [
    { name: 'twitter', value: '#00aced'},
    { name: 'facebook', value: '#3b5998' },
  ],
});

addDecorator(styles({
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
  height: 500,
}))

// automatically import all files ending in *.stories.js and tsx
configure(
  [
    require.context('../stories', true, /\.stories\.js$/),
    require.context('../src/components', true, /\.stories\.tsx$/)
  ],
  module
);

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 3
  • Comments: 25 (7 by maintainers)

Most upvoted comments

I was also having this issue because we import styles (which includes Bootstrap). BS sets background-color on body to white by default. To get around it, I added a stylesheet to Storybook in the .storybook directory (not with our main styles) and imported it into .storybook/config.js.

// .storybook/storybook.scss

.sb-show-main {
  background-color: transparent;
}
// .storybook/config.js

import './storybook.scss'

I am also seeing this issue. The problem seems to come from the fact that the body in the iframe has a background-color set to #fff If I remove that css value in developer tools the background addon starts working.

I ran into this with the Grid tool, because I am using bootstrap with the background-color set on <body></body>, which was already mentioned. I use Angular, but I think its the same for the other iframe story previews. On the body there is a css class not used in my project .sb-show-main, so I just added body.sb-show-main { background-color: transparent; } to my css and it worked in storybook only.

To avoid putting a storybook specific rule in the app’s css I then instead put the following in “preview-head.html”.

<!-- .storybook/preview-head.html -->
<style>
  body.sb-show-main { background-color: transparent; }
</style>

I prefer the app’s default background to be used, since some components don’t have a background and aren’t guaranteed to look right with what ever storybook theme background is set in the parent iframe when the background addon isn’t being used. It is nice to have the option of turning the grid on for a sec or trying a few backgrounds sometimes though, so I took a quick look through the code to see if there was a way to hide the app’s background only when either the Background or Grid tool is being used. So, if I turn off the grid, or unselect a background, I want it to go back to the app’s background.

If you add code that has access to the api, then the addon does emit an event when the background selection is changed, but not when the grid is toggled. There is a state value for both, but I didn’t see a simple way to observe the state value. To know initially before either tool is clicked I had to use a combination of the state and properties, since the state is only set after the tool is used.

The following small addon script isn’t necessarily a good way to do this, but it seems to work without changing anything if needed until a good way to support this is decided. I don’t have this in a published storybook, but it seems to work so far. This is the first time I have used the Storybook API and I rarely use React, so someone with more experience using them may know a better way to do the following, because I used a Panel component just to observe a state change and two events that sometime emit at the same time as the others causing the check to happen multiple times. Depending on the project the following may be trying to do to much and the current addon functionality is enough, instead of trying to sync the in-use state to a property on the body like I did.

// .storybook/background-addon-detectable.js

import React from 'react'
import { addons, types } from '@storybook/addons'
import { AddonPanel } from '@storybook/components'
import { STORY_RENDER } from '@storybook/core-events'

import { ADDON_ID as ADDON_BACKGROUNDS_ID, PARAM_KEY as ADDON_BACKGROUNDS_PARAM_KEY } from '@storybook/addon-backgrounds/dist/constants'

const ADDON_ID = 'background-addon-detectable'
const PANEL_ID = `${ADDON_ID}/panel`

function getStoryBody() {
  const iframe = document && document.querySelector('#storybook-preview-iframe')
  if (iframe && iframe.contentDocument) {
    return iframe.contentDocument.querySelector('body')
  }
  return null
}

function isBackgroundSelected(api) {
  const currentValue = api.getAddonState(ADDON_BACKGROUNDS_PARAM_KEY)

  if (currentValue === 'transparent') {
    return false
  }

  const backgrounds = api.getCurrentParameter(ADDON_BACKGROUNDS_PARAM_KEY) || []

  if (!backgrounds.length) {
    return false
  }

  if (backgrounds.find(i => i.value === currentValue)) {
    return true
  }

  if (!currentValue && backgrounds.find(i => i.default)) {
    return true
  }

  return false
}

function isGridActive(api) {
  return api.getAddonState(`${ADDON_BACKGROUNDS_ID}/grid`) || false
}

function updateBgStateAttrForCss(api) {
  const isBgGridActive = isGridActive(api)
  const isBgSelected = isBackgroundSelected(api)

  const storyBody = getStoryBody()
  if (storyBody) {
    storyBody.setAttribute('data-addon-background', isBgSelected || isBgGridActive)
  }
}

addons.register(ADDON_ID, api => {
  // React to a background change.
  //
  // The README shows how to listen to the event emitted when a background is changed.
  // Source: https://github.com/storybookjs/storybook/blob/next/addons/backgrounds/README.md#events
  api.getChannel().on('storybook/background/update', (bg) => {
    // console.log('Background color', bg.selected)
    // console.log('Background name', bg.name)
    updateBgStateAttrForCss(api)
  })

  // There may be a better event, because it would probably only need this event
  // when the iframe is initially loaded.
  api.on(STORY_RENDER, evt => {
    updateBgStateAttrForCss(api)
  })

  //
  // The following is a way that could detect a grid state change.
  //
  // NOTE: I do not consider this a good thing to do, since I am adding a panel
  // just to listen for an event. I don't have much experience with React, I
  // mainly use Angular, so I just know enough to get by when I need to use it.
  // Someone else may know a better way to do what I am using this render
  // function for.
  //
  const render = ({ active, key }) => {
    // state will be undefined until the tool button is clicked
    // const isBgGridActive = api.getAddonState(`${ADDON_BACKGROUNDS_ID}/grid`) || false
    // console.log('isBgGridActive', isBgGridActive)
    updateBgStateAttrForCss(api)

    return React.createElement(AddonPanel, { active, key })
  }

  // Empty string should make the button visually hidden, but it is still there.
  const title = '';

  addons.add(PANEL_ID, {
    type: types.PANEL,
    title,
    render
  })
})

Found the root cause for this @react-theming/storybook-addon breaks the background addon

Ta-da!! I just released https://github.com/storybookjs/storybook/releases/tag/v6.1.0-alpha.16 containing PR #12368 that references this issue. Upgrade today to try it out!

You can find this prerelease on the @next NPM tag.

Will a fix for this be coming soon? This is a problem that still exists today

Quick fix (might work for you - definitely worked for me while I try to decipher a real fix):

Add a storybook.css file in your .storybook folder and give it this CSS rule:

.sb-show-main {
  background-color: transparent;
}

Obvs, you’ll need to make sure that CSS file gets used so import it in your preview.js file:

import "./storybook.css";

Note that while this may have been closed, this is still a bug and a breaking change from older versions of storybook.

Here’s the content of my preview.js file:

import React from "react";
import { addDecorator, addParameters } from "@storybook/react";
import { withKnobs } from "@storybook/addon-knobs";
  
addParameters({
  backgrounds: [
    { name: "light", value: "#FFFFFF" },
    { name: "gray", value: "#eeeeee" },
    { name: "dark", value: "#505959", default: true }
  ]
});
   
addDecorator(story => {
  const content = story();
  return <div style={{ margin: "50px", height: "1000px" }}>{content}</div>;
});

What this should do is allow users to select the background color as a global setting across all of storybook.

What I’m seeing is that the background color is applied to the wrapper element outside the storybook preview iframe which means any styling you have in your theme could override that setting. Note that this is a change from previous versions of storybook backgrounds which would add the style to the element inside the preview iframe.

Example screenshots:

This is where the background styling is added: http://snpy.in/s4ASQJ This is where your own styling can end up overriding that setting: http://snpy.in/S10n3d This is where old versions of storybook would apply the background styling: http://snpy.in/VffzYh

Yes bro this is totally because of that iframe but I couldn’t find from where that iframe is coming. If you will find please tell me also.