A Happo plugin for Storybook.
Documentation is available at docs.happo.io
A happo.io plugin for Storybook
License: MIT License
A Happo plugin for Storybook.
Documentation is available at docs.happo.io
To keep the noise down when running happo-ci, we could allow the --loglevel
to be set when calling build-storybook
.
https://storybook.js.org/docs/configurations/cli-options/#for-build-storybook
Updating from 3.1.0 to 3.2.0 fails with:
> yarn happo run
No [sha] provided. A temporary one will be used in place: "dev-393ed6dc963506aea14a".
Generating static package...
error: unknown option '--output-dir'
✗
Error: Failed to build static storybook package
at ChildProcess.<anonymous> (xxx\node_modules\happo-plugin-storybook\index.js:67:16)
at ChildProcess.emit (node:events:513:28)
at Process.ChildProcess._handle.onexit (node:internal/child_process:291:12)
xxx\node_modules\happo-plugin-storybook\index.js:67
reject(new Error('Failed to build static storybook package'));
^
Error: Failed to build static storybook package
at ChildProcess.<anonymous> (xxx\node_modules\happo-plugin-storybook\index.js:67:16)
at ChildProcess.emit (node:events:513:28)
at Process.ChildProcess._handle.onexit (node:internal/child_process:291:12)
We have not upgraded to Storybook 7. Is 3.2.0 storybook 7 only?
Good afternoon! Thanks so much for creating this plugin—it's a very helpful tool.
We're building a component library with Storybook and using the plugin to take screenshots of all of our components for visual testing in CI. Instead of creating a story for each variant, we allow users to explore the different colours, sizes, arguments, etc. with the Storybook Controls add-on. Is there a way to configure the plugin so it takes multiple screenshots of the same story with different arguments?
In case it's helpful, here's an example of our Button component story. (Our component library is all about progressive enhancement, so our stories showcase that while other options are available via the Controls.)
I've looked into the variants add on, but it would require making an All Variants
story and/or adding code to each story individually, which isn't quite what we're trying to do.
Please let me know if you'd like more details, or if I can further help.
Thanks so much!
Storybook 8.0 removes the deprecated __STORYBOOK_CLIENT_API__
global and thus no longer works with this plugin. I went ahead and created a prototype fork that updates the project structure off of storybook's addon-kit template and updates the code to work with SB 8.0 here: https://github.com/jpandersen87/happo-plugin-storybook/tree/sb-8.0. I've only done very brief happo test runs on 6.x/7.x/8.x but they worked. Please feel free to use code from that fork if desired.
I'm seeing problems after updating to 2.11.0:
TypeError: (0 , _emotion_utils__WEBPACK_IMPORTED_MODULE_4__.registerStyles) is not a function
at Insertion (webpack-internal:///../node_modules/@emotion/react/dist/emotion-element-cbed451f.browser.esm.js:202:20)
at eval (webpack-internal:///../node_modules/@emotion/react/dist/emotion-element-cbed451f.browser.esm.js:57:66)
We are currently pulling in emotion ourselves through react-select
.
buildStorybook
tries to execute ./node_modules/.bin/build-storybook
which doesn't exist when using yarn berry
https://github.com/happo/happo-plugin-storybook/blob/master/index.js#L37-L44
We often open our stories directly (like localhost:9000/iframe.html?...
instead of localhost:9000/?...
), to have the full window preview of the story. (i.e. to open it in browserstack or for better developer debugging experience).
Unfortunately, when opening it in such a way, isHappoRun
returns true (
happo-plugin-storybook/register.es6.js
Line 97 in d1431b3
This incorrectly makes our stories to be run in happo mode, when we don't want.
To fix it, isHappoRun maybe should be some function that checks for environmental variable that is appended only when the app is actually run in the happo mode? (just thinking out loud)
Hi, @trotzig!
The idea behind this request is the same as happo/happo-e2e#21.
Basically, we have a few monorepo projects with several hundred stories, and we would like to be able to mark some of them as skipped for a given run.
This would give us the flexibility we need to add Happo to new projects without having to worry about spurious differences from one team blocking another, for instance.
Is this something you agree would make sense? Please let me know!
I'm using Storybook in combination with @storybook/addon-interactions
, so I can define play
functions that perform UI interactions before the snapshot is taken.
Interactions with fireEvent
are working correctly, they're executed before the snapshot is taken.
Interactions with userEvent
are working in storybook, but are not executed before the snapshot is taken.
Is that unsupported or is it a bug?
I created a demo repo to demonstrate this:
https://github.com/Q-Man/happo-storybook-userevent/
Here's my code:
import React from 'react'
import {userEvent, screen, fireEvent} from "@storybook/testing-library"
const Test = () => {
return <div>
<input type="radio" name="test" id="test1"/>
<label htmlFor="test1">test1</label>
<input type="radio" name="test" id="test2"/>
<label htmlFor="test2">test2</label>
</div>
}
const meta = {component: Test}
export const Default = {}
export const WithFireEvent = {
play: async () => {
// this is correctly executed before the snapshot
await fireEvent.click(screen.getByLabelText('test2'))
}
}
export const WithUserEvent = {
play: async () => {
// this is working in the storybook UI, but not executed before the snapshot
await userEvent.click(screen.getByLabelText('test2'))
}
}
export default meta
Hi! I'm evaluating Happo to run against some composed pages we have on Storybook.
I noticed that Safari is not taking full page screenshots, the screenshot dimensions are always exactly the specified viewport (e.g: 1440x900). Is there some additional configuration I could do?
After a first look at the source code, it seems that something is wrong with the resolveBuildCommandParts
function.
CI logs:
npm ERR! code E404
npm ERR! 404 Not Found - GET https://registry.npmjs.org/build-storybook - Not found
npm ERR! 404
npm ERR! 404 'build-storybook@*' is not in this registry.
npm ERR! 404
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, http url, or git url.
npm ERR! A complete log of this run can be found in: /github/home/.npm/_logs/2023-12-21T17_13_55_076Z-debug-0.log
✗
Error: Failed to build static storybook package
at ChildProcess.<anonymous> (/__w/vtx-ui-react-component-library/vtx-ui-react-component-library/node_modules/happo-plugin-storybook/index.js:85:16)
at ChildProcess.emit (node:events:517:28)
at Process.ChildProcess._handle.onexit (node:internal/child_process:[29](https://github.com/vertexinc/vtx-ui-react-component-library/actions/runs/7291127463/job/19869429680?pr=1090#step:12:30)2:12)
/__w/vtx-ui-react-component-library/vtx-ui-react-component-library/node_modules/happo-plugin-storybook/index.js:85
reject(new Error('Failed to build static storybook package'));
^
Error: Failed to build static storybook package
at ChildProcess.<anonymous> (/__w/vtx-ui-react-component-library/vtx-ui-react-component-library/node_modules/happo-plugin-storybook/index.js:85:16)
at ChildProcess.emit (node:events:517:28)
at Process.ChildProcess._handle.onexit (node:internal/child_process:292:12)
Error: Process completed with exit code 1.
I tried to debug it by running that function locally, but it throws an error on this step execSync(`${binary} list | grep 'storybook/react@[56]'`)
.
[happo] Check for Storybook v6 failed. Details: Error: Command failed: npm list | grep 'storybook/react@[56]'
at checkExecSyncError (node:child_process:890:11)
at execSync (node:child_process:962:15)
at resolveBuildCommandParts (/home/spanb4/Desktop/vertex/vtx-ui-react-component-library/test.js:13:9)
at Object.<anonymous> (/home/spanb4/Desktop/vertex/vtx-ui-react-component-library/test.js:34:1)
at Module._compile (node:internal/modules/cjs/loader:1356:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1414:10)
at Module.load (node:internal/modules/cjs/loader:1197:32)
at Module._load (node:internal/modules/cjs/loader:1013:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:128:12) {
status: 1,
signal: null,
output: [ null, <Buffer >, <Buffer > ],
pid: 54161,
stdout: <Buffer >,
stderr: <Buffer >
}
Hey! I'm evaluating Happo to run against some composed pages we have on Storybook. My screenshots are looking good on Safari and Firefox, but on Chrome we have components/sections not loading and screenshots getting almost blank after some point.
We tried disabling lazy loading, disabling hydration, but it doesn't help. Weird is that Firefox screenshots are looking fine.
I noticed that if I set Chrome viewport with a huge height (such as 1440x10000
), everything until 10000px height renders perfectly, but this workaround is not perfect since for pages with height < 10000px the rest would be rendered blank 😕
What could I do? A scrollStitch
solution for Chrome would be perfect.
Some screenshots:
Recently we started to update our project to use Storybook V7 + Vite
The thing I discovered that in production storybook build addons store and postmessages channel are not working properly:
Manager page sends some postmessages to the iframe, but it doesn't handle them, because of falling back to the mockChannel
The reasons of this behavior:
happo-plugin-storybook/src/addon.js
uses @storybook/addons
which was replaced with @storybook/manager-api
in Storybook V7happo-plugin-storybook/src/decorator.js
uses @storybook/addons
which was replaced with @storybook/preview-api
in Storybook V7Please see https://www.npmjs.com/package/@storybook/addons
Also I made the script which rewrites this peer deps on the post install stage and it started to work fine, so could you please release the fix for this?
After bumping from 2.0.0 to 2.0.1 we get following error:
Uncaught ReferenceError: regeneratorRuntime is not defined
at Object../node_modules/happo-plugin-storybook/register.js (iframe.bundle.js:55146)
at webpack_require (iframe.bundle.js:725)
at fn (iframe.bundle.js:102)
at Module../storybook/config.js (iframe.bundle.js:209699)
at webpack_require (iframe.bundle.js:725)
at fn (iframe.bundle.js:102)
at Object.0 (iframe.bundle.js:224636)
at webpack_require (iframe.bundle.js:725)
at iframe.bundle.js:792
at iframe.bundle.js:795
https://www.dropbox.com/s/og380j98umij791/Screenshot%202019-01-24%2010.38.25.png?dl=0
This fatal error causes our stories not to render at all (at least in non-happo mode): https://www.dropbox.com/s/jpkvwqeouegpci4/Screenshot%202019-01-24%2010.43.53.png?dl=0 .
FYI, we use Storybook 4.0.7, with following webpack and babel setup .
Side notes:
At 2.0.0 we had noticed that our storybook failed to run on IE11, even when Happo was disabled.
Right now our storybook config looks like that:
import { isHappoRun } from 'happo-plugin-storybook/register';
if (isHappoRun()) {
require('./config.happo');
} else {
require('./config.default');
}
Unfortunately, it seems like just importing the isHappoRun
function brings a lot of dependencies to our codebase.
Thus, maybe the isHappoRun
could be extracted to some separate file, which wouldn't result in importing all of the happo deps on a non-happo build? (Controlling it with a env variable that is always present when in a happo mode, maybe would be a better solution. Just thinking out loud.)
After upgrading to 3.4.1 or 4.0 from 3.3.1, I'm getting:
> yarn happo run
No [sha] provided. A temporary one will be used in place: "dev-3d643f9f96333f550e03".
(node:21412) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
Generating static package...
error: unknown option '--output-dir'
✗
Error: Failed to build static storybook package
at ChildProcess.<anonymous> (D:\...\node_modules\happo-plugin-storybook\index.js:97:16)
at ChildProcess.emit (node:events:519:28)
at Process.ChildProcess._handle.onexit (node:internal/child_process:294:12)
D:\...\node_modules\happo-plugin-storybook\index.js:97
reject(new Error('Failed to build static storybook package'));
^
Error: Failed to build static storybook package
at ChildProcess.<anonymous> (D:\...\node_modules\happo-plugin-storybook\index.js:97:16)
at ChildProcess.emit (node:events:519:28)
at Process.ChildProcess._handle.onexit (node:internal/child_process:294:12)
Good afternoon, thanks again for this useful plugin!
When I run happo in CI, I'm having some trouble with unpredictable diffs. For example, this PR only includes a readme change. There are no changes to any of the components, but I'm getting diffs. Some appear spurious (or at least, I don't see any visual difference). In other screenshots, whole components appear or disappear. I also see diffs, even though the PR doesn't touch the affected component.
It's not always the same components or browsers that create these three types of diffs.
Help much appreciated, thanks so much!
Feature request: For example, npm run happo --only Button
would only compile and run stories that have Button
in their story name (or in the story filename).
In custom happo, you can have an async example by returning a promise from the function that returns the component.
There is no such possibility in storybook though. You can only do some arbitrary delay
but this is a mediocre solution. (Delay will very often be either too long or too short.).
If I could have Happo wait until I set a variable like window.HAPPO_READY = true
or call window.happoExampleReady()
, then I could easily write some decorator for my stories, that i.e. waits until x happens and then calls happoExampleReady().
Usage example:
Right now a lot of our examples don't have images loaded or they are loaded partially. (Probably because happo doesn't detect some of them/doesn't wait for them, or because they appear after the component init?)
Also in a few examples we do some API request and there also, happo doesn't really wait for it, until I force it to wait some long delay like 10s.
Hello 👋.
I'd try to describe our case.
Imagine you have 1k stories in the code base.
For all stories we want to generate desktop screenshots, so:
.happo.js
module.exports = {
targets: {
chrome: new RemoteBrowserTarget('chrome', {
viewport: '1024x768',
}),
},
...
}
But only for some of them, we want to have mobile screenshot:
component story file:
export const DefaultOnMobile: ComponentStory<typeof Component> = () => (
<Component vertical="someVariant" />
)
DefaultOnMobile.storyName = 'Default - on mobile'
DefaultOnMobile.parameters = {
viewport: { defaultViewport: 'mobile' }, // our predefined viewport config
happo: { targets: ['chrome-small'] }
}
With the current implementation of happo-plugin-storybook
the screenshot for DefaultOnMobile
will not be created, as chrome-small
is not defined, but as I mentioned above, we only want to create mobile screenshots only for a few of the stories.
For example with:
.addParameters({ happo: { enabled: false } })
(Right now we have to maintain two separate codebases, or a lot of ifs, to add a story only when in non-happo mode)
Hello, love using Happo, thanks for the amazing tool 🎉
This issue is a request for the ability to control the targets for individual stories.
Looking at the Happo only example I see this: https://docs.happo.io/docs/examples#limiting-targets
And looking at the Happo-Cypress example I see this: https://docs.happo.io/docs/cypress#selecting-targets
We'd love to have the same ability for Storybook. The specific use case being we have a few stories where we'd like to opt-in to additional viewport size screenshots, eg:
export const SpecialComponentStory = Template.bind({});
SpecialComponentStory.parameters = {
happo: {
targets: ['mobile', 'tablet', 'laptop_15', 'monitor_27'],
},
};
Thanks!
When Happo configured to use prebuild package of the storybook it doesnt wait for story rendered event and runs all hooks like beforeScreenshot
before the actual DOM nodes will be exist. It seems like the problem in detecting should wait for render story or not
happo-plugin-storybook/src/register.js
Line 168 in e6de227
As I guess previous versions of Channel API in Storybook contained all possible events as empty arrays and that detection worked fine, but from some release of storybook it started to have undefined until some listener be registred, it seems like this detection never worked since some release of storybook (I dont know which exact) and that's why Happo didn't wait for the actual story render
Easy way to prove it:
npx http-server package-123
storyRenderPhaseChanged
message listener on the channel and immediately unregister itconst channel = window.__STORYBOOK_ADDONS_CHANNEL__;
const callback = (...args) => console.log(...args);
channel.on('storyRenderPhaseChanged', callback);
channel.off('storyRenderPhaseChanged', callback);
await window.happo.nextExample()
Just like in happo run
, happo dev
could use the runner from the plugin, which would inform the happo.io script:
Then, together with filtering stories that should be done, we could have a nice developer experience with Happo, when a dev can work only on one component, and have a live screenshots of it in all the happo targets.
Right now the plugin fails if you use storybook version 4. Something about not finding the webpack config.
Follow up observation of userEvent
behavior after this bug was fixed.
(Storybook in combination with @storybook/addon-interactions)
When userEvent.type
is used with a delay in a play
function the snapshot doesn't wait for the complete input. But it's working correctly when userEvent.type
is used in beforeSnapshot
.
I added the case to the demo repo:
https://github.com/Q-Man/happo-storybook-userevent/
Here's the code:
import React from 'react'
import {userEvent, screen, fireEvent} from "@storybook/testing-library"
const Test = () => {
return <div>
<input type="radio" name="test" id="test1"/>
<label htmlFor="test1">test1</label>
<input type="radio" name="test" id="test2"/>
<label htmlFor="test2">test2</label>
<input type="text" id="test3" data-testid="test3" style={{width: '250px'}}/>
</div>
}
const meta = {component: Test}
export const Default = {}
const play = async () => {
await userEvent.click(screen.getByLabelText('test2'))
await userEvent.type(screen.getByTestId('test3'), 'hello world I am a bit a longer text', {delay: 100})
}
export const WithUserEvent = {
// incomplete snapshot, doesn't wait for complete typing
play
}
export const WithBeforeScreenshot = {
parameters: {
happo: {
// complete snapshot, works as expected
beforeScreenshot: play,
}
}
}
export default meta
Sometimes we have multiple steps in a play
function that we all want to capture as a Happo screenshot. E.g.
play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement);
await step('Regular dropdown', async () => {
await userEvent.click(
canvas.getAllByShadowRole('button', { name: /dropdown link/i })[0],
);
// TAKE HAPPO SCREENSHOT
await expect(
canvas.getAllByText('This is a link')[0],
).toBeInTheDocument();
});
await step('Megamenu dropdown', async () => {
await userEvent.click(
canvas.getAllByShadowRole('button', { name: /dropdown link/i })[1],
);
// TAKE HAPPO SCREENSHOT
await expect(canvas.getAllByText('Item 1')[0]).toBeInTheDocument();
});
};
We need a way to tell Happo to create a screenshot at both of the // TAKE HAPPO SCREENSHOT
moments.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.