mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-01 05:05:25 +08:00
Merge branch 'next' into typescript-config
# Conflicts: # docs/configure/overview.md
This commit is contained in:
commit
a5eace1db0
@ -277,9 +277,9 @@ jobs:
|
||||
- run:
|
||||
name: run e2e tests cra
|
||||
command: yarn test:e2e-framework --pnp cra
|
||||
- run:
|
||||
name: run e2e tests vue
|
||||
command: yarn test:e2e-framework --pnp sfcVue
|
||||
# - run:
|
||||
# name: run e2e tests vue
|
||||
# command: yarn test:e2e-framework --pnp sfcVue
|
||||
- run:
|
||||
name: prep artifacts
|
||||
when: always
|
||||
|
@ -14,6 +14,25 @@
|
||||
},
|
||||
{
|
||||
"pattern": "https://stackblitz.com/*"
|
||||
},
|
||||
{
|
||||
"pattern": "https://*.chromatic.com"
|
||||
},
|
||||
{
|
||||
"pattern": "https://www.chromatic.com/build?*"
|
||||
},
|
||||
{
|
||||
"pattern": "http://*.nodeca.com"
|
||||
},
|
||||
{
|
||||
"pattern": "http://definitelytyped.org/*"
|
||||
},
|
||||
{
|
||||
"pattern": "https://yoursite.com/*"
|
||||
},
|
||||
{
|
||||
"pattern": "https://my-specific-domain.com"
|
||||
}
|
||||
]
|
||||
],
|
||||
"aliveStatusCodes": [429, 200]
|
||||
}
|
120
CHANGELOG.md
120
CHANGELOG.md
@ -1,3 +1,123 @@
|
||||
## 6.5.0-rc.1 (May 18, 2022)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- CLI: Improve webpack version and add detection of nextjs ([#18220](https://github.com/storybookjs/storybook/pull/18220))
|
||||
- ArgsTable: Gracefully handle conditional args failures ([#18248](https://github.com/storybookjs/storybook/pull/18248))
|
||||
- Controls: Fix reset button broken for !undefined URL values ([#18231](https://github.com/storybookjs/storybook/pull/18231))
|
||||
- Vue3: Add support for TSX in single file components ([#18038](https://github.com/storybookjs/storybook/pull/18038))
|
||||
|
||||
## 6.5.0-rc.0 (May 17, 2022)
|
||||
|
||||
### Features
|
||||
|
||||
- Addon-a11y: Show % of users in toolbar menu ([#18003](https://github.com/storybookjs/storybook/pull/18003))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Web-components: Clean Lit Expression comments in story source ([#18108](https://github.com/storybookjs/storybook/pull/18108))
|
||||
- Vue: Map args correctly in CSF3 implicit render function ([#18209](https://github.com/storybookjs/storybook/pull/18209))
|
||||
- Vue3: Fix CSF3 implicit render function when storyStoreV7 is enabled ([#18208](https://github.com/storybookjs/storybook/pull/182)
|
||||
|
||||
### Maintenance
|
||||
|
||||
- CLI: Don't throw is Ctrl + C was pressed when selecting a package in the build command ([#18195](https://github.com/storybookjs/storybook/pull/18195))
|
||||
- Build: Cleanup noise from unit tests ([#18196](https://github.com/storybookjs/storybook/pull/18196))
|
||||
|
||||
### Dependency Upgrades
|
||||
|
||||
- Fixed PnP compatibility for bundled components package ([#18015](https://github.com/storybookjs/storybook/pull/18015))
|
||||
|
||||
## 6.5.0-beta.8 (May 11, 2022)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Composition: Fix metadata.json incorrectly overriding main.js refs versions ([#18185](https://github.com/storybookjs/storybook/pull/18185))
|
||||
|
||||
### Maintenance
|
||||
|
||||
- Examples: Set channelOptions to disallow function serialization ([#18071](https://github.com/storybookjs/storybook/pull/18071))
|
||||
|
||||
### Dependency Upgrades
|
||||
|
||||
- Upgrade to telejson 6 ([#18164](https://github.com/storybookjs/storybook/pull/18164))
|
||||
|
||||
## 6.5.0-beta.7 (May 9, 2022)
|
||||
|
||||
### Features
|
||||
|
||||
- CSF3: Add title prefix support for stories with custom titles ([#17724](https://github.com/storybookjs/storybook/pull/17724))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Components: Fix race conditions in SyntaxHighlighter ([#18158](https://github.com/storybookjs/storybook/pull/18158))
|
||||
|
||||
### Maintenance
|
||||
|
||||
- API: Deprecate isToolshown, rename to showToolbar ([#18131](https://github.com/storybookjs/storybook/pull/18131))
|
||||
|
||||
## 6.5.0-beta.6 (May 6, 2022)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Controls: Fix undefined args handling ([#18135](https://github.com/storybookjs/storybook/pull/18135))
|
||||
|
||||
### Maintenance
|
||||
|
||||
- CLI: Update Introduction.stories.mdx template to be MDX2-friendly ([#18141](https://github.com/storybookjs/storybook/pull/18141))
|
||||
|
||||
### Dependency Upgrades
|
||||
|
||||
- Remove jest from cli peerDependencies ([#18149](https://github.com/storybookjs/storybook/pull/18149))
|
||||
|
||||
## 6.5.0-beta.5 (May 4, 2022)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Core: Fix anonymous ID generation ([#18133](https://github.com/storybookjs/storybook/pull/18133))
|
||||
|
||||
## 6.5.0-beta.4 (May 4, 2022)
|
||||
|
||||
### Features
|
||||
|
||||
- UI: Add a parent level toolbar exclusion key for tabs ([#18106](https://github.com/storybookjs/storybook/pull/18106))
|
||||
- Addon-a11y: Display a11y issues number in addon tab title ([#17983](https://github.com/storybookjs/storybook/pull/17983))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Addon-docs: Fix Canvas block CURRENT_SELECTION handling ([#18130](https://github.com/storybookjs/storybook/pull/18130))
|
||||
- Telemetry: Add safecheck for crash reports ([#18129](https://github.com/storybookjs/storybook/pull/18129))
|
||||
- Addon-a11y: Fix a11y params > element use ([#17989](https://github.com/storybookjs/storybook/pull/17989))
|
||||
|
||||
## 6.5.0-beta.3 (May 4, 2022)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- UI: Externalize `react-syntax-highlighter` from components ([#18127](https://github.com/storybookjs/storybook/pull/18127))
|
||||
|
||||
## 6.5.0-beta.2 (May 2, 2022)
|
||||
|
||||
### Features
|
||||
|
||||
- Core: Add optional telemetry and crash reporting ([#18046](https://github.com/storybookjs/storybook/pull/18046))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Controls: Fix URL deserialization for argTypes with mapping ([#18124](https://github.com/storybookjs/storybook/pull/18124))
|
||||
- Core: Fix telemetry project root detection ([#18125](https://github.com/storybookjs/storybook/pull/18125))
|
||||
- React: Fix version detection for older versions of `react-dom` ([#18105](https://github.com/storybookjs/storybook/pull/18105))
|
||||
- CLI: Add non-monorepo testing tools to exclude lists ([#18092](https://github.com/storybookjs/storybook/pull/18092))
|
||||
|
||||
### Maintenance
|
||||
|
||||
- Examples: Update example to restore 6.4 auto-title behavior in UI ([#18109](https://github.com/storybookjs/storybook/pull/18109))
|
||||
- CLI: Remove git.io URL ([#18070](https://github.com/storybookjs/storybook/pull/18070))
|
||||
- UI: Make panel position a persistent preference ([#18036](https://github.com/storybookjs/storybook/pull/18036))
|
||||
|
||||
### Dependency Upgrades
|
||||
|
||||
- React: Fix jest-specific-snapshot dev dependency ([#18095](https://github.com/storybookjs/storybook/pull/18095))
|
||||
|
||||
## 6.5.0-beta.1 (April 28, 2022)
|
||||
|
||||
### Features
|
||||
|
@ -15,7 +15,7 @@ This document outlines some of the processes that the maintainers should adhere
|
||||
| api:(name) | Issue, bug, or pull request related to Storybook's API (e.g.,[makeDecorator](/docs/addons/addons-api.md#makeDecorator-API)) |
|
||||
| args | Issue, bug, or pull request related to Storybook's [args](/docs/writing-stories/args.md) |
|
||||
| babel/webpack | Issue, bug, or pull request related to Storybook's build system (e.g., Webpack or Babel), for Webpack 5 issues see below |
|
||||
| block:(name) | Issue or bug within a certain surface are of Storybook (e.g., [argsTable](/docs/writing-docs/doc-blocks.md#argstable)) |
|
||||
| block:(name) | Issue or bug within a certain surface are of Storybook (e.g., [argsTable](/docs/writing-docs/doc-block-argstable.md)) |
|
||||
| BREAKING CHANGE | Issue or pull request that introduces a breaking change within Storybook's ecosystem. |
|
||||
| BREAKING PRERELASE | Breaking, but only for prerelease users (not relative to the stable release) |
|
||||
| build-storybook | Issue, bug, or pull request related to Storybook's production build |
|
||||
@ -24,7 +24,7 @@ This document outlines some of the processes that the maintainers should adhere
|
||||
| cli | Issue, bug, or pull request that affects the Storybook's CLI |
|
||||
| compatibility with other tools | Issue, bug, or pull request between Storybook and other tools (e.g., [Nuxt](https://nuxtjs.org/)) |
|
||||
| components | Issue, bug, or pull request related to Storybook's internal components |
|
||||
| composition | Issue, bug, or pull request related to Storybook [Composition](/docs/workflows/storybook-composition.md) |
|
||||
| composition | Issue, bug, or pull request related to Storybook [Composition](/docs/sharing/storybook-composition.md) |
|
||||
| configuration | Issue, bug, or pull request related to Storybook [configuration](/docs/configure/overview.md) |
|
||||
| core | Issue, bug, or pull request related to Storybook's Core |
|
||||
| cra | Issue, bug, or pull request that affects Storybook's compatibility with Create React APP ([CRA](https://create-react-app.dev/docs/getting-started/))|
|
||||
|
96
MIGRATION.md
96
MIGRATION.md
@ -1,7 +1,9 @@
|
||||
<h1>Migration</h1>
|
||||
|
||||
- [From version 6.4.x to 6.5.0](#from-version-64x-to-650)
|
||||
- [Vue 3 upgrade](#vue-3-upgrade)
|
||||
- [React18 new root API](#react18-new-root-api)
|
||||
- [Renamed isToolshown to showToolbar](#renamed-istoolshown-to-showtoolbar)
|
||||
- [Deprecated register.js](#deprecated-registerjs)
|
||||
- [Dropped support for addon-actions addDecorators](#dropped-support-for-addon-actions-adddecorators)
|
||||
- [Vite builder renamed](#vite-builder-renamed)
|
||||
@ -10,6 +12,7 @@
|
||||
- [CSF3 auto-title improvements](#csf3-auto-title-improvements)
|
||||
- [Auto-title filename case](#auto-title-filename-case)
|
||||
- [Auto-title redundant filename](#auto-title-redundant-filename)
|
||||
- [Auto-title always prefixes](#auto-title-always-prefixes)
|
||||
- [From version 6.3.x to 6.4.0](#from-version-63x-to-640)
|
||||
- [Automigrate](#automigrate)
|
||||
- [CRA5 upgrade](#cra5-upgrade)
|
||||
@ -200,6 +203,10 @@
|
||||
|
||||
## From version 6.4.x to 6.5.0
|
||||
|
||||
### Vue 3 upgrade
|
||||
|
||||
Storybook 6.5 supports Vue 3 out of the box when you install it fresh. However, if you're upgrading your project from a previous version, you'll need to [follow the steps for opting-in to webpack 5](#webpack-5).
|
||||
|
||||
### React18 new root API
|
||||
|
||||
React 18 introduces a [new root API](https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html#updates-to-client-rendering-apis). Starting in 6.5, Storybook for React will auto-detect your react version and use the new root API automatically if you're on React18.
|
||||
@ -212,6 +219,21 @@ module.exports = {
|
||||
};
|
||||
```
|
||||
|
||||
### Renamed isToolshown to showToolbar
|
||||
|
||||
Storybook's [manager API](docs/addons/addons-api.md) has deprecated the `isToolshown` option (to show/hide the toolbar) and renamed it to `showToolbar` for consistency with other similar UI options.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
// .storybook/manager.js
|
||||
import { addons } from '@storybook/addons';
|
||||
|
||||
addons.setConfig({
|
||||
showToolbar: false,
|
||||
});
|
||||
```
|
||||
|
||||
### Deprecated register.js
|
||||
|
||||
In ancient versions of Storybook, addons were registered by referring to `addon-name/register.js`. This is going away in SB7.0. Instead you should just add `addon-name` to the `addons` array in `.storybook/main.js`.
|
||||
@ -288,6 +310,19 @@ This might be considered a breaking change. However, we feel justified to releas
|
||||
1. We consider it a bug in the initial auto-title implementation
|
||||
2. CSF3 and the auto-title feature are experimental, and we reserve the right to make breaking changes outside of semver (tho we try to avoid it)
|
||||
|
||||
If you want to restore the old titles in the UI, you can customize your sidebar with the following code snippet in `.storybook/manager.js`:
|
||||
|
||||
```js
|
||||
import { addons } from '@storybook/addons';
|
||||
import startCase from 'lodash/startCase';
|
||||
|
||||
addons.setConfig({
|
||||
sidebar: {
|
||||
renderLabel: ({ name, type }) => (type === 'story' ? name : startCase(name)),
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### Auto-title redundant filename
|
||||
|
||||
The heuristic failed in the common scenario in which each component gets its own directory, e.g. `atoms/Button/Button.stories.js`, which would result in the redundant title `Atoms/Button/Button`. Alternatively, `atoms/Button/index.stories.js` would result in `Atoms/Button/Index`.
|
||||
@ -301,6 +336,36 @@ Since CSF3 is experimental, we are introducing this technically breaking change
|
||||
export default { title: 'Atoms/Button/Button' };
|
||||
```
|
||||
|
||||
#### Auto-title always prefixes
|
||||
|
||||
When the user provides a `prefix` in their `main.js` `stories` field, it now prefixes all titles to matching stories, whereas in 6.4 and earlier it only prefixed auto-titles.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
```js
|
||||
// main.js
|
||||
module.exports = {
|
||||
stories: [{ directory: '../src', titlePrefix: 'Custom' }]
|
||||
}
|
||||
|
||||
// ../src/NoTitle.stories.js
|
||||
export default { component: Foo };
|
||||
|
||||
// ../src/Title.stories.js
|
||||
export default { component: Bar, title: 'Bar' }
|
||||
```
|
||||
|
||||
In 6.4, the final titles would be:
|
||||
|
||||
- `NoTitle.stories.js` => `Custom/NoTitle`
|
||||
- `Title.stories.js` => `Bar`
|
||||
|
||||
In 6.5, the final titles would be:
|
||||
|
||||
- `NoTitle.stories.js` => `Custom/NoTitle`
|
||||
- `Title.stories.js` => `Custom/Bar`
|
||||
|
||||
<!-- markdown-link-check-disable -->
|
||||
## From version 6.3.x to 6.4.0
|
||||
|
||||
### Automigrate
|
||||
@ -314,7 +379,9 @@ For example, if you're in a webpack5 project but still use Storybook's default w
|
||||
You can run the existing suite of automigrations to see which ones apply to your project. This won't update any files unless you accept the changes:
|
||||
|
||||
```
|
||||
|
||||
npx sb@next automigrate
|
||||
|
||||
```
|
||||
|
||||
The automigration suite also runs when you create a new project (`sb init`) or when you update storybook (`sb upgrade`).
|
||||
@ -324,7 +391,9 @@ The automigration suite also runs when you create a new project (`sb init`) or w
|
||||
Storybook 6.3 supports CRA5 out of the box when you install it fresh. However, if you're upgrading your project from a previous version, you'll need to upgrade the configuration. You can do this automatically by running:
|
||||
|
||||
```
|
||||
|
||||
npx sb@next automigrate
|
||||
|
||||
```
|
||||
|
||||
Or you can do the following steps manually to force Storybook to use webpack 5 for building your project:
|
||||
@ -685,7 +754,29 @@ The `--static-dir` flag has been deprecated and will be removed in Storybook 7.0
|
||||
|
||||
### Webpack 5
|
||||
|
||||
Storybook 6.3 brings opt-in support for building both your project and the manager UI with webpack 5. To do so:
|
||||
Storybook 6.3 brings opt-in support for building both your project and the manager UI with webpack 5. To do so, there are two ways:
|
||||
|
||||
1 - Upgrade command
|
||||
|
||||
If you're upgrading your Storybook version, run this command, which will both upgrade your dependencies but also detect whether you should migrate to webpack5 builders and apply the changes automatically:
|
||||
|
||||
```shell
|
||||
npx sb upgrade
|
||||
```
|
||||
|
||||
2 - Automigrate command
|
||||
|
||||
If you don't want to change your Storybook version but want Storybook to detect whether you should migrate to webpack5 builders and apply the changes automatically:
|
||||
|
||||
```shell
|
||||
npx sb automigrate
|
||||
```
|
||||
|
||||
3 - Manually
|
||||
|
||||
If either methods did not work or you just want to proceed manually, do the following steps:
|
||||
|
||||
Install the dependencies:
|
||||
|
||||
```shell
|
||||
yarn add @storybook/builder-webpack5 @storybook/manager-webpack5 --dev
|
||||
@ -2153,7 +2244,7 @@ Theming has been rewritten in v5. If you used theming in v4, please consult the
|
||||
|
||||
### Story hierarchy defaults
|
||||
|
||||
Storybook's UI contains a hierarchical tree of stories that can be configured by `hierarchySeparator` and `hierarchyRootSeparator` [options](./addons/options/README.md).
|
||||
Storybook's UI contains a hierarchical tree of stories that can be configured by `hierarchySeparator` and `hierarchyRootSeparator` [options](https://github.com/storybookjs/deprecated-addons/blob/master/MIGRATION.md#options-addon-deprecated).
|
||||
|
||||
In Storybook 4.x the values defaulted to `null` for both of these options, so that there would be no hierarchy by default.
|
||||
|
||||
@ -2818,3 +2909,4 @@ If you **are** using these addons, it takes two steps to migrate:
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { linkTo } from '@storybook/addon-links';
|
||||
```
|
||||
<!-- markdown-link-check-enable -->
|
37
README.md
37
README.md
@ -42,8 +42,8 @@
|
||||
</a>
|
||||
</p>
|
||||
|
||||
[Storybook](https://storybook.js.org) is a development environment for UI components.
|
||||
It allows you to browse a component library, view the different states of each component, and interactively develop and test components. Find out more at https://storybook.js.org.
|
||||
<p align="center">Storybook is a development environment for UI components.<br/>
|
||||
It allows you to browse a component library, view the different states of each component, and interactively develop and test components.<br/>Find out more at https://storybook.js.org.</p>
|
||||
|
||||
<center>
|
||||
<img src="https://raw.githubusercontent.com/storybookjs/storybook/main/media/storybook-intro.gif" width="100%" />
|
||||
@ -51,8 +51,9 @@ It allows you to browse a component library, view the different states of each c
|
||||
|
||||
<p align="center">
|
||||
View README for:<br/>
|
||||
<a href="https://github.com/storybookjs/storybook/blob/main/README.md" title="latest"><img alt="latest" src="https://img.shields.io/npm/v/@storybook/core/latest.svg" /></a>
|
||||
<a href="https://github.com/storybookjs/storybook/blob/next/README.md" title="next"><img alt="next" src="https://img.shields.io/npm/v/@storybook/core/next.svg" /></a>
|
||||
<a href="https://github.com/storybookjs/storybook/blob/main/README.md" title="latest"><img alt="latest" src="https://img.shields.io/npm/v/@storybook/core/latest?style=for-the-badge&logo=storybook&logoColor=ffffff&color=66BF3C" /></a>
|
||||
<a href="https://github.com/storybookjs/storybook/blob/next/README.md" title="next"><img alt="next" src="https://img.shields.io/npm/v/@storybook/core/next?style=for-the-badge&logo=storybook&logoColor=ffffff&color=1EA7FD" /></a>
|
||||
<a href="https://github.com/storybookjs/storybook/blob/future/base/README.md" title="future"><img alt="future" src="https://img.shields.io/npm/v/@storybook/core-common/future?style=for-the-badge&logo=storybook&logoColor=ffffff&color=FF4785" /></a>
|
||||
</p>
|
||||
|
||||
## Table of contents
|
||||
@ -80,7 +81,7 @@ Documentation can be found [Storybook's docs site](https://storybook.js.org/docs
|
||||
|
||||
### Examples
|
||||
|
||||
Here are some featured examples that you can reference to see how Storybook works: <https://storybook.js.org/docs/react/get-started/examples>
|
||||
Here are some featured examples that you can reference to see how Storybook works: <https://storybook.js.org/showcase>
|
||||
|
||||
Storybook comes with a lot of [addons](https://storybook.js.org/docs/react/configure/storybook-addons) for component design, documentation, testing, interactivity, and so on. Storybook's API makes it possible to configure and extend in various ways. It has even been extended to support React Native, Android, iOS, and Flutter development for mobile.
|
||||
|
||||
@ -92,19 +93,19 @@ For additional help, join us in the [Storybook Discord](https://discord.gg/story
|
||||
|
||||
### Supported Frameworks
|
||||
|
||||
| Framework | Demo | |
|
||||
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| [React](app/react) | [v6.4.x](https://storybookjs.netlify.com/official-storybook/?path=/story/*) | [](app/react) |
|
||||
| [Vue](app/vue) | [v6.4.x](https://storybookjs.netlify.com/vue-kitchen-sink/) | [](app/vue) |
|
||||
| [Angular](app/angular) | [v6.4.x](https://storybookjs.netlify.com/angular-cli/) | [](app/angular) |
|
||||
| [Web components](app/web-components) | [v6.4.x](https://storybookjs.netlify.com/web-components-kitchen-sink/) | [](app/web-components) |
|
||||
| [React Native](https://github.com/storybookjs/react-native) | - | [](app/react-native) |
|
||||
| [HTML](app/html) | [v6.4.x](https://storybookjs.netlify.com/html-kitchen-sink/) | [](app/html) |
|
||||
| [Ember](app/ember) | [v6.4.x](https://storybookjs.netlify.com/ember-cli/) | [](app/ember) |
|
||||
| [Svelte](app/svelte) | [v6.4.x](https://storybookjs.netlify.com/svelte-kitchen-sink/) | [](app/svelte) |
|
||||
| [Preact](app/preact) | [v6.4.x](https://storybookjs.netlify.com/preact-kitchen-sink/) | [](app/preact) |
|
||||
| [Marionette.js](https://github.com/storybookjs/marionette) | - | [](app/marionette) |
|
||||
| [Android, iOS, Flutter](https://github.com/storybookjs/native) | [v6.4.x](https://storybookjs.github.io/native/@storybook/native-flutter-example/index.html) | [](https://github.com/storybookjs/native) |
|
||||
| Framework | Demo | |
|
||||
| -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [React](app/react) | [](https://storybookjs.netlify.com/official-storybook/?path=/story/*) | [](app/react) |
|
||||
| [Vue](app/vue) | [](https://storybookjs.netlify.com/vue-kitchen-sink/) | [](app/vue) |
|
||||
| [Angular](app/angular) | [](https://storybookjs.netlify.com/angular-cli/) | [](app/angular) |
|
||||
| [Web components](app/web-components) | [](https://storybookjs.netlify.com/web-components-kitchen-sink/) | [](app/web-components) |
|
||||
| [React Native](https://github.com/storybookjs/react-native) | - | [](https://github.com/storybookjs/react-native) |
|
||||
| [HTML](app/html) | [](https://storybookjs.netlify.com/html-kitchen-sink/) | [](app/html) |
|
||||
| [Ember](app/ember) | [](https://storybookjs.netlify.com/ember-cli/) | [](app/ember) |
|
||||
| [Svelte](app/svelte) | [](https://storybookjs.netlify.com/svelte-kitchen-sink/) | [](app/svelte) |
|
||||
| [Preact](app/preact) | [](https://storybookjs.netlify.com/preact-kitchen-sink/) | [](app/preact) |
|
||||
| [Marionette.js](https://github.com/storybookjs/marionette) | - | [](https://github.com/storybookjs/marionette) |
|
||||
| [Android, iOS, Flutter](https://github.com/storybookjs/native) | [](https://storybookjs.github.io/native/@storybook/native-flutter-example/index.html) | [](https://github.com/storybookjs/native) |
|
||||
|
||||
### Sub Projects
|
||||
|
||||
|
@ -81,12 +81,11 @@ there gathering upvotes and "me too" comments. We need a way to make sure that
|
||||
these bugs get addressed.
|
||||
|
||||
For every non-PATCH release, we nominate a small number of bugs that must be
|
||||
addressed before a release can go out by adding them to the milestone. For example, here's a list of blocking bugs [for the 3.2 milestone](https://github.com/storybookjs/storybook/milestone/3).
|
||||
addressed before a release can go out by adding them to the milestone. For example, here's a list of blocking bugs [for the 6.5 milestone](https://github.com/storybookjs/storybook/milestone/75).
|
||||
|
||||
Adding bugs to the milestone helps people looking for good ways to contribute,
|
||||
or to understand what is blocking the release so they can actually do something
|
||||
about it. Discussion about which bugs are critical happens in the `#maintenance`
|
||||
channel [in our Slack](https://now-examples-slackin-rrirkqohko.now.sh/) [](https://now-examples-slackin-rrirkqohko.now.sh/)
|
||||
about it. Discussion about which bugs are critical happens in the [`#maintenance` channel](https://discord.com/channels/486522875931656193/490070912448724992) in our Discord Server
|
||||
|
||||
If you're experiencing a bug, the best way to make sure that it gets attention
|
||||
is to upvote it by adding a "thumbs-up" reaction in Github. This way important
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| ---------- | ------------------ |
|
||||
| 6.3, 6.4 | :white_check_mark: |
|
||||
| Version | Supported |
|
||||
| --------------- | ------------------ |
|
||||
| 6.3, 6.4, 6.5 | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
@ -15,6 +15,7 @@ function __setMockFiles(newMockFiles) {
|
||||
const readFile = async (filePath) => mockFiles[filePath];
|
||||
const readFileSync = (filePath = '') => mockFiles[filePath];
|
||||
const existsSync = (filePath) => !!mockFiles[filePath];
|
||||
const readJsonSync = (filePath = '') => JSON.parse(mockFiles[filePath]);
|
||||
const lstatSync = (filePath) => ({
|
||||
isFile: () => !!mockFiles[filePath],
|
||||
});
|
||||
@ -23,6 +24,7 @@ const lstatSync = (filePath) => ({
|
||||
fs.__setMockFiles = __setMockFiles;
|
||||
fs.readFile = readFile;
|
||||
fs.readFileSync = readFileSync;
|
||||
fs.readJsonSync = readJsonSync;
|
||||
fs.existsSync = existsSync;
|
||||
fs.lstatSync = lstatSync;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-a11y",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Test component compliance with web accessibility standards",
|
||||
"keywords": [
|
||||
"a11y",
|
||||
@ -45,14 +45,14 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/api": "6.5.0-beta.1",
|
||||
"@storybook/channels": "6.5.0-beta.1",
|
||||
"@storybook/client-logger": "6.5.0-beta.1",
|
||||
"@storybook/components": "6.5.0-beta.1",
|
||||
"@storybook/core-events": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/api": "6.5.0-rc.1",
|
||||
"@storybook/channels": "6.5.0-rc.1",
|
||||
"@storybook/client-logger": "6.5.0-rc.1",
|
||||
"@storybook/components": "6.5.0-rc.1",
|
||||
"@storybook/core-events": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@storybook/theming": "6.5.0-beta.1",
|
||||
"@storybook/theming": "6.5.0-rc.1",
|
||||
"axe-core": "^4.2.0",
|
||||
"core-js": "^3.8.2",
|
||||
"global": "^4.4.0",
|
||||
@ -81,7 +81,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/index.js",
|
||||
"storybook": {
|
||||
"displayName": "Accessibility",
|
||||
|
@ -15,11 +15,6 @@ let active = false;
|
||||
// Holds latest story we requested a run
|
||||
let activeStoryId: string | undefined;
|
||||
|
||||
const getElement = () => {
|
||||
const storyRoot = document.getElementById('story-root');
|
||||
return storyRoot ? storyRoot.childNodes : document.getElementById('root');
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle A11yContext events.
|
||||
* Because the event are sent without manual check, we split calls
|
||||
@ -41,13 +36,14 @@ const run = async (storyId: string) => {
|
||||
channel.emit(EVENTS.RUNNING);
|
||||
const axe = (await import('axe-core')).default;
|
||||
|
||||
const { element = getElement(), config, options = {} } = input;
|
||||
const { element = '#root', config, options = {} } = input;
|
||||
const htmlElement = document.querySelector(element);
|
||||
axe.reset();
|
||||
if (config) {
|
||||
axe.configure(config);
|
||||
}
|
||||
|
||||
const result = await axe.run(element, options);
|
||||
const result = await axe.run(htmlElement, options);
|
||||
// It's possible that we requested a new run on a different story.
|
||||
// Unfortunately, axe doesn't support a cancel method to abort current run.
|
||||
// We check if the story we run against is still the current one,
|
||||
|
@ -63,6 +63,7 @@ describe('A11YPanel', () => {
|
||||
mockedApi.useStorybookState.mockReset();
|
||||
mockedApi.useAddonState.mockReset();
|
||||
|
||||
mockedApi.useAddonState.mockImplementation((_, defaultState) => React.useState(defaultState));
|
||||
mockedApi.useChannel.mockReturnValue(jest.fn());
|
||||
mockedApi.useParameter.mockReturnValue({ manual: false });
|
||||
const state: Partial<api.State> = { storyId: 'jest' };
|
||||
|
@ -53,11 +53,13 @@ describe('A11YPanel', () => {
|
||||
beforeEach(() => {
|
||||
mockedApi.useChannel.mockReset();
|
||||
mockedApi.useStorybookState.mockReset();
|
||||
mockedApi.useAddonState.mockReset();
|
||||
|
||||
mockedApi.useAddonState.mockImplementation((_, defaultState) => React.useState(defaultState));
|
||||
mockedApi.useChannel.mockReturnValue(jest.fn());
|
||||
const state: Partial<api.State> = { storyId };
|
||||
const storyState: Partial<api.State> = { storyId };
|
||||
// Lazy to mock entire state
|
||||
mockedApi.useStorybookState.mockReturnValue(state as any);
|
||||
mockedApi.useStorybookState.mockReturnValue(storyState as any);
|
||||
});
|
||||
|
||||
it('should render children', () => {
|
||||
|
@ -1,11 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import { themes, convert } from '@storybook/theming';
|
||||
import { Result } from 'axe-core';
|
||||
import { useChannel, useStorybookState } from '@storybook/api';
|
||||
import { useChannel, useStorybookState, useAddonState } from '@storybook/api';
|
||||
import { STORY_CHANGED, STORY_RENDERED } from '@storybook/core-events';
|
||||
import { EVENTS } from '../constants';
|
||||
import { ADDON_ID, EVENTS } from '../constants';
|
||||
|
||||
interface Results {
|
||||
export interface Results {
|
||||
passes: Result[];
|
||||
violations: Result[];
|
||||
incomplete: Result[];
|
||||
@ -22,9 +22,9 @@ interface A11yContextStore {
|
||||
}
|
||||
|
||||
const colorsByType = [
|
||||
convert(themes.normal).color.negative, // VIOLATION,
|
||||
convert(themes.normal).color.positive, // PASS,
|
||||
convert(themes.normal).color.warning, // INCOMPLETION,
|
||||
convert(themes.light).color.negative, // VIOLATION,
|
||||
convert(themes.light).color.positive, // PASS,
|
||||
convert(themes.light).color.warning, // INCOMPLETION,
|
||||
];
|
||||
|
||||
export const A11yContext = React.createContext<A11yContextStore>({
|
||||
@ -52,7 +52,7 @@ const defaultResult = {
|
||||
};
|
||||
|
||||
export const A11yContextProvider: React.FC<A11yContextProviderProps> = ({ active, ...props }) => {
|
||||
const [results, setResults] = React.useState<Results>(defaultResult);
|
||||
const [results, setResults] = useAddonState<Results>(ADDON_ID, defaultResult);
|
||||
const [tab, setTab] = React.useState(0);
|
||||
const [highlighted, setHighlighted] = React.useState<string[]>([]);
|
||||
const { storyId } = useStorybookState();
|
||||
|
69
addons/a11y/src/components/VisionSimulator.test.tsx
Normal file
69
addons/a11y/src/components/VisionSimulator.test.tsx
Normal file
@ -0,0 +1,69 @@
|
||||
import React from 'react';
|
||||
import { render, fireEvent, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { ThemeProvider, themes, convert } from '@storybook/theming';
|
||||
import { VisionSimulator, baseList } from './VisionSimulator';
|
||||
|
||||
const getOptionByNameAndPercentage = (option: string, percentage: number) =>
|
||||
screen.getByText(
|
||||
(content, element) =>
|
||||
content !== '' &&
|
||||
element.textContent === option &&
|
||||
(percentage === undefined || element.nextSibling.textContent === `${percentage}% of users`)
|
||||
);
|
||||
|
||||
function ThemedVisionSimulator() {
|
||||
return (
|
||||
<ThemeProvider theme={convert(themes.light)}>
|
||||
<VisionSimulator />
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
describe('Vision Simulator', () => {
|
||||
it('should render tool button', async () => {
|
||||
// when
|
||||
render(<ThemedVisionSimulator />);
|
||||
|
||||
// then
|
||||
// waitFor because WithTooltip is a lazy component
|
||||
await waitFor(() => expect(screen.getByTitle('Vision simulator')).toBeInTheDocument());
|
||||
});
|
||||
|
||||
it.skip('should display tooltip on click', async () => {
|
||||
// given
|
||||
render(<ThemedVisionSimulator />);
|
||||
await waitFor(() => expect(screen.getByTitle('Vision simulator')).toBeInTheDocument());
|
||||
|
||||
// when
|
||||
userEvent.click(screen.getByRole('button', { name: 'Vision simulator' }));
|
||||
|
||||
// then
|
||||
await waitFor(() => expect(screen.getByText('blurred vision')).toBeInTheDocument());
|
||||
baseList.forEach(({ name, percentage }) =>
|
||||
expect(getOptionByNameAndPercentage(name, percentage)).toBeInTheDocument()
|
||||
);
|
||||
});
|
||||
|
||||
it.skip('should set filter', async () => {
|
||||
// given
|
||||
render(<ThemedVisionSimulator />);
|
||||
await waitFor(() => expect(screen.getByTitle('Vision simulator')).toBeInTheDocument());
|
||||
userEvent.click(screen.getByRole('button', { name: 'Vision simulator' }));
|
||||
await waitFor(() => expect(screen.getByText('blurred vision')).toBeInTheDocument());
|
||||
|
||||
// when
|
||||
fireEvent.click(screen.getByText('blurred vision'));
|
||||
|
||||
// then
|
||||
// eslint-disable-next-line no-undef
|
||||
const rule = Object.values(document.styleSheets)
|
||||
.filter(({ cssRules }) => cssRules)
|
||||
.map(({ cssRules }) => Object.values(cssRules))
|
||||
.flat()
|
||||
.find((cssRule: CSSRule) => cssRule.selectorText === '#storybook-preview-iframe');
|
||||
|
||||
expect(rule).toBeDefined();
|
||||
expect(rule.style.filter).toBe('blur(2px)');
|
||||
});
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
import React, { FunctionComponent, ReactNode, useState } from 'react';
|
||||
import React, { ReactNode, useState } from 'react';
|
||||
import { Global, styled } from '@storybook/theming';
|
||||
import { Icons, IconButton, WithTooltip, TooltipLinkList } from '@storybook/components';
|
||||
|
||||
@ -6,32 +6,37 @@ import { Filters } from './ColorFilters';
|
||||
|
||||
const iframeId = 'storybook-preview-iframe';
|
||||
|
||||
const baseList = [
|
||||
'blurred vision',
|
||||
'deuteranomaly',
|
||||
'deuteranopia',
|
||||
'protanomaly',
|
||||
'protanopia',
|
||||
'tritanomaly',
|
||||
'tritanopia',
|
||||
'achromatomaly',
|
||||
'achromatopsia',
|
||||
'grayscale',
|
||||
] as const;
|
||||
interface Option {
|
||||
name: string;
|
||||
percentage?: number;
|
||||
}
|
||||
|
||||
type Filter = typeof baseList[number] | null;
|
||||
export const baseList = [
|
||||
{ name: 'blurred vision', percentage: 22.9 },
|
||||
{ name: 'deuteranomaly', percentage: 2.7 },
|
||||
{ name: 'deuteranopia', percentage: 0.56 },
|
||||
{ name: 'protanomaly', percentage: 0.66 },
|
||||
{ name: 'protanopia', percentage: 0.59 },
|
||||
{ name: 'tritanomaly', percentage: 0.01 },
|
||||
{ name: 'tritanopia', percentage: 0.016 },
|
||||
{ name: 'achromatomaly', percentage: 0.00001 },
|
||||
{ name: 'achromatopsia', percentage: 0.0001 },
|
||||
{ name: 'grayscale' },
|
||||
] as Option[];
|
||||
|
||||
const getFilter = (filter: Filter) => {
|
||||
if (!filter) {
|
||||
type Filter = Option | null;
|
||||
|
||||
const getFilter = (filterName: string) => {
|
||||
if (!filterName) {
|
||||
return 'none';
|
||||
}
|
||||
if (filter === 'blurred vision') {
|
||||
if (filterName === 'blurred vision') {
|
||||
return 'blur(2px)';
|
||||
}
|
||||
if (filter === 'grayscale') {
|
||||
if (filterName === 'grayscale') {
|
||||
return 'grayscale(100%)';
|
||||
}
|
||||
return `url('#${filter}')`;
|
||||
return `url('#${filterName}')`;
|
||||
};
|
||||
|
||||
const Hidden = styled.div(() => ({
|
||||
@ -42,7 +47,7 @@ const Hidden = styled.div(() => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const ColorIcon = styled.span<{ filter: Filter }>(
|
||||
const ColorIcon = styled.span<{ filter: string }>(
|
||||
{
|
||||
background: 'linear-gradient(to right, #F44336, #FF9800, #FFEB3B, #8BC34A, #2196F3, #9C27B0)',
|
||||
borderRadius: '1rem',
|
||||
@ -66,6 +71,20 @@ export interface Link {
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
const Column = styled.span({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
});
|
||||
|
||||
const Title = styled.span({
|
||||
textTransform: 'capitalize',
|
||||
});
|
||||
|
||||
const Description = styled.span(({ theme }) => ({
|
||||
fontSize: 11,
|
||||
color: theme.textMutedColor,
|
||||
}));
|
||||
|
||||
const getColorList = (active: Filter, set: (i: Filter) => void): Link[] => [
|
||||
...(active !== null
|
||||
? [
|
||||
@ -80,27 +99,34 @@ const getColorList = (active: Filter, set: (i: Filter) => void): Link[] => [
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...baseList.map((i) => ({
|
||||
id: i,
|
||||
title: i.charAt(0).toUpperCase() + i.slice(1),
|
||||
onClick: () => {
|
||||
set(i);
|
||||
},
|
||||
right: <ColorIcon filter={i} />,
|
||||
active: active === i,
|
||||
})),
|
||||
...baseList.map((i) => {
|
||||
const description = i.percentage !== undefined ? `${i.percentage}% of users` : undefined;
|
||||
return {
|
||||
id: i.name,
|
||||
title: (
|
||||
<Column>
|
||||
<Title>{i.name}</Title>
|
||||
{description && <Description>{description}</Description>}
|
||||
</Column>
|
||||
),
|
||||
onClick: () => {
|
||||
set(i);
|
||||
},
|
||||
right: <ColorIcon filter={i.name} />,
|
||||
active: active === i,
|
||||
};
|
||||
}),
|
||||
];
|
||||
|
||||
export const VisionSimulator: FunctionComponent = () => {
|
||||
export const VisionSimulator = () => {
|
||||
const [filter, setFilter] = useState<Filter>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
{filter && (
|
||||
<Global
|
||||
styles={{
|
||||
[`#${iframeId}`]: {
|
||||
filter: getFilter(filter),
|
||||
filter: getFilter(filter.name),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
55
addons/a11y/src/manager.test.tsx
Normal file
55
addons/a11y/src/manager.test.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import { addons } from '@storybook/addons';
|
||||
import * as api from '@storybook/api';
|
||||
import { PANEL_ID } from './constants';
|
||||
import './manager';
|
||||
|
||||
jest.mock('@storybook/api');
|
||||
jest.mock('@storybook/addons');
|
||||
const mockedApi = api as unknown as jest.Mocked<api.API>;
|
||||
mockedApi.getAddonState = jest.fn();
|
||||
const mockedAddons = addons as jest.Mocked<typeof addons>;
|
||||
const registrationImpl = mockedAddons.register.mock.calls[0][1];
|
||||
|
||||
describe('A11yManager', () => {
|
||||
it('should register the panels', () => {
|
||||
// when
|
||||
registrationImpl(mockedApi);
|
||||
|
||||
// then
|
||||
expect(mockedAddons.add.mock.calls).toHaveLength(2);
|
||||
expect(mockedAddons.add).toHaveBeenCalledWith(PANEL_ID, expect.anything());
|
||||
|
||||
const panel = mockedAddons.add.mock.calls
|
||||
.map(([_, def]) => def)
|
||||
.find(({ type }) => type === 'panel');
|
||||
const tool = mockedAddons.add.mock.calls
|
||||
.map(([_, def]) => def)
|
||||
.find(({ type }) => type === 'tool');
|
||||
expect(panel).toBeDefined();
|
||||
expect(tool).toBeDefined();
|
||||
});
|
||||
|
||||
it('should compute title with no issues', () => {
|
||||
// given
|
||||
mockedApi.getAddonState.mockImplementation(() => undefined);
|
||||
registrationImpl(api as unknown as api.API);
|
||||
const title = mockedAddons.add.mock.calls
|
||||
.map(([_, def]) => def)
|
||||
.find(({ type }) => type === 'panel').title as Function;
|
||||
|
||||
// when / then
|
||||
expect(title()).toBe('Accessibility');
|
||||
});
|
||||
|
||||
it('should compute title with issues', () => {
|
||||
// given
|
||||
mockedApi.getAddonState.mockImplementation(() => ({ violations: [{}], incomplete: [{}, {}] }));
|
||||
registrationImpl(mockedApi);
|
||||
const title = mockedAddons.add.mock.calls
|
||||
.map(([_, def]) => def)
|
||||
.find(({ type }) => type === 'panel').title as Function;
|
||||
|
||||
// when / then
|
||||
expect(title()).toBe('Accessibility (3)');
|
||||
});
|
||||
});
|
@ -3,9 +3,9 @@ import { addons, types } from '@storybook/addons';
|
||||
import { ADDON_ID, PANEL_ID, PARAM_KEY } from './constants';
|
||||
import { VisionSimulator } from './components/VisionSimulator';
|
||||
import { A11YPanel } from './components/A11YPanel';
|
||||
import { A11yContextProvider } from './components/A11yContext';
|
||||
import { A11yContextProvider, Results } from './components/A11yContext';
|
||||
|
||||
addons.register(ADDON_ID, () => {
|
||||
addons.register(ADDON_ID, (api) => {
|
||||
addons.add(PANEL_ID, {
|
||||
title: '',
|
||||
type: types.TOOL,
|
||||
@ -14,7 +14,13 @@ addons.register(ADDON_ID, () => {
|
||||
});
|
||||
|
||||
addons.add(PANEL_ID, {
|
||||
title: 'Accessibility',
|
||||
title() {
|
||||
const addonState: Results = api?.getAddonState(ADDON_ID);
|
||||
const violationsNb = addonState?.violations?.length || 0;
|
||||
const incompleteNb = addonState?.incomplete?.length || 0;
|
||||
const totalNb = violationsNb + incompleteNb;
|
||||
return totalNb !== 0 ? `Accessibility (${totalNb})` : 'Accessibility';
|
||||
},
|
||||
type: types.PANEL,
|
||||
render: ({ active = true, key }) => (
|
||||
<A11yContextProvider key={key} active={active}>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-actions",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Get UI feedback when an action is performed on an interactive element",
|
||||
"keywords": [
|
||||
"storybook",
|
||||
@ -41,13 +41,13 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/api": "6.5.0-beta.1",
|
||||
"@storybook/client-logger": "6.5.0-beta.1",
|
||||
"@storybook/components": "6.5.0-beta.1",
|
||||
"@storybook/core-events": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/api": "6.5.0-rc.1",
|
||||
"@storybook/client-logger": "6.5.0-rc.1",
|
||||
"@storybook/components": "6.5.0-rc.1",
|
||||
"@storybook/core-events": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@storybook/theming": "6.5.0-beta.1",
|
||||
"@storybook/theming": "6.5.0-rc.1",
|
||||
"core-js": "^3.8.2",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"global": "^4.4.0",
|
||||
@ -56,7 +56,7 @@
|
||||
"prop-types": "^15.7.2",
|
||||
"react-inspector": "^5.1.0",
|
||||
"regenerator-runtime": "^0.13.7",
|
||||
"telejson": "^5.3.3",
|
||||
"telejson": "^6.0.8",
|
||||
"ts-dedent": "^2.0.0",
|
||||
"util-deprecate": "^1.0.2",
|
||||
"uuid-browser": "^3.1.0"
|
||||
@ -80,7 +80,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/index.js",
|
||||
"storybook": {
|
||||
"displayName": "Actions",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-backgrounds",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Switch backgrounds to view components in different settings",
|
||||
"keywords": [
|
||||
"addon",
|
||||
@ -45,13 +45,13 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/api": "6.5.0-beta.1",
|
||||
"@storybook/client-logger": "6.5.0-beta.1",
|
||||
"@storybook/components": "6.5.0-beta.1",
|
||||
"@storybook/core-events": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/api": "6.5.0-rc.1",
|
||||
"@storybook/client-logger": "6.5.0-rc.1",
|
||||
"@storybook/components": "6.5.0-rc.1",
|
||||
"@storybook/core-events": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@storybook/theming": "6.5.0-beta.1",
|
||||
"@storybook/theming": "6.5.0-rc.1",
|
||||
"core-js": "^3.8.2",
|
||||
"global": "^4.4.0",
|
||||
"memoizerific": "^1.11.3",
|
||||
@ -77,7 +77,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/index.js",
|
||||
"storybook": {
|
||||
"displayName": "Backgrounds",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-controls",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Interact with component inputs dynamically in the Storybook UI",
|
||||
"keywords": [
|
||||
"addon",
|
||||
@ -45,15 +45,15 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/api": "6.5.0-beta.1",
|
||||
"@storybook/client-logger": "6.5.0-beta.1",
|
||||
"@storybook/components": "6.5.0-beta.1",
|
||||
"@storybook/core-common": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/api": "6.5.0-rc.1",
|
||||
"@storybook/client-logger": "6.5.0-rc.1",
|
||||
"@storybook/components": "6.5.0-rc.1",
|
||||
"@storybook/core-common": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@storybook/node-logger": "6.5.0-beta.1",
|
||||
"@storybook/store": "6.5.0-beta.1",
|
||||
"@storybook/theming": "6.5.0-beta.1",
|
||||
"@storybook/node-logger": "6.5.0-rc.1",
|
||||
"@storybook/store": "6.5.0-rc.1",
|
||||
"@storybook/theming": "6.5.0-rc.1",
|
||||
"core-js": "^3.8.2",
|
||||
"lodash": "^4.17.21",
|
||||
"ts-dedent": "^2.0.0"
|
||||
@ -73,7 +73,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/manager.js",
|
||||
"storybook": {
|
||||
"displayName": "Controls",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-docs",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Document component usage and properties in Markdown",
|
||||
"keywords": [
|
||||
"addon",
|
||||
@ -59,20 +59,20 @@
|
||||
"@babel/preset-env": "^7.12.11",
|
||||
"@jest/transform": "^26.6.2",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/api": "6.5.0-beta.1",
|
||||
"@storybook/components": "6.5.0-beta.1",
|
||||
"@storybook/core-common": "6.5.0-beta.1",
|
||||
"@storybook/core-events": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/api": "6.5.0-rc.1",
|
||||
"@storybook/components": "6.5.0-rc.1",
|
||||
"@storybook/core-common": "6.5.0-rc.1",
|
||||
"@storybook/core-events": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@storybook/docs-tools": "6.5.0-beta.1",
|
||||
"@storybook/mdx1-csf": "canary",
|
||||
"@storybook/node-logger": "6.5.0-beta.1",
|
||||
"@storybook/postinstall": "6.5.0-beta.1",
|
||||
"@storybook/preview-web": "6.5.0-beta.1",
|
||||
"@storybook/source-loader": "6.5.0-beta.1",
|
||||
"@storybook/store": "6.5.0-beta.1",
|
||||
"@storybook/theming": "6.5.0-beta.1",
|
||||
"@storybook/docs-tools": "6.5.0-rc.1",
|
||||
"@storybook/mdx1-csf": "^0.0.1",
|
||||
"@storybook/node-logger": "6.5.0-rc.1",
|
||||
"@storybook/postinstall": "6.5.0-rc.1",
|
||||
"@storybook/preview-web": "6.5.0-rc.1",
|
||||
"@storybook/source-loader": "6.5.0-rc.1",
|
||||
"@storybook/store": "6.5.0-rc.1",
|
||||
"@storybook/theming": "6.5.0-rc.1",
|
||||
"babel-loader": "^8.0.0",
|
||||
"core-js": "^3.8.2",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
@ -86,11 +86,11 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.10",
|
||||
"@storybook/mdx2-csf": "canary",
|
||||
"@storybook/mdx2-csf": "^0.0.3",
|
||||
"@types/util-deprecate": "^1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@storybook/mdx2-csf": "*",
|
||||
"@storybook/mdx2-csf": "^0.0.3",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
@ -108,7 +108,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/index.js",
|
||||
"storybook": {
|
||||
"displayName": "Docs",
|
||||
|
@ -11,6 +11,7 @@ import { DocsContext, DocsContextProps } from './DocsContext';
|
||||
import { SourceContext, SourceContextProps } from './SourceContainer';
|
||||
import { getSourceProps, SourceState } from './Source';
|
||||
import { useStories } from './useStory';
|
||||
import { CURRENT_SELECTION } from './types';
|
||||
|
||||
export { SourceState };
|
||||
|
||||
@ -53,7 +54,10 @@ const getPreviewProps = (
|
||||
);
|
||||
const sourceProps = getSourceProps({ ids: targetIds }, docsContext, sourceContext);
|
||||
if (!sourceState) sourceState = sourceProps.state;
|
||||
const stories = useStories(targetIds, docsContext);
|
||||
const storyIds = targetIds.map((targetId) =>
|
||||
targetId === CURRENT_SELECTION ? docsContext.id : targetId
|
||||
);
|
||||
const stories = useStories(storyIds, docsContext);
|
||||
isLoading = stories.some((s) => !s);
|
||||
|
||||
return {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-essentials",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Curated addons to bring out the best of Storybook",
|
||||
"keywords": [
|
||||
"addon",
|
||||
@ -39,25 +39,25 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addon-actions": "6.5.0-beta.1",
|
||||
"@storybook/addon-backgrounds": "6.5.0-beta.1",
|
||||
"@storybook/addon-controls": "6.5.0-beta.1",
|
||||
"@storybook/addon-docs": "6.5.0-beta.1",
|
||||
"@storybook/addon-measure": "6.5.0-beta.1",
|
||||
"@storybook/addon-outline": "6.5.0-beta.1",
|
||||
"@storybook/addon-toolbars": "6.5.0-beta.1",
|
||||
"@storybook/addon-viewport": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/api": "6.5.0-beta.1",
|
||||
"@storybook/core-common": "6.5.0-beta.1",
|
||||
"@storybook/node-logger": "6.5.0-beta.1",
|
||||
"@storybook/addon-actions": "6.5.0-rc.1",
|
||||
"@storybook/addon-backgrounds": "6.5.0-rc.1",
|
||||
"@storybook/addon-controls": "6.5.0-rc.1",
|
||||
"@storybook/addon-docs": "6.5.0-rc.1",
|
||||
"@storybook/addon-measure": "6.5.0-rc.1",
|
||||
"@storybook/addon-outline": "6.5.0-rc.1",
|
||||
"@storybook/addon-toolbars": "6.5.0-rc.1",
|
||||
"@storybook/addon-viewport": "6.5.0-rc.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/api": "6.5.0-rc.1",
|
||||
"@storybook/core-common": "6.5.0-rc.1",
|
||||
"@storybook/node-logger": "6.5.0-rc.1",
|
||||
"core-js": "^3.8.2",
|
||||
"regenerator-runtime": "^0.13.7",
|
||||
"ts-dedent": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.10",
|
||||
"@storybook/vue": "6.5.0-beta.1",
|
||||
"@storybook/vue": "6.5.0-rc.1",
|
||||
"@types/jest": "^26.0.16",
|
||||
"@types/webpack-env": "^1.16.0"
|
||||
},
|
||||
@ -120,6 +120,6 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/index.js"
|
||||
}
|
||||
|
@ -41,22 +41,25 @@ Interactions relies on "instrumented" versions of Jest and Testing Library, that
|
||||
`@storybook/testing-library` instead of their original package. You can then use these libraries in your `play` function.
|
||||
|
||||
```js
|
||||
import { Button } from './Button';
|
||||
import { expect } from '@storybook/jest';
|
||||
import { within, userEvent } from '@storybook/testing-library';
|
||||
|
||||
export default {
|
||||
title: 'Button',
|
||||
component: Button,
|
||||
argTypes: {
|
||||
onClick: { action: true },
|
||||
},
|
||||
};
|
||||
|
||||
export const Demo = {
|
||||
play: async ({ args, canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(canvas.getByRole('button'));
|
||||
await expect(args.onClick).toHaveBeenCalled();
|
||||
},
|
||||
const Template = (args) => <Button {...args} />;
|
||||
|
||||
export const Demo = Template.bind({});
|
||||
Demo.play = async ({ args, canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(canvas.getByRole('button'));
|
||||
await expect(args.onClick).toHaveBeenCalled();
|
||||
};
|
||||
```
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-interactions",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Automate, test and debug user interactions",
|
||||
"keywords": [
|
||||
"storybook-addons",
|
||||
@ -42,15 +42,15 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@devtools-ds/object-inspector": "^1.1.2",
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/api": "6.5.0-beta.1",
|
||||
"@storybook/client-logger": "6.5.0-beta.1",
|
||||
"@storybook/components": "6.5.0-beta.1",
|
||||
"@storybook/core-common": "6.5.0-beta.1",
|
||||
"@storybook/core-events": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/api": "6.5.0-rc.1",
|
||||
"@storybook/client-logger": "6.5.0-rc.1",
|
||||
"@storybook/components": "6.5.0-rc.1",
|
||||
"@storybook/core-common": "6.5.0-rc.1",
|
||||
"@storybook/core-events": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@storybook/instrumenter": "6.5.0-beta.1",
|
||||
"@storybook/theming": "6.5.0-beta.1",
|
||||
"@storybook/instrumenter": "6.5.0-rc.1",
|
||||
"@storybook/theming": "6.5.0-rc.1",
|
||||
"core-js": "^3.8.2",
|
||||
"global": "^4.4.0",
|
||||
"jest-mock": "^27.0.6",
|
||||
@ -59,7 +59,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/jest": "^0.0.5",
|
||||
"@storybook/testing-library": "^0.0.7",
|
||||
"@storybook/testing-library": "0.0.14-next.0",
|
||||
"formik": "^2.2.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@ -77,7 +77,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/index.js",
|
||||
"storybook": {
|
||||
"displayName": "Interactions",
|
||||
|
@ -4,7 +4,7 @@ import { ComponentStoryObj, ComponentMeta } from '@storybook/react';
|
||||
import { CallStates } from '@storybook/instrumenter';
|
||||
import { styled } from '@storybook/theming';
|
||||
|
||||
import { getCall } from './mocks';
|
||||
import { getCalls, getInteractions } from './mocks';
|
||||
import { AddonPanelPure } from './Panel';
|
||||
import SubnavStories from './components/Subnav/Subnav.stories';
|
||||
|
||||
@ -20,6 +20,8 @@ const StyledWrapper = styled.div(({ theme }) => ({
|
||||
overflow: 'auto',
|
||||
}));
|
||||
|
||||
const interactions = getInteractions(CallStates.DONE);
|
||||
|
||||
export default {
|
||||
title: 'Addons/Interactions/Panel',
|
||||
component: AddonPanelPure,
|
||||
@ -34,10 +36,10 @@ export default {
|
||||
layout: 'fullscreen',
|
||||
},
|
||||
args: {
|
||||
calls: new Map(),
|
||||
calls: new Map(getCalls(CallStates.DONE).map((call) => [call.id, call])),
|
||||
controls: SubnavStories.args.controls,
|
||||
controlStates: SubnavStories.args.controlStates,
|
||||
interactions: [getCall(CallStates.DONE)],
|
||||
interactions,
|
||||
fileName: 'addon-interactions.stories.tsx',
|
||||
hasException: false,
|
||||
isPlaying: false,
|
||||
@ -52,14 +54,14 @@ type Story = ComponentStoryObj<typeof AddonPanelPure>;
|
||||
|
||||
export const Passing: Story = {
|
||||
args: {
|
||||
interactions: [getCall(CallStates.DONE)],
|
||||
interactions: getInteractions(CallStates.DONE),
|
||||
},
|
||||
};
|
||||
|
||||
export const Paused: Story = {
|
||||
args: {
|
||||
isPlaying: true,
|
||||
interactions: [getCall(CallStates.WAITING)],
|
||||
interactions: getInteractions(CallStates.WAITING),
|
||||
controlStates: {
|
||||
debugger: true,
|
||||
start: false,
|
||||
@ -68,20 +70,21 @@ export const Paused: Story = {
|
||||
next: true,
|
||||
end: true,
|
||||
},
|
||||
pausedAt: interactions[interactions.length - 1].id,
|
||||
},
|
||||
};
|
||||
|
||||
export const Playing: Story = {
|
||||
args: {
|
||||
isPlaying: true,
|
||||
interactions: [getCall(CallStates.ACTIVE)],
|
||||
interactions: getInteractions(CallStates.ACTIVE),
|
||||
},
|
||||
};
|
||||
|
||||
export const Failed: Story = {
|
||||
args: {
|
||||
hasException: true,
|
||||
interactions: [getCall(CallStates.ERROR)],
|
||||
interactions: getInteractions(CallStates.ERROR),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -28,10 +28,16 @@ interface InteractionsPanelProps {
|
||||
active: boolean;
|
||||
controls: Controls;
|
||||
controlStates: ControlStates;
|
||||
interactions: (Call & { status?: CallStates })[];
|
||||
interactions: (Call & {
|
||||
status?: CallStates;
|
||||
childCallIds: Call['id'][];
|
||||
isCollapsed: boolean;
|
||||
toggleCollapsed: () => void;
|
||||
})[];
|
||||
fileName?: string;
|
||||
hasException?: boolean;
|
||||
isPlaying?: boolean;
|
||||
pausedAt?: Call['id'];
|
||||
calls: Map<string, any>;
|
||||
endRef?: React.Ref<HTMLDivElement>;
|
||||
onScrollToEnd?: () => void;
|
||||
@ -66,6 +72,7 @@ export const AddonPanelPure: React.FC<InteractionsPanelProps> = React.memo(
|
||||
fileName,
|
||||
hasException,
|
||||
isPlaying,
|
||||
pausedAt,
|
||||
onScrollToEnd,
|
||||
endRef,
|
||||
isRerunAnimating,
|
||||
@ -87,15 +94,21 @@ export const AddonPanelPure: React.FC<InteractionsPanelProps> = React.memo(
|
||||
setIsRerunAnimating={setIsRerunAnimating}
|
||||
/>
|
||||
)}
|
||||
{interactions.map((call) => (
|
||||
<Interaction
|
||||
key={call.id}
|
||||
call={call}
|
||||
callsById={calls}
|
||||
controls={controls}
|
||||
controlStates={controlStates}
|
||||
/>
|
||||
))}
|
||||
<div>
|
||||
{interactions.map((call) => (
|
||||
<Interaction
|
||||
key={call.id}
|
||||
call={call}
|
||||
callsById={calls}
|
||||
controls={controls}
|
||||
controlStates={controlStates}
|
||||
childCallIds={call.childCallIds}
|
||||
isCollapsed={call.isCollapsed}
|
||||
toggleCollapsed={call.toggleCollapsed}
|
||||
pausedAt={pausedAt}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div ref={endRef} />
|
||||
{!isPlaying && interactions.length === 0 && (
|
||||
<Placeholder>
|
||||
@ -116,17 +129,17 @@ export const AddonPanelPure: React.FC<InteractionsPanelProps> = React.memo(
|
||||
export const Panel: React.FC<AddonPanelProps> = (props) => {
|
||||
const [storyId, setStoryId] = React.useState<StoryId>();
|
||||
const [controlStates, setControlStates] = React.useState<ControlStates>(INITIAL_CONTROL_STATES);
|
||||
const [pausedAt, setPausedAt] = React.useState<Call['id']>();
|
||||
const [isPlaying, setPlaying] = React.useState(false);
|
||||
const [isRerunAnimating, setIsRerunAnimating] = React.useState(false);
|
||||
const [scrollTarget, setScrollTarget] = React.useState<HTMLElement>();
|
||||
const [collapsed, setCollapsed] = React.useState<Set<Call['id']>>(new Set());
|
||||
const [log, setLog] = React.useState<LogItem[]>([]);
|
||||
|
||||
// Calls are tracked in a ref so we don't needlessly rerender.
|
||||
const calls = React.useRef<Map<Call['id'], Omit<Call, 'status'>>>(new Map());
|
||||
const setCall = ({ status, ...call }: Call) => calls.current.set(call.id, call);
|
||||
|
||||
const [log, setLog] = React.useState<LogItem[]>([]);
|
||||
const interactions = log.map(({ callId, status }) => ({ ...calls.current.get(callId), status }));
|
||||
|
||||
const endRef = React.useRef();
|
||||
React.useEffect(() => {
|
||||
let observer: IntersectionObserver;
|
||||
@ -146,10 +159,12 @@ export const Panel: React.FC<AddonPanelProps> = (props) => {
|
||||
[EVENTS.SYNC]: (payload) => {
|
||||
setControlStates(payload.controlStates);
|
||||
setLog(payload.logItems);
|
||||
setPausedAt(payload.pausedAt);
|
||||
},
|
||||
[STORY_RENDER_PHASE_CHANGED]: (event) => {
|
||||
setStoryId(event.storyId);
|
||||
setPlaying(event.newPhase === 'playing');
|
||||
setPausedAt(undefined);
|
||||
},
|
||||
},
|
||||
[]
|
||||
@ -177,6 +192,38 @@ export const Panel: React.FC<AddonPanelProps> = (props) => {
|
||||
const showStatus = log.length > 0 && !isPlaying;
|
||||
const hasException = log.some((item) => item.status === CallStates.ERROR);
|
||||
|
||||
const interactions = React.useMemo(() => {
|
||||
const callsById = new Map<Call['id'], Call>();
|
||||
const childCallMap = new Map<Call['id'], Call['id'][]>();
|
||||
return log
|
||||
.filter(({ callId, parentId }) => {
|
||||
if (!parentId) return true;
|
||||
childCallMap.set(parentId, (childCallMap.get(parentId) || []).concat(callId));
|
||||
return !collapsed.has(parentId);
|
||||
})
|
||||
.map(({ callId, status }) => ({ ...calls.current.get(callId), status } as Call))
|
||||
.map((call) => {
|
||||
const status =
|
||||
call.status === CallStates.ERROR &&
|
||||
callsById.get(call.parentId)?.status === CallStates.ACTIVE
|
||||
? CallStates.ACTIVE
|
||||
: call.status;
|
||||
callsById.set(call.id, { ...call, status });
|
||||
return {
|
||||
...call,
|
||||
status,
|
||||
childCallIds: childCallMap.get(call.id),
|
||||
isCollapsed: collapsed.has(call.id),
|
||||
toggleCollapsed: () =>
|
||||
setCollapsed((ids) => {
|
||||
if (ids.has(call.id)) ids.delete(call.id);
|
||||
else ids.add(call.id);
|
||||
return new Set(ids);
|
||||
}),
|
||||
};
|
||||
});
|
||||
}, [log, collapsed]);
|
||||
|
||||
return (
|
||||
<React.Fragment key="interactions">
|
||||
<TabStatus>
|
||||
@ -191,6 +238,7 @@ export const Panel: React.FC<AddonPanelProps> = (props) => {
|
||||
fileName={fileName}
|
||||
hasException={hasException}
|
||||
isPlaying={isPlaying}
|
||||
pausedAt={pausedAt}
|
||||
endRef={endRef}
|
||||
onScrollToEnd={scrollTarget && scrollToTarget}
|
||||
isRerunAnimating={isRerunAnimating}
|
||||
|
@ -35,6 +35,12 @@ export const Demo: CSF2Story = (args) => (
|
||||
Demo.play = async ({ args, canvasElement }) => {
|
||||
await userEvent.click(within(canvasElement).getByRole('button'));
|
||||
await expect(args.onSubmit).toHaveBeenCalledWith(expect.stringMatching(/([A-Z])\w+/gi));
|
||||
await expect([{ name: 'John', age: 42 }]).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ name: 'John' }),
|
||||
expect.objectContaining({ age: 42 }),
|
||||
])
|
||||
);
|
||||
};
|
||||
|
||||
export const FindBy: CSF2Story = (args) => {
|
||||
@ -131,7 +137,7 @@ export const StandardEmailFailed: CSF3Story = {
|
||||
await userEvent.click(canvas.getByRole('button', { name: /create account/i }));
|
||||
|
||||
await canvas.findByText('Please enter a correctly formatted email address');
|
||||
expect(args.onSubmit).not.toHaveBeenCalled();
|
||||
await expect(args.onSubmit).not.toHaveBeenCalled();
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { ComponentStoryObj, ComponentMeta } from '@storybook/react';
|
||||
import { expect } from '@storybook/jest';
|
||||
import { CallStates } from '@storybook/instrumenter';
|
||||
import { userEvent, within } from '@storybook/testing-library';
|
||||
import { getCall } from '../../mocks';
|
||||
import { getCalls } from '../../mocks';
|
||||
|
||||
import { Interaction } from './Interaction';
|
||||
import SubnavStories from '../Subnav/Subnav.stories';
|
||||
@ -13,7 +13,7 @@ export default {
|
||||
title: 'Addons/Interactions/Interaction',
|
||||
component: Interaction,
|
||||
args: {
|
||||
callsById: new Map(),
|
||||
callsById: new Map(getCalls(CallStates.DONE).map((call) => [call.id, call])),
|
||||
controls: SubnavStories.args.controls,
|
||||
controlStates: SubnavStories.args.controlStates,
|
||||
},
|
||||
@ -21,25 +21,31 @@ export default {
|
||||
|
||||
export const Active: Story = {
|
||||
args: {
|
||||
call: getCall(CallStates.ACTIVE),
|
||||
call: getCalls(CallStates.ACTIVE).slice(-1)[0],
|
||||
},
|
||||
};
|
||||
|
||||
export const Waiting: Story = {
|
||||
args: {
|
||||
call: getCall(CallStates.WAITING),
|
||||
call: getCalls(CallStates.WAITING).slice(-1)[0],
|
||||
},
|
||||
};
|
||||
|
||||
export const Failed: Story = {
|
||||
args: {
|
||||
call: getCall(CallStates.ERROR),
|
||||
call: getCalls(CallStates.ERROR).slice(-1)[0],
|
||||
},
|
||||
};
|
||||
|
||||
export const Done: Story = {
|
||||
args: {
|
||||
call: getCall(CallStates.DONE),
|
||||
call: getCalls(CallStates.DONE).slice(-1)[0],
|
||||
},
|
||||
};
|
||||
|
||||
export const WithParent: Story = {
|
||||
args: {
|
||||
call: { ...getCalls(CallStates.DONE).slice(-1)[0], parentId: 'parent-id' },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { IconButton, Icons, TooltipNote, WithTooltip } from '@storybook/components';
|
||||
import { Call, CallStates, ControlStates } from '@storybook/instrumenter';
|
||||
import { styled, typography } from '@storybook/theming';
|
||||
import { transparentize } from 'polished';
|
||||
@ -15,23 +16,55 @@ const MethodCallWrapper = styled.div(() => ({
|
||||
inlineSize: 'calc( 100% - 40px )',
|
||||
}));
|
||||
|
||||
const RowContainer = styled('div', { shouldForwardProp: (prop) => !['call'].includes(prop) })<{
|
||||
call: Call;
|
||||
}>(({ theme, call }) => ({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderBottom: `1px solid ${theme.appBorderColor}`,
|
||||
fontFamily: typography.fonts.base,
|
||||
fontSize: 13,
|
||||
...(call.status === CallStates.ERROR && {
|
||||
backgroundColor:
|
||||
theme.base === 'dark' ? transparentize(0.93, theme.color.negative) : theme.background.warning,
|
||||
const RowContainer = styled('div', {
|
||||
shouldForwardProp: (prop) => !['call', 'pausedAt'].includes(prop),
|
||||
})<{ call: Call; pausedAt: Call['id'] }>(
|
||||
({ theme, call }) => ({
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderBottom: `1px solid ${theme.appBorderColor}`,
|
||||
fontFamily: typography.fonts.base,
|
||||
fontSize: 13,
|
||||
...(call.status === CallStates.ERROR && {
|
||||
backgroundColor:
|
||||
theme.base === 'dark'
|
||||
? transparentize(0.93, theme.color.negative)
|
||||
: theme.background.warning,
|
||||
}),
|
||||
paddingLeft: call.parentId ? 20 : 0,
|
||||
}),
|
||||
({ theme, call, pausedAt }) =>
|
||||
pausedAt === call.id && {
|
||||
'&::before': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
top: -5,
|
||||
zIndex: 1,
|
||||
borderTop: '4.5px solid transparent',
|
||||
borderLeft: `7px solid ${theme.color.warning}`,
|
||||
borderBottom: '4.5px solid transparent',
|
||||
},
|
||||
'&::after': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
top: -1,
|
||||
zIndex: 1,
|
||||
width: '100%',
|
||||
borderTop: `1.5px solid ${theme.color.warning}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const RowHeader = styled.div<{ disabled: boolean }>(({ theme, disabled }) => ({
|
||||
display: 'flex',
|
||||
'&:hover': disabled ? {} : { background: theme.background.hoverable },
|
||||
}));
|
||||
|
||||
const RowLabel = styled('button', { shouldForwardProp: (prop) => !['call'].includes(prop) })<
|
||||
React.ButtonHTMLAttributes<HTMLButtonElement> & { call: Call }
|
||||
>(({ theme, disabled, call }) => ({
|
||||
flex: 1,
|
||||
display: 'grid',
|
||||
background: 'none',
|
||||
border: 0,
|
||||
@ -42,7 +75,6 @@ const RowLabel = styled('button', { shouldForwardProp: (prop) => !['call'].inclu
|
||||
padding: '8px 15px',
|
||||
textAlign: 'start',
|
||||
cursor: disabled || call.status === CallStates.ERROR ? 'default' : 'pointer',
|
||||
'&:hover': disabled ? {} : { background: theme.background.hoverable },
|
||||
'&:focus-visible': {
|
||||
outline: 0,
|
||||
boxShadow: `inset 3px 0 0 0 ${
|
||||
@ -55,45 +87,101 @@ const RowLabel = styled('button', { shouldForwardProp: (prop) => !['call'].inclu
|
||||
},
|
||||
}));
|
||||
|
||||
const RowMessage = styled('pre')({
|
||||
margin: 0,
|
||||
padding: '8px 10px 8px 30px',
|
||||
const RowActions = styled.div(({ theme }) => ({
|
||||
padding: 6,
|
||||
}));
|
||||
|
||||
export const StyledIconButton = styled(IconButton as any)(({ theme }) => ({
|
||||
color: theme.color.mediumdark,
|
||||
margin: '0 3px',
|
||||
}));
|
||||
|
||||
const Note = styled(TooltipNote)(({ theme }) => ({
|
||||
fontFamily: theme.typography.fonts.base,
|
||||
}));
|
||||
|
||||
const RowMessage = styled('div')(({ theme }) => ({
|
||||
padding: '8px 10px 8px 36px',
|
||||
fontSize: typography.size.s1,
|
||||
});
|
||||
pre: {
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
},
|
||||
p: {
|
||||
color: theme.color.dark,
|
||||
},
|
||||
}));
|
||||
|
||||
const Exception = ({ exception }: { exception: Call['exception'] }) => {
|
||||
if (exception.message.startsWith('expect(')) {
|
||||
return <MatcherResult {...exception} />;
|
||||
}
|
||||
const paragraphs = exception.message.split('\n\n');
|
||||
const more = paragraphs.length > 1;
|
||||
return (
|
||||
<RowMessage>
|
||||
<pre>{paragraphs[0]}</pre>
|
||||
{more && <p>See the full stack trace in the browser console.</p>}
|
||||
</RowMessage>
|
||||
);
|
||||
};
|
||||
|
||||
export const Interaction = ({
|
||||
call,
|
||||
callsById,
|
||||
controls,
|
||||
controlStates,
|
||||
childCallIds,
|
||||
isCollapsed,
|
||||
toggleCollapsed,
|
||||
pausedAt,
|
||||
}: {
|
||||
call: Call;
|
||||
callsById: Map<Call['id'], Call>;
|
||||
controls: Controls;
|
||||
controlStates: ControlStates;
|
||||
childCallIds?: Call['id'][];
|
||||
isCollapsed: boolean;
|
||||
toggleCollapsed: () => void;
|
||||
pausedAt?: Call['id'];
|
||||
}) => {
|
||||
const [isHovered, setIsHovered] = React.useState(false);
|
||||
return (
|
||||
<RowContainer call={call}>
|
||||
<RowLabel
|
||||
call={call}
|
||||
onClick={() => controls.goto(call.id)}
|
||||
disabled={!controlStates.goto}
|
||||
onMouseEnter={() => controlStates.goto && setIsHovered(true)}
|
||||
onMouseLeave={() => controlStates.goto && setIsHovered(false)}
|
||||
>
|
||||
<StatusIcon status={isHovered ? CallStates.ACTIVE : call.status} />
|
||||
<MethodCallWrapper style={{ marginLeft: 6, marginBottom: 1 }}>
|
||||
<MethodCall call={call} callsById={callsById} />
|
||||
</MethodCallWrapper>
|
||||
</RowLabel>
|
||||
{call.status === CallStates.ERROR &&
|
||||
call.exception &&
|
||||
(call.exception.message.startsWith('expect(') ? (
|
||||
<MatcherResult {...call.exception} />
|
||||
) : (
|
||||
<RowMessage>{call.exception.message}</RowMessage>
|
||||
))}
|
||||
<RowContainer call={call} pausedAt={pausedAt}>
|
||||
<RowHeader disabled={!controlStates.goto || !call.interceptable || !!call.parentId}>
|
||||
<RowLabel
|
||||
call={call}
|
||||
onClick={() => controls.goto(call.id)}
|
||||
disabled={!controlStates.goto || !call.interceptable || !!call.parentId}
|
||||
onMouseEnter={() => controlStates.goto && setIsHovered(true)}
|
||||
onMouseLeave={() => controlStates.goto && setIsHovered(false)}
|
||||
>
|
||||
<StatusIcon status={isHovered ? CallStates.ACTIVE : call.status} />
|
||||
<MethodCallWrapper style={{ marginLeft: 6, marginBottom: 1 }}>
|
||||
<MethodCall call={call} callsById={callsById} />
|
||||
</MethodCallWrapper>
|
||||
</RowLabel>
|
||||
<RowActions>
|
||||
{childCallIds?.length > 0 && (
|
||||
<WithTooltip
|
||||
hasChrome={false}
|
||||
tooltip={
|
||||
<Note
|
||||
note={`${isCollapsed ? 'Show' : 'Hide'} interactions (${childCallIds.length})`}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<StyledIconButton containsIcon onClick={toggleCollapsed}>
|
||||
<Icons icon="listunordered" />
|
||||
</StyledIconButton>
|
||||
</WithTooltip>
|
||||
)}
|
||||
</RowActions>
|
||||
</RowHeader>
|
||||
|
||||
{call.status === CallStates.ERROR && call.exception?.callId === call.id && (
|
||||
<Exception exception={call.exception} />
|
||||
)}
|
||||
</RowContainer>
|
||||
);
|
||||
};
|
||||
|
@ -12,9 +12,9 @@ const ListWrapper = styled.ul({
|
||||
const Wrapper = styled.div({
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
borderBottom: `1px solid ${convert(themes.normal).appBorderColor}`,
|
||||
borderBottom: `1px solid ${convert(themes.light).appBorderColor}`,
|
||||
'&:hover': {
|
||||
background: convert(themes.normal).background.hoverable,
|
||||
background: convert(themes.light).background.hoverable,
|
||||
},
|
||||
});
|
||||
|
||||
@ -22,7 +22,7 @@ const Icon = styled(Icons)<IconsProps>({
|
||||
height: 10,
|
||||
width: 10,
|
||||
minWidth: 10,
|
||||
color: convert(themes.normal).color.mediumdark,
|
||||
color: convert(themes.light).color.mediumdark,
|
||||
marginRight: 10,
|
||||
transition: 'transform 0.1s ease-in-out',
|
||||
alignSelf: 'center',
|
||||
@ -30,8 +30,8 @@ const Icon = styled(Icons)<IconsProps>({
|
||||
});
|
||||
|
||||
const HeaderBar = styled.div({
|
||||
padding: convert(themes.normal).layoutMargin,
|
||||
paddingLeft: convert(themes.normal).layoutMargin - 3,
|
||||
padding: convert(themes.light).layoutMargin,
|
||||
paddingLeft: convert(themes.light).layoutMargin - 3,
|
||||
background: 'none',
|
||||
color: 'inherit',
|
||||
textAlign: 'left',
|
||||
@ -41,13 +41,13 @@ const HeaderBar = styled.div({
|
||||
|
||||
'&:focus': {
|
||||
outline: '0 none',
|
||||
borderLeft: `3px solid ${convert(themes.normal).color.secondary}`,
|
||||
borderLeft: `3px solid ${convert(themes.light).color.secondary}`,
|
||||
},
|
||||
});
|
||||
|
||||
const Description = styled.div({
|
||||
padding: convert(themes.normal).layoutMargin,
|
||||
marginBottom: convert(themes.normal).layoutMargin,
|
||||
padding: convert(themes.light).layoutMargin,
|
||||
marginBottom: convert(themes.light).layoutMargin,
|
||||
fontStyle: 'italic',
|
||||
});
|
||||
|
||||
@ -69,7 +69,7 @@ export const ListItem: React.FC<ListItemProps> = ({ item }) => {
|
||||
<HeaderBar onClick={() => onToggle(!open)} role="button">
|
||||
<Icon
|
||||
icon="chevrondown"
|
||||
color={convert(themes.normal).appBorderColor}
|
||||
color={convert(themes.light).appBorderColor}
|
||||
style={{
|
||||
transform: `rotate(${open ? 0 : -90}deg)`,
|
||||
}}
|
||||
|
@ -50,7 +50,7 @@ export const MatcherResult = ({ message }: { message: string }) => {
|
||||
<pre
|
||||
style={{
|
||||
margin: 0,
|
||||
padding: '8px 10px 8px 30px',
|
||||
padding: '8px 10px 8px 36px',
|
||||
fontSize: typography.size.s1,
|
||||
}}
|
||||
>
|
||||
|
@ -27,7 +27,6 @@ export default {
|
||||
},
|
||||
};
|
||||
|
||||
class FooBar {}
|
||||
export const Args = () => (
|
||||
<div style={{ display: 'inline-flex', flexDirection: 'column', gap: 10 }}>
|
||||
<Node value={null} />
|
||||
@ -56,37 +55,49 @@ export const Args = () => (
|
||||
}}
|
||||
showObjectInspector
|
||||
/>
|
||||
<Node value={new FooBar()} />
|
||||
<Node value={function goFaster() {}} />
|
||||
<Node value={{ __class__: { name: 'FooBar' } }} />
|
||||
<Node value={{ __function__: { name: 'goFaster' } }} />
|
||||
<Node value={{ __element__: { localName: 'hr' } }} />
|
||||
<Node value={{ __element__: { localName: 'foo', prefix: 'x' } }} />
|
||||
<Node value={{ __element__: { localName: 'div', id: 'foo' } }} />
|
||||
<Node value={{ __element__: { localName: 'span', classNames: ['foo', 'bar'] } }} />
|
||||
<Node value={{ __element__: { localName: 'button', innerText: 'Click me' } }} />
|
||||
<Node value={new Date(Date.UTC(2012, 11, 20, 0, 0, 0))} />
|
||||
<Node value={new Date(1600000000000)} />
|
||||
<Node value={new Date(1600000000123)} />
|
||||
<Node value={new EvalError()} />
|
||||
<Node value={new SyntaxError("Can't do that")} />
|
||||
<Node value={new TypeError("Cannot read property 'foo' of undefined")} />
|
||||
<Node value={new ReferenceError('Invalid left-hand side in assignment')} />
|
||||
<Node
|
||||
value={
|
||||
new Error(
|
||||
"XMLHttpRequest cannot load https://example.com. No 'Access-Control-Allow-Origin' header is present on the requested resource."
|
||||
)
|
||||
}
|
||||
value={{ __date__: { value: new Date(Date.UTC(2012, 11, 20, 0, 0, 0)).toISOString() } }}
|
||||
/>
|
||||
<Node value={/hello/i} />
|
||||
<Node value={new RegExp(`src(.*)\\.js$`)} />
|
||||
{/* eslint-disable-next-line symbol-description */}
|
||||
<Node value={Symbol()} />
|
||||
<Node value={Symbol('Hello world')} />
|
||||
<Node value={{ __date__: { value: new Date(1600000000000).toISOString() } }} />
|
||||
<Node value={{ __date__: { value: new Date(1600000000123).toISOString() } }} />
|
||||
<Node value={{ __error__: { name: 'EvalError', message: '' } }} />
|
||||
<Node value={{ __error__: { name: 'SyntaxError', message: "Can't do that" } }} />
|
||||
<Node
|
||||
value={{
|
||||
__error__: { name: 'TypeError', message: "Cannot read property 'foo' of undefined" },
|
||||
}}
|
||||
/>
|
||||
<Node
|
||||
value={{
|
||||
__error__: { name: 'ReferenceError', message: 'Invalid left-hand side in assignment' },
|
||||
}}
|
||||
/>
|
||||
<Node
|
||||
value={{
|
||||
__error__: {
|
||||
name: 'Error',
|
||||
message:
|
||||
"XMLHttpRequest cannot load https://example.com. No 'Access-Control-Allow-Origin' header is present on the requested resource.",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Node value={{ __regexp__: { flags: 'i', source: 'hello' } }} />
|
||||
<Node value={{ __regexp__: { flags: '', source: 'src(.*)\\.js$' } }} />
|
||||
<Node value={{ __symbol__: { description: '' } }} />
|
||||
<Node value={{ __symbol__: { description: 'Hello world' } }} />
|
||||
</div>
|
||||
);
|
||||
|
||||
const calls: Call[] = [
|
||||
{
|
||||
cursor: 0,
|
||||
id: '1',
|
||||
path: ['screen'],
|
||||
method: 'getByText',
|
||||
@ -96,6 +107,7 @@ const calls: Call[] = [
|
||||
retain: false,
|
||||
},
|
||||
{
|
||||
cursor: 1,
|
||||
id: '2',
|
||||
path: ['userEvent'],
|
||||
method: 'click',
|
||||
@ -105,6 +117,7 @@ const calls: Call[] = [
|
||||
retain: false,
|
||||
},
|
||||
{
|
||||
cursor: 2,
|
||||
id: '3',
|
||||
path: [],
|
||||
method: 'expect',
|
||||
@ -114,6 +127,7 @@ const calls: Call[] = [
|
||||
retain: false,
|
||||
},
|
||||
{
|
||||
cursor: 3,
|
||||
id: '4',
|
||||
path: [{ __callId__: '3' }, 'not'],
|
||||
method: 'toBe',
|
||||
@ -123,15 +137,17 @@ const calls: Call[] = [
|
||||
retain: false,
|
||||
},
|
||||
{
|
||||
cursor: 4,
|
||||
id: '5',
|
||||
path: ['jest'],
|
||||
method: 'fn',
|
||||
storyId: 'kind--story',
|
||||
args: [function actionHandler() {}],
|
||||
args: [{ __function__: { name: 'actionHandler' } }],
|
||||
interceptable: false,
|
||||
retain: false,
|
||||
},
|
||||
{
|
||||
cursor: 5,
|
||||
id: '6',
|
||||
path: [],
|
||||
method: 'expect',
|
||||
@ -141,20 +157,28 @@ const calls: Call[] = [
|
||||
retain: false,
|
||||
},
|
||||
{
|
||||
cursor: 6,
|
||||
id: '7',
|
||||
path: ['expect'],
|
||||
method: 'stringMatching',
|
||||
storyId: 'kind--story',
|
||||
args: [/hello/i],
|
||||
args: [{ __regexp__: { flags: 'i', source: 'hello' } }],
|
||||
interceptable: false,
|
||||
retain: false,
|
||||
},
|
||||
{
|
||||
cursor: 7,
|
||||
id: '8',
|
||||
path: [{ __callId__: '6' }, 'not'],
|
||||
method: 'toHaveBeenCalledWith',
|
||||
storyId: 'kind--story',
|
||||
args: [{ __callId__: '7' }, new Error("Cannot read property 'foo' of undefined")],
|
||||
args: [
|
||||
{ __callId__: '7' },
|
||||
[
|
||||
{ __error__: { name: 'Error', message: "Cannot read property 'foo' of undefined" } },
|
||||
{ __symbol__: { description: 'Hello world' } },
|
||||
],
|
||||
],
|
||||
interceptable: false,
|
||||
retain: false,
|
||||
},
|
||||
|
@ -111,32 +111,34 @@ export const Node = ({
|
||||
return <NullNode {...props} />;
|
||||
case value === undefined:
|
||||
return <UndefinedNode {...props} />;
|
||||
case Array.isArray(value):
|
||||
return <ArrayNode {...props} value={value} callsById={callsById} />;
|
||||
case typeof value === 'string':
|
||||
return <StringNode value={value} {...props} />;
|
||||
return <StringNode {...props} value={value} />;
|
||||
case typeof value === 'number':
|
||||
return <NumberNode value={value} {...props} />;
|
||||
return <NumberNode {...props} value={value} />;
|
||||
case typeof value === 'boolean':
|
||||
return <BooleanNode value={value} {...props} />;
|
||||
case typeof value === 'function':
|
||||
return <FunctionNode value={value} {...props} />;
|
||||
case value instanceof Array:
|
||||
return <ArrayNode value={value} {...props} />;
|
||||
case value instanceof Date:
|
||||
return <DateNode value={value} {...props} />;
|
||||
case value instanceof Error:
|
||||
return <ErrorNode value={value} {...props} />;
|
||||
case value instanceof RegExp:
|
||||
return <RegExpNode value={value} {...props} />;
|
||||
return <BooleanNode {...props} value={value} />;
|
||||
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
case Object.prototype.hasOwnProperty.call(value, '__date__'):
|
||||
return <DateNode {...props} {...value.__date__} />;
|
||||
case Object.prototype.hasOwnProperty.call(value, '__error__'):
|
||||
return <ErrorNode {...props} {...value.__error__} />;
|
||||
case Object.prototype.hasOwnProperty.call(value, '__regexp__'):
|
||||
return <RegExpNode {...props} {...value.__regexp__} />;
|
||||
case Object.prototype.hasOwnProperty.call(value, '__function__'):
|
||||
return <FunctionNode {...props} {...value.__function__} />;
|
||||
case Object.prototype.hasOwnProperty.call(value, '__symbol__'):
|
||||
return <SymbolNode {...props} {...value.__symbol__} />;
|
||||
case Object.prototype.hasOwnProperty.call(value, '__element__'):
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
return <ElementNode value={value.__element__} {...props} />;
|
||||
return <ElementNode {...props} {...value.__element__} />;
|
||||
case Object.prototype.hasOwnProperty.call(value, '__class__'):
|
||||
return <ClassNode {...props} {...value.__class__} />;
|
||||
case Object.prototype.hasOwnProperty.call(value, '__callId__'):
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
return <MethodCall call={callsById.get(value.__callId__)} callsById={callsById} />;
|
||||
case typeof value === 'object' &&
|
||||
value.constructor?.name &&
|
||||
value.constructor?.name !== 'Object':
|
||||
return <ClassNode value={value} {...props} />;
|
||||
/* eslint-enable no-underscore-dangle */
|
||||
|
||||
case Object.prototype.toString.call(value) === '[object Object]':
|
||||
return <ObjectNode value={value} showInspector={showObjectInspector} {...props} />;
|
||||
default:
|
||||
@ -189,12 +191,22 @@ export const BooleanNode = ({ value, ...props }: { value: boolean }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const ArrayNode = ({ value, nested = false }: { value: any[]; nested?: boolean }) => {
|
||||
export const ArrayNode = ({
|
||||
value,
|
||||
nested = false,
|
||||
callsById,
|
||||
}: {
|
||||
value: any[];
|
||||
nested?: boolean;
|
||||
callsById?: Map<Call['id'], Call>;
|
||||
}) => {
|
||||
const colors = useThemeColors();
|
||||
if (nested) {
|
||||
return <span style={{ color: colors.base }}>[…]</span>;
|
||||
}
|
||||
const nodes = value.slice(0, 3).map((v) => <Node key={v} value={v} nested />);
|
||||
const nodes = value
|
||||
.slice(0, 3)
|
||||
.map((v) => <Node key={JSON.stringify(v)} value={v} nested callsById={callsById} />);
|
||||
const nodelist = interleave(nodes, <span>, </span>);
|
||||
if (value.length <= 3) {
|
||||
return <span style={{ color: colors.base }}>[{nodelist}]</span>;
|
||||
@ -263,18 +275,27 @@ export const ObjectNode = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const ClassNode = ({ value }: { value: Record<string, any> }) => {
|
||||
export const ClassNode = ({ name }: { name: string }) => {
|
||||
const colors = useThemeColors();
|
||||
return <span style={{ color: colors.instance }}>{value.constructor.name}</span>;
|
||||
return <span style={{ color: colors.instance }}>{name}</span>;
|
||||
};
|
||||
|
||||
export const FunctionNode = ({ value }: { value: Function }) => {
|
||||
export const FunctionNode = ({ name }: { name: string }) => {
|
||||
const colors = useThemeColors();
|
||||
return <span style={{ color: colors.function }}>{value.name || 'anonymous'}</span>;
|
||||
return name ? (
|
||||
<span style={{ color: colors.function }}>{name}</span>
|
||||
) : (
|
||||
<span style={{ color: colors.nullish, fontStyle: 'italic' }}>anonymous</span>
|
||||
);
|
||||
};
|
||||
|
||||
export const ElementNode = ({ value }: { value: ElementRef['__element__'] }) => {
|
||||
const { prefix, localName, id, classNames = [], innerText } = value;
|
||||
export const ElementNode = ({
|
||||
prefix,
|
||||
localName,
|
||||
id,
|
||||
classNames = [],
|
||||
innerText,
|
||||
}: ElementRef['__element__']) => {
|
||||
const name = prefix ? `${prefix}:${localName}` : localName;
|
||||
const colors = useThemeColors();
|
||||
return (
|
||||
@ -309,8 +330,8 @@ export const ElementNode = ({ value }: { value: ElementRef['__element__'] }) =>
|
||||
);
|
||||
};
|
||||
|
||||
export const DateNode = ({ value }: { value: Date }) => {
|
||||
const [date, time, ms] = value.toISOString().split(/[T.Z]/);
|
||||
export const DateNode = ({ value }: { value: string }) => {
|
||||
const [date, time, ms] = value.split(/[T.Z]/);
|
||||
const colors = useThemeColors();
|
||||
return (
|
||||
<span style={{ whiteSpace: 'nowrap', color: colors.date }}>
|
||||
@ -323,42 +344,36 @@ export const DateNode = ({ value }: { value: Date }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const ErrorNode = ({ value }: { value: Error }) => {
|
||||
export const ErrorNode = ({ name, message }: { name: string; message: string }) => {
|
||||
const colors = useThemeColors();
|
||||
return (
|
||||
<span style={{ color: colors.error.name }}>
|
||||
{value.name}
|
||||
{value.message && ': '}
|
||||
{value.message && (
|
||||
<span
|
||||
style={{ color: colors.error.message }}
|
||||
title={value.message.length > 50 ? value.message : ''}
|
||||
>
|
||||
{ellipsize(value.message, 50)}
|
||||
{name}
|
||||
{message && ': '}
|
||||
{message && (
|
||||
<span style={{ color: colors.error.message }} title={message.length > 50 ? message : ''}>
|
||||
{ellipsize(message, 50)}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export const RegExpNode = ({ value }: { value: RegExp }) => {
|
||||
export const RegExpNode = ({ flags, source }: { flags: string; source: string }) => {
|
||||
const colors = useThemeColors();
|
||||
return (
|
||||
<span style={{ whiteSpace: 'nowrap', color: colors.regex.flags }}>
|
||||
/<span style={{ color: colors.regex.source }}>{value.source}</span>/{value.flags}
|
||||
/<span style={{ color: colors.regex.source }}>{source}</span>/{flags}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export const SymbolNode = ({ value }: { value: symbol }) => {
|
||||
export const SymbolNode = ({ description }: { description: string }) => {
|
||||
const colors = useThemeColors();
|
||||
return (
|
||||
<span style={{ whiteSpace: 'nowrap', color: colors.instance }}>
|
||||
Symbol(
|
||||
{value.description && (
|
||||
<span style={{ color: colors.meta }}>{JSON.stringify(value.description)}</span>
|
||||
)}
|
||||
)
|
||||
{description && <span style={{ color: colors.meta }}>"{description}"</span>})
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
@ -12,6 +12,7 @@ export default {
|
||||
goto: action('goto'),
|
||||
next: action('next'),
|
||||
end: action('end'),
|
||||
rerun: action('rerun'),
|
||||
},
|
||||
controlStates: {
|
||||
debugger: true,
|
||||
|
@ -1,31 +1,122 @@
|
||||
import { CallStates, Call } from '@storybook/instrumenter';
|
||||
|
||||
export const getCall = (status: CallStates): Call => {
|
||||
const defaultData = {
|
||||
id: 'addons-interactions-accountform--standard-email-filled [3] change',
|
||||
cursor: 0,
|
||||
path: ['fireEvent'],
|
||||
method: 'change',
|
||||
storyId: 'addons-interactions-accountform--standard-email-filled',
|
||||
args: [
|
||||
{
|
||||
__callId__: 'addons-interactions-accountform--standard-email-filled [2] getByTestId',
|
||||
retain: false,
|
||||
},
|
||||
{
|
||||
target: {
|
||||
value: 'michael@chromatic.com',
|
||||
},
|
||||
},
|
||||
],
|
||||
interceptable: true,
|
||||
retain: false,
|
||||
status,
|
||||
};
|
||||
export const getCalls = (finalStatus: CallStates) => {
|
||||
const calls: Call[] = [
|
||||
{
|
||||
id: 'story--id [3] within',
|
||||
storyId: 'story--id',
|
||||
cursor: 3,
|
||||
path: [],
|
||||
method: 'within',
|
||||
args: [{ __element__: { localName: 'div', id: 'root' } }],
|
||||
interceptable: false,
|
||||
retain: false,
|
||||
status: CallStates.DONE,
|
||||
},
|
||||
{
|
||||
id: 'story--id [4] findByText',
|
||||
storyId: 'story--id',
|
||||
cursor: 4,
|
||||
path: [{ __callId__: 'story--id [3] within' }],
|
||||
method: 'findByText',
|
||||
args: ['Click'],
|
||||
interceptable: true,
|
||||
retain: false,
|
||||
status: CallStates.DONE,
|
||||
},
|
||||
{
|
||||
id: 'story--id [5] click',
|
||||
storyId: 'story--id',
|
||||
cursor: 5,
|
||||
path: ['userEvent'],
|
||||
method: 'click',
|
||||
args: [{ __element__: { localName: 'button', innerText: 'Click' } }],
|
||||
interceptable: true,
|
||||
retain: false,
|
||||
status: CallStates.DONE,
|
||||
},
|
||||
{
|
||||
id: 'story--id [6] waitFor',
|
||||
storyId: 'story--id',
|
||||
cursor: 6,
|
||||
path: [],
|
||||
method: 'waitFor',
|
||||
args: [{ __function__: { name: '' } }],
|
||||
interceptable: true,
|
||||
retain: false,
|
||||
status: CallStates.DONE,
|
||||
},
|
||||
{
|
||||
id: 'story--id [6] waitFor [0] expect',
|
||||
parentId: 'story--id [6] waitFor',
|
||||
storyId: 'story--id',
|
||||
cursor: 1,
|
||||
path: [],
|
||||
method: 'expect',
|
||||
args: [{ __function__: { name: 'handleSubmit' } }],
|
||||
interceptable: false,
|
||||
retain: false,
|
||||
status: CallStates.DONE,
|
||||
},
|
||||
{
|
||||
id: 'story--id [6] waitFor [1] stringMatching',
|
||||
parentId: 'story--id [6] waitFor',
|
||||
storyId: 'story--id',
|
||||
cursor: 2,
|
||||
path: ['expect'],
|
||||
method: 'stringMatching',
|
||||
args: [{ __regexp__: { flags: 'gi', source: '([A-Z])w+' } }],
|
||||
interceptable: false,
|
||||
retain: false,
|
||||
status: CallStates.DONE,
|
||||
},
|
||||
{
|
||||
id: 'story--id [6] waitFor [2] toHaveBeenCalledWith',
|
||||
parentId: 'story--id [6] waitFor',
|
||||
storyId: 'story--id',
|
||||
cursor: 3,
|
||||
path: [{ __callId__: 'story--id [6] waitFor [0] expect' }],
|
||||
method: 'toHaveBeenCalledWith',
|
||||
args: [{ __callId__: 'story--id [6] waitFor [1] stringMatching', retain: false }],
|
||||
interceptable: true,
|
||||
retain: false,
|
||||
status: CallStates.DONE,
|
||||
},
|
||||
{
|
||||
id: 'story--id [7] expect',
|
||||
storyId: 'story--id',
|
||||
cursor: 7,
|
||||
path: [],
|
||||
method: 'expect',
|
||||
args: [{ __function__: { name: 'handleReset' } }],
|
||||
interceptable: false,
|
||||
retain: false,
|
||||
status: CallStates.DONE,
|
||||
},
|
||||
{
|
||||
id: 'story--id [8] toHaveBeenCalled',
|
||||
storyId: 'story--id',
|
||||
cursor: 8,
|
||||
path: [{ __callId__: 'story--id [7] expect' }, 'not'],
|
||||
method: 'toHaveBeenCalled',
|
||||
args: [],
|
||||
interceptable: true,
|
||||
retain: false,
|
||||
status: finalStatus,
|
||||
},
|
||||
];
|
||||
|
||||
const overrides = CallStates.ERROR
|
||||
? { exception: { name: 'Error', stack: '', message: "Things didn't work!" } }
|
||||
: {};
|
||||
if (finalStatus === CallStates.ERROR) {
|
||||
calls[calls.length - 1].exception = {
|
||||
name: 'Error',
|
||||
stack: '',
|
||||
message: 'Oops!',
|
||||
callId: calls[calls.length - 1].id,
|
||||
};
|
||||
}
|
||||
|
||||
return { ...defaultData, ...overrides };
|
||||
return calls;
|
||||
};
|
||||
|
||||
export const getInteractions = (finalStatus: CallStates) =>
|
||||
getCalls(finalStatus).filter((call) => call.interceptable);
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-jest",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "React storybook addon that show component jest report",
|
||||
"keywords": [
|
||||
"addon",
|
||||
@ -47,12 +47,12 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/api": "6.5.0-beta.1",
|
||||
"@storybook/client-logger": "6.5.0-beta.1",
|
||||
"@storybook/components": "6.5.0-beta.1",
|
||||
"@storybook/core-events": "6.5.0-beta.1",
|
||||
"@storybook/theming": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/api": "6.5.0-rc.1",
|
||||
"@storybook/client-logger": "6.5.0-rc.1",
|
||||
"@storybook/components": "6.5.0-rc.1",
|
||||
"@storybook/core-events": "6.5.0-rc.1",
|
||||
"@storybook/theming": "6.5.0-rc.1",
|
||||
"core-js": "^3.8.2",
|
||||
"global": "^4.4.0",
|
||||
"react-sizeme": "^3.0.1",
|
||||
@ -77,7 +77,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/index.js",
|
||||
"storybook": {
|
||||
"displayName": "Jest",
|
||||
|
@ -99,13 +99,13 @@ const getColorByType = (type: string) => {
|
||||
// using switch to allow for new types to be added
|
||||
switch (type) {
|
||||
case StatusTypes.PASSED_TYPE:
|
||||
return convert(themes.normal).color.positive;
|
||||
return convert(themes.light).color.positive;
|
||||
case StatusTypes.FAILED_TYPE:
|
||||
return convert(themes.normal).color.negative;
|
||||
return convert(themes.light).color.negative;
|
||||
case StatusTypes.PENDING_TYPE:
|
||||
return convert(themes.normal).color.warning;
|
||||
return convert(themes.light).color.warning;
|
||||
case StatusTypes.TODO_TYPE:
|
||||
return convert(themes.normal).color.purple;
|
||||
return convert(themes.light).color.purple;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@ -154,7 +154,7 @@ const Content = styled(({ tests, className }: ContentProps) => (
|
||||
</SuiteHead>
|
||||
<TabsState
|
||||
initial="failing-tests"
|
||||
backgroundColor={convert(themes.normal).background.hoverable}
|
||||
backgroundColor={convert(themes.light).background.hoverable}
|
||||
>
|
||||
<div
|
||||
id="failing-tests"
|
||||
|
@ -67,7 +67,7 @@ export function Result(props: ResultProps) {
|
||||
<Icon
|
||||
icon="chevrondown"
|
||||
size={10}
|
||||
color={convert(themes.normal).color.mediumdark}
|
||||
color={convert(themes.light).color.mediumdark}
|
||||
style={{
|
||||
transform: `rotate(${isOpen ? 0 : -90}deg)`,
|
||||
}}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-links",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Link stories together to build demos and prototypes with your UI components",
|
||||
"keywords": [
|
||||
"addon",
|
||||
@ -41,11 +41,11 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/client-logger": "6.5.0-beta.1",
|
||||
"@storybook/core-events": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/client-logger": "6.5.0-rc.1",
|
||||
"@storybook/core-events": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@storybook/router": "6.5.0-beta.1",
|
||||
"@storybook/router": "6.5.0-rc.1",
|
||||
"@types/qs": "^6.9.5",
|
||||
"core-js": "^3.8.2",
|
||||
"global": "^4.4.0",
|
||||
@ -72,7 +72,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/index.js",
|
||||
"storybook": {
|
||||
"displayName": "Links",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-measure",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Inspect layouts by visualizing the box model",
|
||||
"keywords": [
|
||||
"storybook-addons",
|
||||
@ -44,11 +44,11 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/api": "6.5.0-beta.1",
|
||||
"@storybook/client-logger": "6.5.0-beta.1",
|
||||
"@storybook/components": "6.5.0-beta.1",
|
||||
"@storybook/core-events": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/api": "6.5.0-rc.1",
|
||||
"@storybook/client-logger": "6.5.0-rc.1",
|
||||
"@storybook/components": "6.5.0-rc.1",
|
||||
"@storybook/core-events": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"core-js": "^3.8.2",
|
||||
"global": "^4.4.0"
|
||||
@ -71,7 +71,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/index.js",
|
||||
"storybook": {
|
||||
"displayName": "Measure",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-outline",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Outline all elements with CSS to help with layout placement and alignment",
|
||||
"keywords": [
|
||||
"storybook-addons",
|
||||
@ -47,11 +47,11 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/api": "6.5.0-beta.1",
|
||||
"@storybook/client-logger": "6.5.0-beta.1",
|
||||
"@storybook/components": "6.5.0-beta.1",
|
||||
"@storybook/core-events": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/api": "6.5.0-rc.1",
|
||||
"@storybook/client-logger": "6.5.0-rc.1",
|
||||
"@storybook/components": "6.5.0-rc.1",
|
||||
"@storybook/core-events": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"core-js": "^3.8.2",
|
||||
"global": "^4.4.0",
|
||||
@ -76,7 +76,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/index.js",
|
||||
"storybook": {
|
||||
"displayName": "Outline",
|
||||
|
@ -470,7 +470,7 @@ Whenever you change your data requirements by adding (and rendering) or (acciden
|
||||
|
||||
## Using a custom directory
|
||||
|
||||
Depending on your project's needs, you can configure the `@storybook/addon-storyshots` to use a custom directory for the snapshots. You can read more about it in the [official docs](https://storybook.js.org/docs/react/workflows/snapshot-testing).
|
||||
Depending on your project's needs, you can configure the `@storybook/addon-storyshots` to use a custom directory for the snapshots. You can read more about it in the [official docs](https://storybook.js.org/docs/react/writing-tests/snapshot-testing).
|
||||
|
||||
## Options
|
||||
|
||||
@ -654,7 +654,7 @@ This option needs to be set if either:
|
||||
|
||||
### `serializer` (deprecated)
|
||||
|
||||
Pass a custom serializer (such as enzyme-to-json) to serialize components to snapshot-comparable data. The functionality of this option is completely covered by [snapshotSerializers](`snapshotSerializers`) which should be used instead.
|
||||
Pass a custom serializer (such as enzyme-to-json) to serialize components to snapshot-comparable data. The functionality of this option is completely covered by [snapshotSerializers](#snapshotserializers) which should be used instead.
|
||||
|
||||
```js
|
||||
import initStoryshots from '@storybook/addon-storyshots';
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-storyshots",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Take a code snapshot of every story automatically with Jest",
|
||||
"keywords": [
|
||||
"addon",
|
||||
@ -45,12 +45,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@jest/transform": "^26.6.2",
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/babel-plugin-require-context-hook": "1.0.1",
|
||||
"@storybook/client-api": "6.5.0-beta.1",
|
||||
"@storybook/core": "6.5.0-beta.1",
|
||||
"@storybook/core-client": "6.5.0-beta.1",
|
||||
"@storybook/core-common": "6.5.0-beta.1",
|
||||
"@storybook/client-api": "6.5.0-rc.1",
|
||||
"@storybook/core": "6.5.0-rc.1",
|
||||
"@storybook/core-client": "6.5.0-rc.1",
|
||||
"@storybook/core-common": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@types/glob": "^7.1.3",
|
||||
"@types/jest": "^26.0.16",
|
||||
@ -70,11 +70,11 @@
|
||||
"@angular/core": "^11.2.0",
|
||||
"@angular/platform-browser-dynamic": "^11.2.0",
|
||||
"@emotion/jest": "^11.8.0",
|
||||
"@storybook/addon-docs": "6.5.0-beta.1",
|
||||
"@storybook/angular": "6.5.0-beta.1",
|
||||
"@storybook/react": "6.5.0-beta.1",
|
||||
"@storybook/vue": "6.5.0-beta.1",
|
||||
"@storybook/vue3": "6.5.0-beta.1",
|
||||
"@storybook/addon-docs": "6.5.0-rc.1",
|
||||
"@storybook/angular": "6.5.0-rc.1",
|
||||
"@storybook/react": "6.5.0-rc.1",
|
||||
"@storybook/vue": "6.5.0-rc.1",
|
||||
"@storybook/vue3": "6.5.0-rc.1",
|
||||
"babel-loader": "^8.0.0",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-to-json": "^3.6.1",
|
||||
@ -151,7 +151,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"storybook": {
|
||||
"displayName": "Storyshots",
|
||||
"icon": "https://user-images.githubusercontent.com/263385/101991676-48cdf300-3c7c-11eb-8aa1-944dab6ab29b.png",
|
||||
|
@ -1,15 +1,21 @@
|
||||
import 'jest-specific-snapshot';
|
||||
import { StoryshotsTestMethod, TestMethodOptions } from './api/StoryshotsOptions';
|
||||
import {
|
||||
StoryshotsTestMethod,
|
||||
TestMethodOptions,
|
||||
StoryshotsOptions,
|
||||
} from './api/StoryshotsOptions';
|
||||
|
||||
const isFunction = (obj: any) => !!(obj && obj.constructor && obj.call && obj.apply);
|
||||
const optionsOrCallOptions = (opts: any, story: any) => (isFunction(opts) ? opts(story) : opts);
|
||||
|
||||
type SnapshotsWithOptionsArgType = Pick<StoryshotsOptions, 'renderer' | 'serializer'> | Function;
|
||||
|
||||
type SnapshotsWithOptionsReturnType = (
|
||||
options: Pick<TestMethodOptions, 'story' | 'context' | 'renderTree' | 'snapshotFileName'>
|
||||
) => any;
|
||||
|
||||
export function snapshotWithOptions(
|
||||
options: { renderer?: any; serializer?: any } | Function = {}
|
||||
options: SnapshotsWithOptionsArgType = {}
|
||||
): SnapshotsWithOptionsReturnType {
|
||||
return ({ story, context, renderTree, snapshotFileName }) => {
|
||||
const result = renderTree(story, context, optionsOrCallOptions(options, story));
|
||||
@ -44,7 +50,9 @@ export function snapshotWithOptions(
|
||||
};
|
||||
}
|
||||
|
||||
export function multiSnapshotWithOptions(options = {}): StoryshotsTestMethod {
|
||||
export function multiSnapshotWithOptions(
|
||||
options: SnapshotsWithOptionsArgType = {}
|
||||
): StoryshotsTestMethod {
|
||||
return ({ story, context, renderTree, stories2snapsConverter }) => {
|
||||
const snapshotFileName = stories2snapsConverter.getSnapshotFileName(context);
|
||||
return snapshotWithOptions(options)({ story, context, renderTree, snapshotFileName });
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-storyshots-puppeteer",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Image snapshots addition to StoryShots based on puppeteer",
|
||||
"keywords": [
|
||||
"addon",
|
||||
@ -42,7 +42,7 @@
|
||||
"dependencies": {
|
||||
"@axe-core/puppeteer": "^4.2.0",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@storybook/node-logger": "6.5.0-beta.1",
|
||||
"@storybook/node-logger": "6.5.0-rc.1",
|
||||
"@types/jest-image-snapshot": "^4.1.3",
|
||||
"core-js": "^3.8.2",
|
||||
"jest-image-snapshot": "^4.3.0",
|
||||
@ -50,11 +50,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@types/puppeteer": "^5.4.0"
|
||||
"@types/puppeteer": "^5.4.0",
|
||||
"puppeteer": "^2.0.0 || ^3.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@storybook/addon-storyshots": "6.5.0-beta.1",
|
||||
"puppeteer": "^2.0.0 || ^3.0.0"
|
||||
"@storybook/addon-storyshots": "6.5.0-rc.1",
|
||||
"puppeteer": ">=2.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"puppeteer": {
|
||||
@ -64,5 +65,5 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a"
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401"
|
||||
}
|
||||
|
@ -1,11 +1,7 @@
|
||||
import { MatchImageSnapshotOptions } from 'jest-image-snapshot';
|
||||
import {
|
||||
Base64ScreenShotOptions,
|
||||
Browser,
|
||||
DirectNavigationOptions,
|
||||
Page,
|
||||
ElementHandle,
|
||||
} from 'puppeteer';
|
||||
import { ScreenshotOptions, Browser, Page, ElementHandle } from 'puppeteer';
|
||||
|
||||
type PuppeteerLifeCycleEvent = 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';
|
||||
|
||||
export interface Context {
|
||||
kind: string;
|
||||
@ -20,6 +16,16 @@ interface Options {
|
||||
url: string;
|
||||
}
|
||||
|
||||
interface Base64ScreenShotOptions extends ScreenshotOptions {
|
||||
encoding: 'base64';
|
||||
}
|
||||
|
||||
interface DirectNavigationOptions {
|
||||
referer?: string;
|
||||
timeout?: number;
|
||||
waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
|
||||
}
|
||||
|
||||
export interface CommonConfig {
|
||||
storybookUrl: string;
|
||||
chromeExecutablePath: string;
|
||||
@ -40,7 +46,7 @@ export interface ImageSnapshotConfig extends CommonConfig {
|
||||
getMatchOptions: (options: Options) => MatchImageSnapshotOptions;
|
||||
getScreenshotOptions: (options: Options) => Base64ScreenShotOptions;
|
||||
beforeScreenshot: (page: Page, options: Options) => Promise<void | ElementHandle>;
|
||||
afterScreenshot: (options: { image: string; context: Context }) => Promise<void>;
|
||||
afterScreenshot: (options: { image: string | void | Buffer; context: Context }) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface AxeConfig extends CommonConfig {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-storysource",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "View a story’s source code to see how it works and paste into your app",
|
||||
"keywords": [
|
||||
"addon",
|
||||
@ -41,18 +41,18 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/api": "6.5.0-beta.1",
|
||||
"@storybook/client-logger": "6.5.0-beta.1",
|
||||
"@storybook/components": "6.5.0-beta.1",
|
||||
"@storybook/router": "6.5.0-beta.1",
|
||||
"@storybook/source-loader": "6.5.0-beta.1",
|
||||
"@storybook/theming": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/api": "6.5.0-rc.1",
|
||||
"@storybook/client-logger": "6.5.0-rc.1",
|
||||
"@storybook/components": "6.5.0-rc.1",
|
||||
"@storybook/router": "6.5.0-rc.1",
|
||||
"@storybook/source-loader": "6.5.0-rc.1",
|
||||
"@storybook/theming": "6.5.0-rc.1",
|
||||
"core-js": "^3.8.2",
|
||||
"estraverse": "^5.2.0",
|
||||
"loader-utils": "^2.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-syntax-highlighter": "^15.4.5",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"regenerator-runtime": "^0.13.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -74,7 +74,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/index.js",
|
||||
"storybook": {
|
||||
"displayName": "Storysource",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-toolbars",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Create your own toolbar items that control story rendering",
|
||||
"keywords": [
|
||||
"addon",
|
||||
@ -45,11 +45,11 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/api": "6.5.0-beta.1",
|
||||
"@storybook/client-logger": "6.5.0-beta.1",
|
||||
"@storybook/components": "6.5.0-beta.1",
|
||||
"@storybook/theming": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/api": "6.5.0-rc.1",
|
||||
"@storybook/client-logger": "6.5.0-rc.1",
|
||||
"@storybook/components": "6.5.0-rc.1",
|
||||
"@storybook/theming": "6.5.0-rc.1",
|
||||
"core-js": "^3.8.2",
|
||||
"regenerator-runtime": "^0.13.7"
|
||||
},
|
||||
@ -68,7 +68,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/manager.js",
|
||||
"storybook": {
|
||||
"displayName": "Toolbars",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-viewport",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Build responsive components by adjusting Storybook’s viewport size and orientation",
|
||||
"keywords": [
|
||||
"addon",
|
||||
@ -42,12 +42,12 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/api": "6.5.0-beta.1",
|
||||
"@storybook/client-logger": "6.5.0-beta.1",
|
||||
"@storybook/components": "6.5.0-beta.1",
|
||||
"@storybook/core-events": "6.5.0-beta.1",
|
||||
"@storybook/theming": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/api": "6.5.0-rc.1",
|
||||
"@storybook/client-logger": "6.5.0-rc.1",
|
||||
"@storybook/components": "6.5.0-rc.1",
|
||||
"@storybook/core-events": "6.5.0-rc.1",
|
||||
"@storybook/theming": "6.5.0-rc.1",
|
||||
"core-js": "^3.8.2",
|
||||
"global": "^4.4.0",
|
||||
"memoizerific": "^1.11.3",
|
||||
@ -69,7 +69,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/preview.js",
|
||||
"storybook": {
|
||||
"displayName": "Viewport",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/angular",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Storybook for Angular: Develop Angular Components in isolation with Hot Reloading.",
|
||||
"keywords": [
|
||||
"storybook"
|
||||
@ -45,17 +45,17 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/api": "6.5.0-beta.1",
|
||||
"@storybook/client-logger": "6.5.0-beta.1",
|
||||
"@storybook/core": "6.5.0-beta.1",
|
||||
"@storybook/core-common": "6.5.0-beta.1",
|
||||
"@storybook/core-events": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/api": "6.5.0-rc.1",
|
||||
"@storybook/client-logger": "6.5.0-rc.1",
|
||||
"@storybook/core": "6.5.0-rc.1",
|
||||
"@storybook/core-common": "6.5.0-rc.1",
|
||||
"@storybook/core-events": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@storybook/docs-tools": "6.5.0-beta.1",
|
||||
"@storybook/node-logger": "6.5.0-beta.1",
|
||||
"@storybook/docs-tools": "6.5.0-rc.1",
|
||||
"@storybook/node-logger": "6.5.0-rc.1",
|
||||
"@storybook/semver": "^7.3.2",
|
||||
"@storybook/store": "6.5.0-beta.1",
|
||||
"@storybook/store": "6.5.0-rc.1",
|
||||
"@types/node": "^14.14.20 || ^16.0.0",
|
||||
"@types/react": "^16.14.23",
|
||||
"@types/react-dom": "^16.9.14",
|
||||
@ -75,7 +75,7 @@
|
||||
"read-pkg-up": "^7.0.1",
|
||||
"regenerator-runtime": "^0.13.7",
|
||||
"sass-loader": "^10.1.0",
|
||||
"telejson": "^5.3.3",
|
||||
"telejson": "^6.0.8",
|
||||
"ts-dedent": "^2.0.0",
|
||||
"ts-loader": "^8.0.14",
|
||||
"tsconfig-paths-webpack-plugin": "^3.3.0",
|
||||
@ -137,5 +137,5 @@
|
||||
"access": "public"
|
||||
},
|
||||
"builders": "dist/ts3.9/builders/builders.json",
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a"
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401"
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import 'jest-preset-angular';
|
||||
import 'jest-preset-angular/setup-jest';
|
||||
|
||||
global.EventSource = class {} as any;
|
||||
|
@ -142,7 +142,7 @@ describe('Build Storybook Builder', () => {
|
||||
});
|
||||
|
||||
it('should throw error', async () => {
|
||||
buildStandaloneMock.mockRejectedValue(new Error());
|
||||
buildStandaloneMock.mockRejectedValue(true);
|
||||
|
||||
const run = await architect.scheduleBuilder('@storybook/angular:start-storybook', {
|
||||
browserTarget: 'angular-cli:build-2',
|
||||
|
@ -124,7 +124,7 @@ describe('Start Storybook Builder', () => {
|
||||
});
|
||||
|
||||
it('should throw error', async () => {
|
||||
buildStandaloneMock.mockRejectedValue(new Error());
|
||||
buildStandaloneMock.mockRejectedValue(true);
|
||||
|
||||
const run = await architect.scheduleBuilder('@storybook/angular:start-storybook', {
|
||||
browserTarget: 'angular-cli:build-2',
|
||||
|
@ -21,11 +21,11 @@ export const buildStandaloneErrorHandler = (error: any): any => {
|
||||
logger.line();
|
||||
return error.close
|
||||
? dedent`
|
||||
FATAL broken build!, will close the process,
|
||||
Fix the error below and restart storybook.
|
||||
`
|
||||
FATAL broken build!, will close the process,
|
||||
Fix the error below and restart storybook.
|
||||
`
|
||||
: dedent`
|
||||
Broken build, fix the error above.
|
||||
You may need to refresh the browser.
|
||||
`;
|
||||
Broken build, fix the error above.
|
||||
You may need to refresh the browser.
|
||||
`;
|
||||
};
|
||||
|
@ -22,6 +22,7 @@ describe('RendererFactory', () => {
|
||||
rootTargetDOMNode = global.document.getElementById('root');
|
||||
rootDocstargetDOMNode = global.document.getElementById('root-docs');
|
||||
(platformBrowserDynamic as any).mockImplementation(platformBrowserDynamicTesting);
|
||||
jest.spyOn(console, 'log').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -7,7 +7,7 @@ import deprecate from 'util-deprecate';
|
||||
import { ICollection, StoryFnAngularReturnType } from '../types';
|
||||
import { storyPropsProvider } from './StorybookProvider';
|
||||
import { isComponentAlreadyDeclaredInModules } from './utils/NgModulesAnalyzer';
|
||||
import { isDeclarable } from './utils/NgComponentAnalyzer';
|
||||
import { isDeclarable, isStandaloneComponent } from './utils/NgComponentAnalyzer';
|
||||
import { createStorybookWrapperComponent } from './StorybookWrapperComponent';
|
||||
import { computesTemplateFromComponent } from './ComputesTemplateFromComponent';
|
||||
|
||||
@ -61,6 +61,7 @@ export const getStorybookModuleMetadata = (
|
||||
props
|
||||
);
|
||||
|
||||
const isStandalone = isStandaloneComponent(component);
|
||||
// Look recursively (deep) if the component is not already declared by an import module
|
||||
const requiresComponentDeclaration =
|
||||
isDeclarable(component) &&
|
||||
@ -68,7 +69,8 @@ export const getStorybookModuleMetadata = (
|
||||
component,
|
||||
moduleMetadata.declarations,
|
||||
moduleMetadata.imports
|
||||
);
|
||||
) &&
|
||||
!isStandalone;
|
||||
|
||||
return {
|
||||
declarations: [
|
||||
@ -76,7 +78,11 @@ export const getStorybookModuleMetadata = (
|
||||
ComponentToInject,
|
||||
...(moduleMetadata.declarations ?? []),
|
||||
],
|
||||
imports: [BrowserModule, ...(moduleMetadata.imports ?? [])],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
...(isStandalone ? [component] : []),
|
||||
...(moduleMetadata.imports ?? []),
|
||||
],
|
||||
providers: [storyPropsProvider(storyProps$), ...(moduleMetadata.providers ?? [])],
|
||||
entryComponents: [...(moduleMetadata.entryComponents ?? [])],
|
||||
schemas: [...(moduleMetadata.schemas ?? [])],
|
||||
|
@ -19,6 +19,7 @@ import {
|
||||
isComponent,
|
||||
isDeclarable,
|
||||
getComponentDecoratorMetadata,
|
||||
isStandaloneComponent,
|
||||
} from './NgComponentAnalyzer';
|
||||
|
||||
describe('getComponentInputsOutputs', () => {
|
||||
@ -235,6 +236,46 @@ describe('isComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('isStandaloneComponent', () => {
|
||||
it('should return true with a Component with "standalone: true"', () => {
|
||||
// TODO: `standalone` is only available in Angular v14. Remove cast to `any` once
|
||||
// Angular deps are updated to v14.x.x.
|
||||
@Component({ standalone: true } as any)
|
||||
class FooComponent {}
|
||||
|
||||
expect(isStandaloneComponent(FooComponent)).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return false with a Component with "standalone: false"', () => {
|
||||
// TODO: `standalone` is only available in Angular v14. Remove cast to `any` once
|
||||
// Angular deps are updated to v14.x.x.
|
||||
@Component({ standalone: false } as any)
|
||||
class FooComponent {}
|
||||
|
||||
expect(isStandaloneComponent(FooComponent)).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return false with a Component without the "standalone" property', () => {
|
||||
@Component({})
|
||||
class FooComponent {}
|
||||
|
||||
expect(isStandaloneComponent(FooComponent)).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return false with simple class', () => {
|
||||
class FooPipe {}
|
||||
|
||||
expect(isStandaloneComponent(FooPipe)).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return false with Directive', () => {
|
||||
@Directive()
|
||||
class FooDirective {}
|
||||
|
||||
expect(isStandaloneComponent(FooDirective)).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getComponentDecoratorMetadata', () => {
|
||||
it('should return Component with a Component', () => {
|
||||
@Component({ selector: 'foo' })
|
||||
|
@ -108,6 +108,18 @@ export const isComponent = (component: any): component is Type<unknown> => {
|
||||
return (decorators || []).some((d) => d instanceof Component);
|
||||
};
|
||||
|
||||
export const isStandaloneComponent = (component: any): component is Type<unknown> => {
|
||||
if (!component) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const decorators = reflectionCapabilities.annotations(component);
|
||||
|
||||
// TODO: `standalone` is only available in Angular v14. Remove cast to `any` once
|
||||
// Angular deps are updated to v14.x.x.
|
||||
return (decorators || []).some((d) => d instanceof Component && (d as any).standalone);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns all component decorator properties
|
||||
* is used to get all `@Input` and `@Output` Decorator
|
||||
|
@ -2,6 +2,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"outDir": "../../out-tsc/lib",
|
||||
"target": "es2015",
|
||||
"declaration": true,
|
||||
|
@ -2,6 +2,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"outDir": "../../out-tsc/lib",
|
||||
"target": "es2015",
|
||||
"declaration": true,
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/ember",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Storybook for Ember: Develop Ember Component in isolation with Hot Reloading.",
|
||||
"homepage": "https://github.com/storybookjs/storybook/tree/main/app/ember",
|
||||
"bugs": {
|
||||
@ -42,10 +42,10 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/core": "6.5.0-beta.1",
|
||||
"@storybook/core-common": "6.5.0-beta.1",
|
||||
"@storybook/docs-tools": "6.5.0-beta.1",
|
||||
"@storybook/store": "6.5.0-beta.1",
|
||||
"@storybook/core": "6.5.0-rc.1",
|
||||
"@storybook/core-common": "6.5.0-rc.1",
|
||||
"@storybook/docs-tools": "6.5.0-rc.1",
|
||||
"@storybook/store": "6.5.0-rc.1",
|
||||
"core-js": "^3.8.2",
|
||||
"global": "^4.4.0",
|
||||
"react": "16.14.0",
|
||||
@ -56,9 +56,10 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "*",
|
||||
"@types/ember__component": "4.0.8",
|
||||
"babel-plugin-ember-modules-api-polyfill": "^2.12.0",
|
||||
"babel-plugin-htmlbars-inline-precompile": "2 || 3",
|
||||
"ember-source": "^3.16.0"
|
||||
"ember-source": "~3.28.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
@ -66,6 +67,6 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/client/index.js"
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import global from 'global';
|
||||
import dedent from 'ts-dedent';
|
||||
import { RenderContext } from '@storybook/store';
|
||||
import type { RenderContext } from '@storybook/store';
|
||||
// @ts-ignore
|
||||
import Component from '@ember/component'; // eslint-disable-line import/no-unresolved
|
||||
import { OptionsArgs, EmberFramework } from './types';
|
||||
|
||||
const { window: globalWindow, document } = global;
|
||||
|
||||
declare let Ember: any;
|
||||
|
||||
const rootEl = document.getElementById('root');
|
||||
|
||||
const config = globalWindow.require(`${globalWindow.STORYBOOK_NAME}/config/environment`);
|
||||
@ -38,7 +38,7 @@ function render(options: OptionsArgs, el: HTMLElement) {
|
||||
.then((instance: any) => {
|
||||
instance.register(
|
||||
'component:story-mode',
|
||||
Ember.Component.extend({
|
||||
Component.extend({
|
||||
layout: template || options,
|
||||
...context,
|
||||
})
|
||||
|
@ -19,6 +19,11 @@ npx sb init -t html
|
||||
|
||||
For more information visit: [storybook.js.org](https://storybook.js.org)
|
||||
|
||||
### Typescript
|
||||
|
||||
`npx sb init` will select `.ts` starter stories if your `package.json` has typescript as a dependency. If starting a new project,
|
||||
run `npm init` and `npm install typescript --save-dev` before initializing storybook to get the typescript starter stories.
|
||||
|
||||
---
|
||||
|
||||
Storybook also comes with a lot of [addons](https://storybook.js.org/addons) and a great API to customize as you wish.
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/html",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Storybook for HTML: View HTML snippets in isolation with Hot Reloading.",
|
||||
"keywords": [
|
||||
"storybook"
|
||||
@ -45,13 +45,13 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/core": "6.5.0-beta.1",
|
||||
"@storybook/core-common": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/core": "6.5.0-rc.1",
|
||||
"@storybook/core-common": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@storybook/docs-tools": "6.5.0-beta.1",
|
||||
"@storybook/preview-web": "6.5.0-beta.1",
|
||||
"@storybook/store": "6.5.0-beta.1",
|
||||
"@storybook/docs-tools": "6.5.0-rc.1",
|
||||
"@storybook/preview-web": "6.5.0-rc.1",
|
||||
"@storybook/store": "6.5.0-rc.1",
|
||||
"@types/node": "^14.14.20 || ^16.0.0",
|
||||
"@types/webpack-env": "^1.16.0",
|
||||
"core-js": "^3.8.2",
|
||||
@ -76,6 +76,6 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/client/index.js"
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/preact",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Storybook for Preact: Develop Preact Component in isolation.",
|
||||
"keywords": [
|
||||
"storybook"
|
||||
@ -46,11 +46,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/plugin-transform-react-jsx": "^7.12.12",
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/core": "6.5.0-beta.1",
|
||||
"@storybook/core-common": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/core": "6.5.0-rc.1",
|
||||
"@storybook/core-common": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@storybook/store": "6.5.0-beta.1",
|
||||
"@storybook/store": "6.5.0-rc.1",
|
||||
"@types/node": "^14.14.20 || ^16.0.0",
|
||||
"@types/webpack-env": "^1.16.0",
|
||||
"core-js": "^3.8.2",
|
||||
@ -76,6 +76,6 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/client/index.js"
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/react",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Storybook for React: Develop React Component in isolation with Hot Reloading.",
|
||||
"keywords": [
|
||||
"storybook"
|
||||
@ -49,16 +49,16 @@
|
||||
"@babel/preset-flow": "^7.12.1",
|
||||
"@babel/preset-react": "^7.12.10",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/client-logger": "6.5.0-beta.1",
|
||||
"@storybook/core": "6.5.0-beta.1",
|
||||
"@storybook/core-common": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/client-logger": "6.5.0-rc.1",
|
||||
"@storybook/core": "6.5.0-rc.1",
|
||||
"@storybook/core-common": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@storybook/docs-tools": "6.5.0-beta.1",
|
||||
"@storybook/node-logger": "6.5.0-beta.1",
|
||||
"@storybook/docs-tools": "6.5.0-rc.1",
|
||||
"@storybook/node-logger": "6.5.0-rc.1",
|
||||
"@storybook/react-docgen-typescript-plugin": "1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0",
|
||||
"@storybook/semver": "^7.3.2",
|
||||
"@storybook/store": "6.5.0-beta.1",
|
||||
"@storybook/store": "6.5.0-rc.1",
|
||||
"@types/estree": "^0.0.51",
|
||||
"@types/node": "^14.14.20 || ^16.0.0",
|
||||
"@types/webpack-env": "^1.16.0",
|
||||
@ -119,6 +119,6 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/client/index.js"
|
||||
}
|
||||
|
@ -284,4 +284,33 @@ describe('jsxDecorator', () => {
|
||||
'<div className="foo" />'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles stories that trigger Suspense', async () => {
|
||||
// if a story function uses a hook or other library that triggers suspense, it will throw a Promise until it is resolved
|
||||
// and then it will return the story content after the promise is resolved
|
||||
const storyFn = jest.fn();
|
||||
storyFn
|
||||
.mockImplementationOnce(() => {
|
||||
throw Promise.resolve();
|
||||
})
|
||||
.mockImplementation(() => {
|
||||
return <div>resolved args story</div>;
|
||||
});
|
||||
const jsx = '';
|
||||
const context = makeContext('args', { __isArgsStory: true, jsx }, {});
|
||||
expect(() => {
|
||||
jsxDecorator(storyFn, context);
|
||||
}).toThrow(Promise);
|
||||
jsxDecorator(storyFn, context);
|
||||
await new Promise((r) => setTimeout(r, 0));
|
||||
|
||||
expect(mockChannel.emit).toHaveBeenCalledTimes(2);
|
||||
expect(mockChannel.emit).nthCalledWith(1, SNIPPET_RENDERED, 'jsx-test--args', '');
|
||||
expect(mockChannel.emit).nthCalledWith(
|
||||
2,
|
||||
SNIPPET_RENDERED,
|
||||
'jsx-test--args',
|
||||
'<div>\n resolved args story\n</div>'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -177,7 +177,6 @@ export const jsxDecorator = (
|
||||
) => {
|
||||
const channel = addons.getChannel();
|
||||
const skip = skipJsxRender(context);
|
||||
const story = storyFn();
|
||||
|
||||
let jsx = '';
|
||||
|
||||
@ -185,6 +184,7 @@ export const jsxDecorator = (
|
||||
if (!skip) channel.emit(SNIPPET_RENDERED, (context || {}).id, jsx);
|
||||
});
|
||||
|
||||
const story = storyFn();
|
||||
// We only need to render JSX if the source block is actually going to
|
||||
// consume it. Otherwise it's just slowing us down.
|
||||
if (skip) {
|
||||
|
@ -148,7 +148,7 @@ describe('parse', () => {
|
||||
});
|
||||
|
||||
it('support array', () => {
|
||||
const result = parse("['bottom-left', 'botton-center', 'bottom-right']");
|
||||
const result = parse("['bottom-left', 'bottom-center', 'bottom-right']");
|
||||
const inferredType = result.inferredType as InspectionArray;
|
||||
|
||||
expect(inferredType.type).toBe(InspectionType.ARRAY);
|
||||
|
@ -48,7 +48,7 @@ const renderElement = async (node: ReactElement, el: Element) => {
|
||||
};
|
||||
|
||||
const canUseNewReactRootApi =
|
||||
reactDomVersion.startsWith('18') || reactDomVersion.startsWith('0.0.0');
|
||||
reactDomVersion && (reactDomVersion.startsWith('18') || reactDomVersion.startsWith('0.0.0'));
|
||||
|
||||
const shouldUseNewRootApi = FRAMEWORK_OPTIONS?.legacyRootApi !== true;
|
||||
|
||||
@ -147,4 +147,6 @@ export async function renderToDOM(
|
||||
}
|
||||
|
||||
await renderElement(element, domElement);
|
||||
|
||||
return () => unmountElement(domElement);
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ describe('framework-preset-react-docgen', () => {
|
||||
presets: ['env', 'foo-preset'],
|
||||
overrides: [
|
||||
{
|
||||
test: /\.(mjs|tsx?|jsx?)$/,
|
||||
test: /\.(cjs|mjs|tsx?|jsx?)$/,
|
||||
plugins: [
|
||||
[
|
||||
babelPluginReactDocgenPath,
|
||||
|
@ -19,8 +19,9 @@ export async function babel(config: TransformOptions, options: Options) {
|
||||
return {
|
||||
...config,
|
||||
overrides: [
|
||||
...(config?.overrides || []),
|
||||
{
|
||||
test: reactDocgen === 'react-docgen' ? /\.(mjs|tsx?|jsx?)$/ : /\.(mjs|jsx?)$/,
|
||||
test: reactDocgen === 'react-docgen' ? /\.(cjs|mjs|tsx?|jsx?)$/ : /\.(cjs|mjs|jsx?)$/,
|
||||
plugins: [
|
||||
[
|
||||
require.resolve('babel-plugin-react-docgen'),
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/server",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Storybook for Server: View HTML snippets from a server in isolation with Hot Reloading.",
|
||||
"keywords": [
|
||||
"storybook"
|
||||
@ -45,15 +45,15 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/api": "6.5.0-beta.1",
|
||||
"@storybook/client-api": "6.5.0-beta.1",
|
||||
"@storybook/core": "6.5.0-beta.1",
|
||||
"@storybook/core-common": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/api": "6.5.0-rc.1",
|
||||
"@storybook/client-api": "6.5.0-rc.1",
|
||||
"@storybook/core": "6.5.0-rc.1",
|
||||
"@storybook/core-common": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@storybook/node-logger": "6.5.0-beta.1",
|
||||
"@storybook/preview-web": "6.5.0-beta.1",
|
||||
"@storybook/store": "6.5.0-beta.1",
|
||||
"@storybook/node-logger": "6.5.0-rc.1",
|
||||
"@storybook/preview-web": "6.5.0-rc.1",
|
||||
"@storybook/store": "6.5.0-rc.1",
|
||||
"@types/node": "^14.14.20 || ^16.0.0",
|
||||
"@types/webpack-env": "^1.16.0",
|
||||
"core-js": "^3.8.2",
|
||||
@ -76,6 +76,6 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/client/index.js"
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/svelte",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Storybook for Svelte: Develop Svelte Component in isolation with Hot Reloading.",
|
||||
"keywords": [
|
||||
"storybook"
|
||||
@ -46,14 +46,14 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/client-logger": "6.5.0-beta.1",
|
||||
"@storybook/core": "6.5.0-beta.1",
|
||||
"@storybook/core-common": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/client-logger": "6.5.0-rc.1",
|
||||
"@storybook/core": "6.5.0-rc.1",
|
||||
"@storybook/core-common": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@storybook/docs-tools": "6.5.0-beta.1",
|
||||
"@storybook/node-logger": "6.5.0-beta.1",
|
||||
"@storybook/store": "6.5.0-beta.1",
|
||||
"@storybook/docs-tools": "6.5.0-rc.1",
|
||||
"@storybook/node-logger": "6.5.0-rc.1",
|
||||
"@storybook/store": "6.5.0-rc.1",
|
||||
"core-js": "^3.8.2",
|
||||
"global": "^4.4.0",
|
||||
"loader-utils": "^2.0.0",
|
||||
@ -83,6 +83,6 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/client/index.js"
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/vue",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Storybook for Vue: Develop Vue Component in isolation with Hot Reloading.",
|
||||
"keywords": [
|
||||
"storybook"
|
||||
@ -45,13 +45,13 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/client-logger": "6.5.0-beta.1",
|
||||
"@storybook/core": "6.5.0-beta.1",
|
||||
"@storybook/core-common": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/client-logger": "6.5.0-rc.1",
|
||||
"@storybook/core": "6.5.0-rc.1",
|
||||
"@storybook/core-common": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@storybook/docs-tools": "6.5.0-beta.1",
|
||||
"@storybook/store": "6.5.0-beta.1",
|
||||
"@storybook/docs-tools": "6.5.0-rc.1",
|
||||
"@storybook/store": "6.5.0-rc.1",
|
||||
"@types/node": "^14.14.20 || ^16.0.0",
|
||||
"@types/webpack-env": "^1.16.0",
|
||||
"core-js": "^3.8.2",
|
||||
@ -86,6 +86,6 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/client/index.js"
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
export { renderToDOM } from './render';
|
||||
export { decorateStory } from './decorateStory';
|
||||
export { render, renderToDOM } from './render';
|
||||
export { decorateStory as applyDecorators } from './decorateStory';
|
||||
|
||||
export const parameters = { framework: 'vue' };
|
||||
|
@ -22,7 +22,7 @@ const root = new Vue({
|
||||
});
|
||||
|
||||
export const render: ArgsStoryFn<VueFramework> = (props, context) => {
|
||||
const { id, component: Component } = context;
|
||||
const { id, component: Component, argTypes } = context;
|
||||
const component = Component as VueFramework['component'] & {
|
||||
__docgenInfo?: { displayName: string };
|
||||
props: Record<string, any>;
|
||||
@ -49,7 +49,7 @@ export const render: ArgsStoryFn<VueFramework> = (props, context) => {
|
||||
}
|
||||
|
||||
return {
|
||||
props: component.props,
|
||||
props: Object.keys(argTypes),
|
||||
components: { [componentName]: component },
|
||||
template: `<${componentName} v-bind="$props" />`,
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/vue3",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Storybook for Vue 3: Develop Vue 3 Components in isolation with Hot Reloading.",
|
||||
"keywords": [
|
||||
"storybook"
|
||||
@ -45,12 +45,12 @@
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/core": "6.5.0-beta.1",
|
||||
"@storybook/core-common": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/core": "6.5.0-rc.1",
|
||||
"@storybook/core-common": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@storybook/docs-tools": "6.5.0-beta.1",
|
||||
"@storybook/store": "6.5.0-beta.1",
|
||||
"@storybook/docs-tools": "6.5.0-rc.1",
|
||||
"@storybook/store": "6.5.0-rc.1",
|
||||
"@types/node": "^14.14.20 || ^16.0.0",
|
||||
"@types/webpack-env": "^1.16.0",
|
||||
"core-js": "^3.8.2",
|
||||
@ -83,6 +83,6 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/client/index.js"
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import type { StrictArgTypes } from '@storybook/csf';
|
||||
import type { ArgTypesExtractor } from '@storybook/docs-tools';
|
||||
import { hasDocgen, extractComponentProps, convert } from '@storybook/docs-tools';
|
||||
|
||||
const SECTIONS = ['props', 'events', 'slots'];
|
||||
const SECTIONS = ['props', 'events', 'slots', 'methods'];
|
||||
|
||||
export const extractArgTypes: ArgTypesExtractor = (component) => {
|
||||
if (!hasDocgen(component)) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
export { renderToDOM } from './render';
|
||||
export { decorateStory } from './decorateStory';
|
||||
export { render, renderToDOM } from './render';
|
||||
export { decorateStory as applyDecorators } from './decorateStory';
|
||||
|
||||
export const parameters = { framework: 'vue3' };
|
||||
|
@ -24,7 +24,7 @@ export function webpack(config: Configuration): Configuration {
|
||||
options: {},
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
test: /\.ts$/,
|
||||
use: [
|
||||
{
|
||||
loader: require.resolve('ts-loader'),
|
||||
@ -35,6 +35,19 @@ export function webpack(config: Configuration): Configuration {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.tsx$/,
|
||||
use: [
|
||||
{
|
||||
loader: require.resolve('ts-loader'),
|
||||
options: {
|
||||
transpileOnly: true,
|
||||
// Note this is different from the `appendTsSuffixTo` above!
|
||||
appendTsxSuffixTo: [/\.vue$/],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/web-components",
|
||||
"version": "6.5.0-beta.1",
|
||||
"version": "6.5.0-rc.1",
|
||||
"description": "Storybook for web-components: View web components snippets in isolation with Hot Reloading.",
|
||||
"keywords": [
|
||||
"lit-html",
|
||||
@ -50,15 +50,15 @@
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/plugin-syntax-import-meta": "^7.10.4",
|
||||
"@babel/preset-env": "^7.12.11",
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/client-api": "6.5.0-beta.1",
|
||||
"@storybook/client-logger": "6.5.0-beta.1",
|
||||
"@storybook/core": "6.5.0-beta.1",
|
||||
"@storybook/core-common": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-rc.1",
|
||||
"@storybook/client-api": "6.5.0-rc.1",
|
||||
"@storybook/client-logger": "6.5.0-rc.1",
|
||||
"@storybook/core": "6.5.0-rc.1",
|
||||
"@storybook/core-common": "6.5.0-rc.1",
|
||||
"@storybook/csf": "0.0.2--canary.4566f4d.1",
|
||||
"@storybook/docs-tools": "6.5.0-beta.1",
|
||||
"@storybook/preview-web": "6.5.0-beta.1",
|
||||
"@storybook/store": "6.5.0-beta.1",
|
||||
"@storybook/docs-tools": "6.5.0-rc.1",
|
||||
"@storybook/preview-web": "6.5.0-rc.1",
|
||||
"@storybook/store": "6.5.0-rc.1",
|
||||
"@types/node": "^14.14.20 || ^16.0.0",
|
||||
"@types/webpack-env": "^1.16.0",
|
||||
"babel-plugin-bundled-import-meta": "^0.3.1",
|
||||
@ -82,6 +82,6 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "85bcae3041a0664d7c0ee4756241e29ad1063a9a",
|
||||
"gitHead": "3f09d4e6b0c655a092dc812488ef2c7ed3808401",
|
||||
"sbmodern": "dist/modern/client/index.js"
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { html } from 'lit-html';
|
||||
import { styleMap } from 'lit-html/directives/style-map';
|
||||
import { addons, useEffect } from '@storybook/addons';
|
||||
import type { StoryContext } from '@storybook/addons';
|
||||
import { SNIPPET_RENDERED } from '@storybook/docs-tools';
|
||||
import type { StoryContext, WebComponentsFramework } from '..';
|
||||
import { sourceDecorator } from './sourceDecorator';
|
||||
|
||||
jest.mock('@storybook/addons');
|
||||
const mockedAddons = addons as jest.Mocked<typeof addons>;
|
||||
const mockedUseEffect = useEffect as jest.Mocked<typeof useEffect>;
|
||||
const mockedUseEffect = useEffect as jest.Mock;
|
||||
|
||||
expect.addSnapshotSerializer({
|
||||
print: (val: any) => val,
|
||||
@ -16,16 +16,22 @@ expect.addSnapshotSerializer({
|
||||
|
||||
const tick = () => new Promise((r) => setTimeout(r, 0));
|
||||
|
||||
const makeContext = (name: string, parameters: any, args: any, extra?: object): StoryContext => ({
|
||||
id: `lit-test--${name}`,
|
||||
kind: 'js-text',
|
||||
name,
|
||||
parameters,
|
||||
args,
|
||||
argTypes: {},
|
||||
globals: {},
|
||||
...extra,
|
||||
});
|
||||
const makeContext = (
|
||||
name: string,
|
||||
parameters: any,
|
||||
args: any,
|
||||
extra?: Partial<StoryContext<WebComponentsFramework>>
|
||||
) =>
|
||||
({
|
||||
id: `lit-test--${name}`,
|
||||
kind: 'js-text',
|
||||
name,
|
||||
parameters,
|
||||
args,
|
||||
argTypes: {},
|
||||
globals: {},
|
||||
...extra,
|
||||
} as StoryContext<WebComponentsFramework>);
|
||||
|
||||
describe('sourceDecorator', () => {
|
||||
let mockChannel: { on: jest.Mock; emit?: jest.Mock };
|
||||
@ -106,4 +112,23 @@ describe('sourceDecorator', () => {
|
||||
sourceDecorator(storyFn, context);
|
||||
expect(transformSource).toHaveBeenCalledWith('<div>args story</div>', context);
|
||||
});
|
||||
|
||||
it('should clean lit expression comments', async () => {
|
||||
const storyFn = (args: any) => html`<div>${args.slot}</div>`;
|
||||
const context = makeContext(
|
||||
'args',
|
||||
{ __isArgsStory: true },
|
||||
{ slot: 'some content' },
|
||||
{ originalStoryFn: storyFn }
|
||||
);
|
||||
// bind args to storyFn, as it's done in Storybook
|
||||
const boundStoryFn = storyFn.bind(null, context.args);
|
||||
sourceDecorator(boundStoryFn, context);
|
||||
await tick();
|
||||
expect(mockChannel.emit).toHaveBeenCalledWith(
|
||||
SNIPPET_RENDERED,
|
||||
'lit-test--args',
|
||||
'<div>some content</div>'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -6,6 +6,9 @@ import { SNIPPET_RENDERED, SourceType } from '@storybook/docs-tools';
|
||||
|
||||
import type { WebComponentsFramework } from '..';
|
||||
|
||||
// Taken from https://github.com/lit/lit/blob/main/packages/lit-html/src/test/test-utils/strip-markers.ts
|
||||
const LIT_EXPRESSION_COMMENTS = /<!--\?lit\$[0-9]+\$-->|<!--\??-->/g;
|
||||
|
||||
function skipSourceRender(context: StoryContext<WebComponentsFramework>) {
|
||||
const sourceParams = context?.parameters.docs?.source;
|
||||
const isArgsStory = context?.parameters.__isArgsStory;
|
||||
@ -44,7 +47,10 @@ export function sourceDecorator(
|
||||
if (!skipSourceRender(context)) {
|
||||
const container = window.document.createElement('div');
|
||||
render(story, container);
|
||||
source = applyTransformSource(container.innerHTML.replace(/<!---->/g, ''), context);
|
||||
source = applyTransformSource(
|
||||
container.innerHTML.replace(LIT_EXPRESSION_COMMENTS, ''),
|
||||
context
|
||||
);
|
||||
}
|
||||
|
||||
return story;
|
||||
|
@ -362,7 +362,7 @@ The following table details how to use the API values:
|
||||
| **showPanel** | Boolean | Display panel that shows addon configurations | `true` |
|
||||
| **panelPosition** | String/Object | Where to show the addon panel | `bottom` or `right` |
|
||||
| **enableShortcuts** | Boolean | Enable/disable shortcuts | `true` |
|
||||
| **isToolshown** | Boolean | Show/hide tool bar | `true` |
|
||||
| **showToolbar** | Boolean | Show/hide tool bar | `true` |
|
||||
| **theme** | Object | Storybook Theme, see next section | `undefined` |
|
||||
| **selectedPanel** | String | Id to select an addon panel | `storybook/actions/panel` |
|
||||
| **initialActive** | String | Select the default active tab on Mobile | `sidebar` or `canvas` or `addons` |
|
||||
|
@ -2,7 +2,7 @@
|
||||
title: Install addons
|
||||
---
|
||||
|
||||
Storybook has [hundreds of reusable addons](/addons) that are packaged as NPM modules. Let's walk through how to extend Storybook by installing and registering addons.
|
||||
Storybook has [hundreds of reusable addons](https://storybook.js.org/addons) that are packaged as NPM modules. Let's walk through how to extend Storybook by installing and registering addons.
|
||||
|
||||
### Using addons
|
||||
|
||||
|
@ -5,7 +5,7 @@ title: 'Introduction to addons'
|
||||
Addons extend Storybook with features and integrations that are not built into the core. Most Storybook features are implemented as addons. For instance: [documentation](../writing-docs/introduction.md), [accessibility testing](https://github.com/storybookjs/storybook/tree/master/addons/a11y), [interactive controls](../essentials/controls.md), among others.
|
||||
The [addon API](./addons-api.md) makes it easy for you to configure and customize Storybook in new ways. There are countless addons made by the community that unlock time-saving workflows.
|
||||
|
||||
Browse our [addon catalog](/addons) to install an existing addon or as inspiration for your own addon.
|
||||
Browse our [addon catalog](https://storybook.js.org/addons) to install an existing addon or as inspiration for your own addon.
|
||||
|
||||
## Storybook basics
|
||||
|
||||
|
@ -50,14 +50,23 @@ Once you've gone through the prompts, your `package.json` should look like:
|
||||
|
||||
### Build system
|
||||
|
||||
We'll need to add the necessary dependencies and make some adjustments. Run the following commands:
|
||||
We'll need to add the necessary dependencies and make some adjustments. Run the following command to install the required dependencies:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-write-addon-install-dependencies.yarn.js.mdx',
|
||||
'common/storybook-write-addon-install-dependencies.npm.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
Initialize a local Storybook instance to allow you to test your addon.
|
||||
|
||||
```shell
|
||||
# Installs React and Babel CLI
|
||||
yarn add react react-dom @babel/cli
|
||||
|
||||
# Adds Storybook:
|
||||
npx sb init
|
||||
npx storybook init
|
||||
```
|
||||
|
||||
<div class="aside">
|
||||
@ -246,4 +255,4 @@ To dive deeper, we recommend Storybook's [creating an addon](https://storybook.j
|
||||
|
||||
### Addon kit
|
||||
|
||||
To help you jumpstart the addon development, the Storybook maintainers created an [`addon-kit`](https://github.com/storybookjs/addon-kit), use it to bootstrap your next addon.
|
||||
To help you jumpstart the addon development, the Storybook maintainers created an [`addon-kit`](https://github.com/storybookjs/addon-kit), use it to bootstrap your next addon.
|
||||
|
@ -2,7 +2,7 @@
|
||||
title: 'Write a preset addon'
|
||||
---
|
||||
|
||||
[Storybook preset addons](./addon-types.md#preset-addons) are grouped collections of `babel`, `webpack`, and `addons` configurations that support specific use cases in Storybook, such as typescript or MDX support.
|
||||
[Storybook preset addons](./addon-types.md#preset-addons) are grouped collections of `babel`, `webpack`, and `addons` configurations that support specific use cases in Storybook, such as TypeScript or MDX support.
|
||||
|
||||
This doc covers the [presets API](#presets-api) and how to use the presets mechanism for [advanced configuration](#advanced-configuration).
|
||||
|
||||
@ -10,7 +10,7 @@ This doc covers the [presets API](#presets-api) and how to use the presets mecha
|
||||
|
||||
A preset is a set of hooks that is called by Storybook on initialization and can override configurations for `babel`, `webpack`, `addons`, and `entries`.
|
||||
|
||||
Each configuration has a similar signature, accepting a base configuration object and options, as in this webpack example:
|
||||
Each configuration has a similar signature, accepting a base configuration object and options, as in this Webpack example:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
@ -46,7 +46,7 @@ For example, Storybook's Mihtril support uses plugins internally and here's how
|
||||
|
||||
### Webpack
|
||||
|
||||
The webpack functions `webpack`, `webpackFinal`, and `managerWebpack` configure webpack.
|
||||
The Webpack functions `webpack`, `webpackFinal`, and `managerWebpack` configure Webpack.
|
||||
|
||||
All functions take a [webpack4 configuration object](https://webpack.js.org/configuration/).
|
||||
|
||||
@ -62,11 +62,11 @@ For example, here is how Storybook automatically adopts `create-react-app`'s con
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
- `webpack` is applied to the preview config after it has been initialized by storybook
|
||||
- `webpack` is applied to the preview config after it has been initialized by Storybook
|
||||
- `webpackFinal` is applied to the preview config after all user presets have been applied
|
||||
- `managerWebpack` is applied to the manager config
|
||||
|
||||
As of Storybook 6.3, Storybook can run with either `webpack4` or `webpack5` builder. If your addon needs to know which version of Webpack it's running inside, the version and the actual webpack instance itself are both available inside your preset:
|
||||
As of Storybook 6.3, Storybook can run with either `webpack4` or `webpack5` builder. If your addon needs to know which version of Webpack it's running inside, the version and the actual Webpack instance itself are both available inside your preset:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
@ -81,7 +81,7 @@ As of Storybook 6.3, Storybook can run with either `webpack4` or `webpack5` buil
|
||||
|
||||
### Manager entries
|
||||
|
||||
The addon config `managerEntries` allows you to add addons to Storybook from within a preset. For addons that require custom webpack/babel configuration, it is easier to install the preset, and it will take care of everything.
|
||||
The addon config `managerEntries` allows you to add addons to Storybook from within a preset. For addons that require custom Webpack/Babel configuration, it is easier to install the preset, and it will take care of everything.
|
||||
|
||||
For example, the Storysource preset contains the following code:
|
||||
|
||||
@ -176,9 +176,9 @@ Entries are the place to register entry points for the preview. For example it c
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
The presets API is also more powerful than the [standard configuration options](../configure/webpack.md#extending-storybooks-webpack-config) available in Storybook, so it's also possible to use presets for more advanced configuration without actually publishing a preset yourself.
|
||||
The presets API is also more powerful than the [standard configuration options](../builders/webpack.md#extending-storybooks-webpack-config) available in Storybook, so it's also possible to use presets for more advanced configuration without actually publishing a preset yourself.
|
||||
|
||||
For example, some users want to configure the webpack for Storybook's UI and addons ([issue](https://github.com/storybookjs/storybook/issues/4995)), but this is not possible using [standard webpack configuration](../configure/webpack.md#default-configuration) (it used to be possible before SB4.1). However, you can achieve this with a private preset.
|
||||
For example, some users want to configure the Webpack for Storybook's UI and addons ([issue](https://github.com/storybookjs/storybook/issues/4995)), but this is not possible using [standard Webpack configuration](../builders/webpack.md#default-configuration) (it used to be possible before SB4.1). However, you can achieve this with a private preset.
|
||||
|
||||
If it doesn't exist yet, create a file `.storybook/main.js`:
|
||||
|
||||
|
@ -4,6 +4,12 @@ title: 'CLI options'
|
||||
|
||||
Storybook comes with two CLI utilities: `start-storybook` and `build-storybook`.
|
||||
|
||||
<div class="aside">
|
||||
|
||||
Storybook collects completely anonymous data to help us improve user experience. Participation is optional, and you may [opt-out](../configure/telemetry.md#how-to-opt-out) if you'd not like to share any information.
|
||||
|
||||
</div>
|
||||
|
||||
Pass these commands the following options to alter Storybook's behavior.
|
||||
|
||||
## start-storybook
|
||||
@ -18,10 +24,10 @@ Usage: start-storybook [options]
|
||||
| `-V`, `--version` | Output the version number <br/>`start-storybook -V` |
|
||||
| `-p`, `--port [number]` | Port to run Storybook <br/>`start-storybook -p 9009` |
|
||||
| `-h`, `--host [string]` | Host to run Storybook <br/>`start-storybook -h my-host.com` |
|
||||
| `-s`, `--static-dir` | **Deprecated** [see note](#static-dir-deprecation). Directory where to load static files from, comma-separated list <br/>`start-storybook -s public` |
|
||||
| `-s`, `--static-dir` | **Deprecated** [see note](#static-dir-deprecation). Directory where to load static files from, comma-separated list<br/>`start-storybook -s public` |
|
||||
| `-c`, `--config-dir [dir-name]` | Directory where to load Storybook configurations from <br/>`start-storybook -c .storybook` |
|
||||
| `--https` | Serve Storybook over HTTPS. Note: You must provide your own certificate information. <br/>`start-storybook --https` |
|
||||
| `--ssl-ca` | Provide an SSL certificate authority. (Optional with --https, required if using a self-signed certificate) <br/>`start-storybook --ssl-ca my-certificate` |
|
||||
| `--https` | Serve Storybook over HTTPS. Note: You must provide your own certificate information<br/>`start-storybook --https` |
|
||||
| `--ssl-ca` | Provide an SSL certificate authority. (Optional with --https, required if using a self-signed certificate)<br/>`start-storybook --ssl-ca my-certificate` |
|
||||
| `--ssl-cert` | Provide an SSL certificate. (Required with --https)<br/>`start-storybook --ssl-cert my-ssl-certificate` |
|
||||
| `--ssl-key` | Provide an SSL key. (Required with --https)<br/>`start-storybook --ssl-key my-ssl-key` |
|
||||
| `--smoke-test` | Exit after successful start<br/>`start-storybook --smoke-test` |
|
||||
@ -32,7 +38,8 @@ Usage: start-storybook [options]
|
||||
| `--debug-webpack` | Display final webpack configurations for debugging purposes<br/>`start-storybook --debug-webpack` |
|
||||
| `--webpack-stats-json` | Write Webpack Stats JSON to disk<br/>`start-storybook --webpack-stats-json /tmp/webpack-stats` |
|
||||
| `--docs` | Starts Storybook in documentation mode. Learn more about it in [here](../writing-docs/build-documentation.md#preview-storybooks-documentation)<br/>`start-storybook --docs` |
|
||||
| `--no-manager-cache` | Disables Storybook's manager caching mechanism. See note below.<br/>`start-storybook --no-manager-cache` |
|
||||
| `--no-manager-cache` | Disables Storybook's manager caching mechanism. See note below<br/>`start-storybook --no-manager-cache` |
|
||||
| `--disable-telemetry` | Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry.md)<br/>`start-storybook --disable-telemetry` |
|
||||
|
||||
<div class="aside">
|
||||
💡 The flag <code>--no-manager-cache</code> disables the internal caching of Storybook and can severely impact your Storybook loading time, so only use it when you need to refresh Storybook's UI, such as when editing themes.
|
||||
@ -54,15 +61,16 @@ Usage: build-storybook [options]
|
||||
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `-h`, `--help` | Output usage information<br/>`build-storybook --help` |
|
||||
| `-V`, `--version` | Output the version number<br/>`build-storybook -V` |
|
||||
| `-s`, `--static-dir` | **Deprecated** [see note](#static-dir-deprecation). Directory where to load static files from, comma-separated list<br/>`build-storybook -s public` |
|
||||
| `-s`, `--static-dir` | **Deprecated** [see note](#static-dir-deprecation).<br/> Directory where to load static files from, comma-separated list<br/>`build-storybook -s public` |
|
||||
| `-o`, `--output-dir [dir-name]` | Directory where to store built files<br/>`build-storybook -o /my-deployed-storybook` |
|
||||
| `-c`, `--config-dir [dir-name]` | Directory where to load Storybook configurations from<br/>`build-storybook -c .storybook` |
|
||||
| `--loglevel [level]` | Controls level of logging during build. Can be one of: [silly, verbose, info (default), warn, error, silent]<br/>`build-storybook --loglevel warn` |
|
||||
| `--loglevel [level]` | Controls level of logging during build.<br/> Available options: `silly`, `verbose`, `info` (default), `warn`, `error`, `silent`<br/>`build-storybook --loglevel warn` |
|
||||
| `--quiet` | Suppress verbose build output<br/>`build-storybook --quiet` |
|
||||
| `--no-dll` | Do not use dll reference (no-op)<br/>`build-storybook --no-dll` |
|
||||
| `--debug-webpack` | Display final webpack configurations for debugging purposes<br/>`build-storybook --debug-webpack` |
|
||||
| `--webpack-stats-json` | Write Webpack Stats JSON to disk<br/>`build-storybook --webpack-stats-json /my-storybook/webpack-stats` |
|
||||
| `--docs` | Builds Storybook in documentation mode. Learn more about it in [here](../writing-docs/build-documentation.md#publish-storybooks-documentation)<br/>`build-storybook --docs` |
|
||||
| `--disable-telemetry` | Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry.md).<br/>`build-storybook --disable-telemetry` |
|
||||
|
||||
<div class="aside">
|
||||
💡 If you're using npm instead of yarn to publish Storybook, the commands work slightly different. For example, <code>npm run build-storybook -- -o ./path/to/build</code>.
|
||||
|
163
docs/builders/builder-api.md
Normal file
163
docs/builders/builder-api.md
Normal file
@ -0,0 +1,163 @@
|
||||
---
|
||||
title: 'Builder API'
|
||||
---
|
||||
|
||||
Storybook is architected to support multiple builders, including [Webpack](https://webpack.js.org/), [Vite](https://vitejs.dev/), and [ESBuild](https://esbuild.github.io/). The builder API is the set of interfaces you can use to add a new builder to Storybook.
|
||||
|
||||

|
||||
|
||||
## How do builders work?
|
||||
|
||||
In Storybook, a builder is responsible for compiling your components and stories into JS bundles that run in the browser. A builder also provides a development server for interactive development and a production mode for optimized bundles.
|
||||
|
||||
To opt into a builder, the user must add it as a dependency and then edit their configuration file (`.storybook/main.js`) to enable it. For example, with the Vite builder:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-vite-builder-install.yarn.js.mdx',
|
||||
'common/storybook-vite-builder-install.npm.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-vite-builder-register.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
## Builder API
|
||||
|
||||
In Storybook, every builder must implement the following [API](https://github.com/storybookjs/storybook/blob/next/lib/core-common/src/types.ts#L170-L189), exposing the following configuration options and entry points:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-builder-api-interface.ts.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
In development mode, the `start` API call is responsible for initializing the development server to monitor the file system for changes (for example, components and stories) then execute a hot module reload in the browser.
|
||||
It also provides a **bail** function to allow the running process to end gracefully, either via user input or error.
|
||||
|
||||
In production, the `build` API call is responsible for generating a static Storybook build, storing it by default in the `storybook-static` directory if no additional configuration is provided. The generated output should contain everything the user needs to view its Storybook by opening either the `index.html` or `iframe.html` in a browser with no other processes running.
|
||||
|
||||
## Implementation
|
||||
|
||||
Under the hood, a builder is responsible for serving/building the preview `iframe`, which has its own set of requirements. To fully support Storybook, including the [Essential addons](../writing-stories/introduction.md) that ship with Storybook, it must consider the following.
|
||||
|
||||
### Import stories
|
||||
|
||||
The `stories` configuration field enables story loading in Storybook. It defines an array of file globs containing the physical location of the component's stories. The builder must be able to load those files and monitor them for changes and update the UI accordingly.
|
||||
|
||||
### Provide configuration options
|
||||
|
||||
By default, Storybook's configuration is handled in a dedicated file (`storybook/main.js|ts`), giving the user the option to customize it to suit its needs. The builder should also provide its own configuration support through additional fields or some other builder-appropriate mechanism. For example:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-builder-api-configuration-options.ts.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
### Handle preview.js exports
|
||||
|
||||
The [`preview.js`](../configure/overview.md#configure-story-rendering) configuration file allows users to control how the story renders in the UI. This is provided via the [decorators](../writing-stories/decorators.md) named export. When Storybook starts, it converts these named exports into internal API calls via virtual module entry, for example, `addDecorator()`. The builder must also provide a similar implementation. For example:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-builder-api-preview-exports.ts.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
### MDX support
|
||||
|
||||
[Storybook's Docs](../writing-docs/introduction.md) includes the ability to author stories/documentation in MDX using a Webpack loader. The builder must also know how to interpret MDX and invoke Storybook's special extensions. For example:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-builder-api-mdx.ts.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
### Generate source code snippets
|
||||
|
||||
Storybook annotates components and stories with additional metadata related to their inputs to automatically generate interactive controls and documentation. Currently, this is provided via Webpack loaders/plugins. The builder must re-implement this to support those features.
|
||||
|
||||
### Generate a static build
|
||||
|
||||
One of Storybook's core features it's the ability to generate a static build that can be [published](../sharing/publish-storybook.md) to a web hosting service. The builder must also be able to provide a similar mechanism. For example:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-builder-api-build-server.ts.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
### Development server integration
|
||||
|
||||
By default, when Storybook starts in development mode, it relies on its internal development server. The builder needs to be able to integrate with it. For example:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-builder-api-dev-server.ts.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
### Shutdown the development server
|
||||
|
||||
The builder must provide a way to stop the development server once the process terminates; this can be via user input or error. For example:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-builder-api-shutdown-server.ts.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
### HMR support
|
||||
|
||||
While running in development mode, the builder's development server must be able to reload the page once a change happens, either in a story, component, or helper function.
|
||||
|
||||
### More information
|
||||
|
||||
This area is under rapid development, so the documented is still in progress and subject to change. If you are interested in writing your builder, we encourage you to check [webpack](https://github.com/storybookjs/storybook/tree/next/lib/builder-webpack4), [Vite](https://github.com/storybookjs/builder-vite), and Modern Web's [dev-server-storybook](https://github.com/modernweb-dev/web/blob/master/packages/dev-server-storybook/src/serve/storybookPlugin.ts) source code. In addition, we have a wonderful contributor community on [Storybook Discord](https://discord.gg/storybook) if you have questions. Ping us in the [#contributing](https://discord.com/channels/486522875931656193/839297503446695956) channel.
|
||||
|
||||
#### Learn more about builders
|
||||
|
||||
- [Vite builder](./vite.md) for bundling with Vite
|
||||
- [Webpack builder](./webpack.md) for bundling with Webpack
|
||||
- Builder API for building a Storybook builder
|
24
docs/builders/overview.md
Normal file
24
docs/builders/overview.md
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
title: 'Builders'
|
||||
---
|
||||
|
||||
Storybook, at its core, is powered by builders such as Webpack and Vite. These builders spin up a development environment, compile your code—Javascript, CSS, and MDX—into an executable bundle and update the browser in real-time.
|
||||
|
||||

|
||||
|
||||
## CLI basics
|
||||
|
||||
Before diving into setting up Storybook's builders, let's look at how the CLI configures them. When you initialize Storybook (via `npx storybook init`), the CLI automatically detects which builder to use based on your application. For example, if you're working with Vite, it will install the Vite builder. If you're working with Webpack, it installs the Webpack builder based on your current version.
|
||||
|
||||
Additionally, you can also provide a flag to Storybook's CLI and specify the builder you want to use:
|
||||
|
||||
```shell
|
||||
npx storybook init --builder <webpack4 | webpack5 | vite>
|
||||
```
|
||||
|
||||
## Manual setup
|
||||
|
||||
Storybook uses the Webpack 4 builder by default if you don't specify one. If you want to use a different builder in your application, these docs detail how you can set up Storybook's supported builders.
|
||||
|
||||
- [**Vite builder**](./vite.md) for bundling your stories with Vite with near-instant HMR.
|
||||
- [**Webpack**](./webpack.md) for bundling your stories with Webpack with improved performance
|
BIN
docs/builders/storybook-builder-workflow.png
Normal file
BIN
docs/builders/storybook-builder-workflow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 178 KiB |
BIN
docs/builders/storybook-builders.png
Normal file
BIN
docs/builders/storybook-builders.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 84 KiB |
122
docs/builders/vite.md
Normal file
122
docs/builders/vite.md
Normal file
@ -0,0 +1,122 @@
|
||||
---
|
||||
title: 'Vite'
|
||||
---
|
||||
|
||||
Storybook Vite builder bundles your components and stories with [Vite](https://vitejs.dev/), a fast ESM bundler.
|
||||
|
||||
- For applications built with Vite: it allows reusing the existing configuration in Storybook.
|
||||
- For applications built with Webpack: it provides faster startup and refresh times, with the disadvantage that your component's execution environment differs from your application.
|
||||
|
||||
## Setup
|
||||
|
||||
If you ran `npx storybook init` to include Storybook in your Vite application, the builder is already installed and configured for you. If you want, you can also opt into it manually.
|
||||
|
||||
Run the following command to install the builder.
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-vite-builder-install.yarn.js.mdx',
|
||||
'common/storybook-vite-builder-install.npm.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
Update your Storybook configuration (in `.storybook/main.js|ts`) to include the builder.
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-vite-builder-register.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
## Configuration
|
||||
|
||||
Out of the box, Storybook's Vite builder includes a set of configuration defaults for the supported frameworks. You can also fine-tune them or override them to match your existing configuration as, by default, the builder does not read your `vite.config.js` file. For example, if you need to set up aliasing, you can adjust your Storybook configuration file (`.storybook/main.js|ts`) and provide the following:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-vite-builder-aliasing.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
The asynchronous function`viteFinal` receives a `config` object with the default builder configuration and returns the updated configuration with the defined alias.
|
||||
|
||||
You can also override the builder's configuration based on the environment. For instance, if you need to provide a custom configuration for development purposes and another for production, you can extend the default configuration as follows:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-vite-builder-config-env.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
### Svelte configuration
|
||||
|
||||
If you're working with Svelte's Vite plugin ([`vite-plugin-svelte`](https://github.com/sveltejs/vite-plugin-svelte/tree/main/packages/vite-plugin-svelte)), you can extend your existing configuration and include an additional `SvelteOptions` object to customize it. For example:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-vite-builder-svelte-plugin.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
### TypeScript
|
||||
|
||||
If you need, you can also configure Storybook's Vite builder using TypeScript. Rename your `.storybook/main.js` to `.storybook/main.ts` and adjust it as follows:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-vite-builder-ts-configure.ts.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Working directory not being detected
|
||||
|
||||
By default, the Vite builder enables Vite's [`server.fs.strict`](https://vitejs.dev/config/#server-fs-strict) option for increased security, defining the project's `root` to Storybook's configuration directory
|
||||
If you need to override it, you can use the `viteFinal` function and adjust it.
|
||||
|
||||
### ArgTypes are not generated automatically
|
||||
|
||||
Currently, [automatic argType inference](../api/argtypes.md#automatic-argtype-inference) is only available for React and Vue3. With React, the Vite builder defaults to `react-docgen-typescript` if TypeScript is listed as a dependency. If you run into any issues, you can revert to `react-docgen` by updating your Storybook configuration file as follows:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-vite-builder-react-docgen.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
#### Learn more about builders
|
||||
|
||||
- Vite builder for bundling with Vite
|
||||
- [Webpack builder](./webpack.md) for bundling with Webpack
|
||||
- [Builder API](./builder-api.md) for building a Storybook builder
|
221
docs/builders/webpack.md
Normal file
221
docs/builders/webpack.md
Normal file
@ -0,0 +1,221 @@
|
||||
---
|
||||
title: 'Webpack'
|
||||
---
|
||||
|
||||
Storybook displays your components in a custom web application built using [Webpack](https://webpack.js.org/). Webpack is a complex tool, but our default configuration is intended to cover most use cases. [Addons](https://storybook.js.org/addons/) are also available that extend the configuration for other everyday use cases.
|
||||
|
||||
You can customize Storybook's Webpack setup by providing a `webpackFinal` field in [`.storybook/main.js`](../configure/overview.md#configure-your-storybook-project) file.
|
||||
|
||||
The value should be an async function that receives a Webpack config and eventually returns a Webpack config.
|
||||
|
||||
### Default configuration
|
||||
|
||||
By default, Storybook's Webpack configuration will allow you to:
|
||||
|
||||
#### Import images and other static files
|
||||
|
||||
You can import images and other local files and have them built into the Storybook:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/my-component-story-import-static-asset.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
#### Import JSON as JavaScript
|
||||
|
||||
You can import `.json` files and have them expanded to a JavaScript object:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/my-component-story-import-json.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
If you want to know the exact details of the Webpack config, the best way is to run either of the following commands:
|
||||
|
||||
For development mode:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-debug-webpack-dev.yarn.js.mdx',
|
||||
'common/storybook-debug-webpack-dev.npm.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
For production mode:
|
||||
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-debug-webpack-prod.yarn.js.mdx',
|
||||
'common/storybook-debug-webpack-prod.npm.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
### Code splitting
|
||||
|
||||
Starting with Storybook 6.4, [code splitting](https://v4.webpack.js.org/guides/code-splitting/) is supported through a configuration flag. Update your Storybook configuration and add the `storyStoreV7` flag:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-on-demand-story-loading.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
When you start your Storybook, you'll see an improvement in loading times. Read more about it in the [announcement post](https://storybook.js.org/blog/storybook-on-demand-architecture/) and the [configuration documentation](../configure/overview.md#configure-your-storybook-project).
|
||||
|
||||
### Webpack 5
|
||||
|
||||
Storybook builds your project with Webpack 4 by default. If your project uses Webpack 5, you can opt into the Webpack 5 builder by installing the required dependencies (i.e., `@storybook/builder-webpack5`, `@storybook/manager-webpack5`) and update your Storybook configuration as follows:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-main-webpack5.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
Once you are using Webpack 5, you can further opt into some features to optimize your build:
|
||||
|
||||
#### Lazy Compilation
|
||||
|
||||
Storybook supports Webpack's experimental [lazy compilation](https://webpack.js.org/configuration/experiments/#experimentslazycompilation) feature, via the `lazyCompilation` builder flag:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-main-webpack5-lazyCompilation.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
This feature applies in development mode, and will mean your Storybook will start up faster, at the cost of slightly slower browsing time when you change stories.
|
||||
|
||||
#### Filesystem Caching
|
||||
|
||||
Storybook supports Webpack's [filesystem caching](https://webpack.js.org/configuration/cache/#cachetype) feature, via the `fsCache` builder flag:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-main-webpack5-fsCache.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
This feature will mean build output is cached between runs of Storybook, speeding up subsequent startup times.
|
||||
|
||||
### Extending Storybook’s Webpack config
|
||||
|
||||
To extend the above configuration, use the `webpackFinal` field of [`.storybook/main.js`](../configure/overview.md#configure-your-storybook-project).
|
||||
|
||||
The value should export a `function`, which will receive the default config as its first argument. The second argument is an options object from Storybook, and this will have information about where config came from, whether we're in production or development mode, etc.
|
||||
|
||||
For example, if you wanted to add [Sass](https://sass-lang.com/) support, you can adjust your configuration as such:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-main-add-sass-config.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
Storybook uses the config returned from the above function to render your components in Storybook's "preview" iframe. Note that Storybook has an entirely separate Webpack config for its UI (also referred to as the "manager"), so the customizations you make only apply to the rendering of your stories, i.e., you can completely replace `config.module.rules` if you want.
|
||||
|
||||
Nevertheless, edit `config` with care. Make sure to preserve the following config options:
|
||||
|
||||
- **entry**
|
||||
- **output**
|
||||
|
||||
Furthermore, `config` requires the `HtmlWebpackplugin` to generate the preview page, so rather than overwriting `config.plugins` you should probably append to it (or overwrite it with care), see [the following issue](https://github.com/storybookjs/storybook/issues/6020) for examples on how to handle this:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-main-simplified-config.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
Finally, if your custom Webpack config uses a loader that does not explicitly include specific file extensions via the `test` property, in that case, it is necessary to `exclude` the `.ejs` file extension from that loader.
|
||||
|
||||
If you're using a non-standard Storybook config directory, you should put `main.js` there instead of `.storybook` and update the `include` path to ensure it resolves to your project root.
|
||||
|
||||
### Using your existing config
|
||||
|
||||
Suppose you have an existing Webpack config for your project and want to reuse this app's configuration. In that case, you can import your main Webpack config into Storybook's [`.storybook/main.js`](../configure/overview.md#configure-your-storybook-project) and merge both:
|
||||
|
||||
The following code snippet shows how you can replace the loaders from Storybook with the ones from your app's `webpack.config.js`:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-main-using-existing-config.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<div class="aside">
|
||||
|
||||
💡 Projects initialized via generators (e.g, Vue CLI) may require that you import their own Webpack config file (i.e., <code>/projectRoot/node_modules/@vue/cli-service/webpack.config.js</code>) to use a certain feature with Storybook. For other generators, make sure to check the documentation for instructions.
|
||||
|
||||
</div>
|
||||
|
||||
### TypeScript Module Resolution
|
||||
|
||||
When working with TypeScript projects, the default Webpack configuration may fail to resolve module aliases defined in your [`tsconfig` file](https://www.typescriptlang.org/tsconfig). To work around this issue you may use [`tsconfig-paths-webpack-plugin`](https://github.com/dividab/tsconfig-paths-webpack-plugin#tsconfig-paths-webpack-plugin) while [extending Storybook's Webpack config](#extending-storybooks-webpack-config) like:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<CodeSnippets
|
||||
paths={[
|
||||
'common/storybook-main-ts-module-resolution.js.mdx',
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<div class="aside">
|
||||
💡 Learn more about Storybook's <a href="../configure/typescript">built-in TypeScript support</a> or see <a href="https://github.com/storybookjs/storybook/issues/14087">this issue</a> for more information.
|
||||
</div>
|
||||
|
||||
#### Learn more about builders
|
||||
|
||||
- [Vite builder](./vite.md) for bundling with Vite
|
||||
- Webpack builder for bundling with Webpack
|
||||
- [Builder API](./builder-api.md) for building a Storybook builder
|
@ -81,7 +81,7 @@ For detailed instructions on migrating from `V6` mode, please see [MIGRATION.md]
|
||||
If your app does not include a babelrc file, and you need one, you can create it by running the following command in your project directory:
|
||||
|
||||
```sh
|
||||
npx sb@next babelrc
|
||||
npx storybook@next babelrc
|
||||
```
|
||||
|
||||
Once the command completes, you should have a `.babelrc.json` file created in the root directory of your project, similar to the following example:
|
||||
|
@ -42,7 +42,11 @@ Additionally, if you need Storybook specific styles that are separate from your
|
||||
```
|
||||
|
||||
### Nx with Angular 13
|
||||
If you're working with Storybook and [NX libraries](https://nx.dev/structure/library-types), you can extend your project's configuration (i.e., `project.json`) and provide the application's styles. For example:
|
||||
|
||||
If you're working with Storybook and [Nx libraries](https://nx.dev/structure/library-types),
|
||||
you can extend your project's configuration (i.e., `project.json`) and provide the application's styles.
|
||||
|
||||
For earlier Nx versions (prior to `14.1.8`), your configuration would look like this:
|
||||
|
||||
```json
|
||||
"build-storybook": {
|
||||
@ -56,11 +60,35 @@ If you're working with Storybook and [NX libraries](https://nx.dev/structure/lib
|
||||
},
|
||||
"projectBuildConfig": "example-lib:build-storybook",
|
||||
"styles": ["apps/example-app/src/styles.scss"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Starting with version `14.1.8`, Nx uses the Storybook builder directly, which means any configuration supplied to the builder also applies to the NX setup. If you're working with a library, you'll need to configure the styling options ( e.g., preprocessors) inside the `build-storybook` `options` configuration object. For example:
|
||||
|
||||
```json
|
||||
"storybook": {
|
||||
"executor": "@storybook/angular:start-storybook",
|
||||
"options": {
|
||||
"configDir": "apps/example-lib/.storybook",
|
||||
"browserTarget": "example-lib:build-storybook",
|
||||
},
|
||||
},
|
||||
"configurations": {
|
||||
"ci": {
|
||||
"quiet": true
|
||||
"build-storybook": {
|
||||
"executor": "@storybook/angular:build-storybook",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputDir": "dist/storybook/example-lib",
|
||||
"configDir": "apps/example-lib/.storybook",
|
||||
"browserTarget": "example-lib:build-storybook",
|
||||
"styles": [".storybook/custom-styles.scss"],
|
||||
"stylePreprocessorOptions": {
|
||||
"includePaths": [
|
||||
"libs/design-system/src/lib"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When Nx runs, it will load Storybook's configuration and styling based on the `storybook`'s [`browserTarget`](https://nx.dev/storybook/extra-topics-for-angular-projects#setting-up-browsertarget).
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user