diff --git a/.circleci/config.yml b/.circleci/config.yml index 372652ee4264..6dcb72899d0d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -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 diff --git a/.github/workflows/markdown-link-check-config.json b/.github/workflows/markdown-link-check-config.json index df6db06fff2e..8ae064afbbe4 100644 --- a/.github/workflows/markdown-link-check-config.json +++ b/.github/workflows/markdown-link-check-config.json @@ -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] } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 17b844fc5104..f8f01a839ea2 100644 --- a/CHANGELOG.md +++ b/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 diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 6ff77fef4875..28c946793682 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -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/))| diff --git a/MIGRATION.md b/MIGRATION.md index 32b3877ed32e..55d2d4a43fac 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,7 +1,9 @@

Migration

- [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` + + ## 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'; ``` + \ No newline at end of file diff --git a/README.md b/README.md index e4eefb52ce8a..d514f050ed93 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,8 @@

-[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. +

Storybook 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.

@@ -51,8 +51,9 @@ It allows you to browse a component library, view the different states of each c

View README for:
- latest - next + latest + next + future

## 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: +Here are some featured examples that you can reference to see how Storybook works: 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/*) | [![React](https://img.shields.io/npm/dm/@storybook/react.svg)](app/react) | -| [Vue](app/vue) | [v6.4.x](https://storybookjs.netlify.com/vue-kitchen-sink/) | [![Vue](https://img.shields.io/npm/dm/@storybook/vue.svg)](app/vue) | -| [Angular](app/angular) | [v6.4.x](https://storybookjs.netlify.com/angular-cli/) | [![Angular](https://img.shields.io/npm/dm/@storybook/angular.svg)](app/angular) | -| [Web components](app/web-components) | [v6.4.x](https://storybookjs.netlify.com/web-components-kitchen-sink/) | [![Svelte](https://img.shields.io/npm/dm/@storybook/web-components.svg)](app/web-components) | -| [React Native](https://github.com/storybookjs/react-native) | - | [![React Native](https://img.shields.io/npm/dm/@storybook/react-native.svg)](app/react-native) | -| [HTML](app/html) | [v6.4.x](https://storybookjs.netlify.com/html-kitchen-sink/) | [![HTML](https://img.shields.io/npm/dm/@storybook/html.svg)](app/html) | -| [Ember](app/ember) | [v6.4.x](https://storybookjs.netlify.com/ember-cli/) | [![Ember](https://img.shields.io/npm/dm/@storybook/ember.svg)](app/ember) | -| [Svelte](app/svelte) | [v6.4.x](https://storybookjs.netlify.com/svelte-kitchen-sink/) | [![Svelte](https://img.shields.io/npm/dm/@storybook/svelte.svg)](app/svelte) | -| [Preact](app/preact) | [v6.4.x](https://storybookjs.netlify.com/preact-kitchen-sink/) | [![Preact](https://img.shields.io/npm/dm/@storybook/preact.svg)](app/preact) | -| [Marionette.js](https://github.com/storybookjs/marionette) | - | [![Marionette.js](https://img.shields.io/npm/dm/@storybook/marionette.svg)](app/marionette) | -| [Android, iOS, Flutter](https://github.com/storybookjs/native) | [v6.4.x](https://storybookjs.github.io/native/@storybook/native-flutter-example/index.html) | [![Native](https://img.shields.io/npm/dm/@storybook/native.svg)](https://github.com/storybookjs/native) | +| Framework | Demo | | +| -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| [React](app/react) | [![Storybook demo](https://img.shields.io/npm/v/@storybook/react/latest?style=flat-square&color=blue&label)](https://storybookjs.netlify.com/official-storybook/?path=/story/*) | [![React](https://img.shields.io/npm/dm/@storybook/react?style=flat-square&color=eee)](app/react) | +| [Vue](app/vue) | [![Storybook demo](https://img.shields.io/npm/v/@storybook/vue/latest?style=flat-square&color=blue&label)](https://storybookjs.netlify.com/vue-kitchen-sink/) | [![Vue](https://img.shields.io/npm/dm/@storybook/vue?style=flat-square&color=eee)](app/vue) | +| [Angular](app/angular) | [![Storybook demo](https://img.shields.io/npm/v/@storybook/angular/latest?style=flat-square&color=blue&label)](https://storybookjs.netlify.com/angular-cli/) | [![Angular](https://img.shields.io/npm/dm/@storybook/angular?style=flat-square&color=eee)](app/angular) | +| [Web components](app/web-components) | [![Storybook demo](https://img.shields.io/npm/v/@storybook/web-components/latest?style=flat-square&color=blue&label)](https://storybookjs.netlify.com/web-components-kitchen-sink/) | [![Svelte](https://img.shields.io/npm/dm/@storybook/web-components?style=flat-square&color=eee)](app/web-components) | +| [React Native](https://github.com/storybookjs/react-native) | - | [![React Native](https://img.shields.io/npm/dm/@storybook/react-native?style=flat-square&color=eee)](https://github.com/storybookjs/react-native) | +| [HTML](app/html) | [![Storybook demo](https://img.shields.io/npm/v/@storybook/html/latest?style=flat-square&color=blue&label)](https://storybookjs.netlify.com/html-kitchen-sink/) | [![HTML](https://img.shields.io/npm/dm/@storybook/html?style=flat-square&color=eee)](app/html) | +| [Ember](app/ember) | [![Storybook demo](https://img.shields.io/npm/v/@storybook/ember/latest?style=flat-square&color=blue&label)](https://storybookjs.netlify.com/ember-cli/) | [![Ember](https://img.shields.io/npm/dm/@storybook/ember?style=flat-square&color=eee)](app/ember) | +| [Svelte](app/svelte) | [![Storybook demo](https://img.shields.io/npm/v/@storybook/svelte/latest?style=flat-square&color=blue&label)](https://storybookjs.netlify.com/svelte-kitchen-sink/) | [![Svelte](https://img.shields.io/npm/dm/@storybook/svelte?style=flat-square&color=eee)](app/svelte) | +| [Preact](app/preact) | [![Storybook demo](https://img.shields.io/npm/v/@storybook/preact/latest?style=flat-square&color=blue&label)](https://storybookjs.netlify.com/preact-kitchen-sink/) | [![Preact](https://img.shields.io/npm/dm/@storybook/preact?style=flat-square&color=eee)](app/preact) | +| [Marionette.js](https://github.com/storybookjs/marionette) | - | [![Marionette.js](https://img.shields.io/npm/dm/@storybook/marionette?style=flat-square&color=eee)](https://github.com/storybookjs/marionette) | +| [Android, iOS, Flutter](https://github.com/storybookjs/native) | [![Storybook demo](https://img.shields.io/npm/v/@storybook/native/latest?style=flat-square&color=blue&label)](https://storybookjs.github.io/native/@storybook/native-flutter-example/index.html) | [![Native](https://img.shields.io/npm/dm/@storybook/native?style=flat-square&color=eee)](https://github.com/storybookjs/native) | ### Sub Projects diff --git a/RELEASES.md b/RELEASES.md index 0d98bf5f8736..88557cf2fbeb 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -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/) [![Storybook Slack](https://now-examples-slackin-rrirkqohko.now.sh/badge.svg)](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 diff --git a/SECURITY.md b/SECURITY.md index 721021f54540..04845f5bf4a8 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -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 diff --git a/__mocks__/fs-extra.js b/__mocks__/fs-extra.js index 349ffb2b4c7f..ebe0d6c11d36 100644 --- a/__mocks__/fs-extra.js +++ b/__mocks__/fs-extra.js @@ -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; diff --git a/addons/a11y/package.json b/addons/a11y/package.json index a0e27f90aa24..0bf05819540a 100644 --- a/addons/a11y/package.json +++ b/addons/a11y/package.json @@ -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", diff --git a/addons/a11y/src/a11yRunner.ts b/addons/a11y/src/a11yRunner.ts index 73e97a6340cd..1f4fd7ddeab6 100644 --- a/addons/a11y/src/a11yRunner.ts +++ b/addons/a11y/src/a11yRunner.ts @@ -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, diff --git a/addons/a11y/src/components/A11YPanel.test.tsx b/addons/a11y/src/components/A11YPanel.test.tsx index 744e7e418b8b..7c366377b12d 100644 --- a/addons/a11y/src/components/A11YPanel.test.tsx +++ b/addons/a11y/src/components/A11YPanel.test.tsx @@ -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 = { storyId: 'jest' }; diff --git a/addons/a11y/src/components/A11yContext.test.tsx b/addons/a11y/src/components/A11yContext.test.tsx index 5d4e7003f149..a70ced0368a6 100644 --- a/addons/a11y/src/components/A11yContext.test.tsx +++ b/addons/a11y/src/components/A11yContext.test.tsx @@ -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 = { storyId }; + const storyState: Partial = { storyId }; // Lazy to mock entire state - mockedApi.useStorybookState.mockReturnValue(state as any); + mockedApi.useStorybookState.mockReturnValue(storyState as any); }); it('should render children', () => { diff --git a/addons/a11y/src/components/A11yContext.tsx b/addons/a11y/src/components/A11yContext.tsx index 5061f5623d47..723b4e8962c7 100644 --- a/addons/a11y/src/components/A11yContext.tsx +++ b/addons/a11y/src/components/A11yContext.tsx @@ -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({ @@ -52,7 +52,7 @@ const defaultResult = { }; export const A11yContextProvider: React.FC = ({ active, ...props }) => { - const [results, setResults] = React.useState(defaultResult); + const [results, setResults] = useAddonState(ADDON_ID, defaultResult); const [tab, setTab] = React.useState(0); const [highlighted, setHighlighted] = React.useState([]); const { storyId } = useStorybookState(); diff --git a/addons/a11y/src/components/VisionSimulator.test.tsx b/addons/a11y/src/components/VisionSimulator.test.tsx new file mode 100644 index 000000000000..9191e058db6f --- /dev/null +++ b/addons/a11y/src/components/VisionSimulator.test.tsx @@ -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 ( + + + + ); +} + +describe('Vision Simulator', () => { + it('should render tool button', async () => { + // when + render(); + + // 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(); + 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(); + 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)'); + }); +}); diff --git a/addons/a11y/src/components/VisionSimulator.tsx b/addons/a11y/src/components/VisionSimulator.tsx index f4fb24cafc18..050c9392ecaa 100644 --- a/addons/a11y/src/components/VisionSimulator.tsx +++ b/addons/a11y/src/components/VisionSimulator.tsx @@ -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; +} + +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[]; -type Filter = typeof baseList[number] | null; +type Filter = Option | null; -const getFilter = (filter: Filter) => { - if (!filter) { +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: , - active: active === i, - })), + ...baseList.map((i) => { + const description = i.percentage !== undefined ? `${i.percentage}% of users` : undefined; + return { + id: i.name, + title: ( + + {i.name} + {description && {description}} + + ), + onClick: () => { + set(i); + }, + right: , + active: active === i, + }; + }), ]; -export const VisionSimulator: FunctionComponent = () => { +export const VisionSimulator = () => { const [filter, setFilter] = useState(null); - return ( <> {filter && ( diff --git a/addons/a11y/src/manager.test.tsx b/addons/a11y/src/manager.test.tsx new file mode 100644 index 000000000000..92ef7866313d --- /dev/null +++ b/addons/a11y/src/manager.test.tsx @@ -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; +mockedApi.getAddonState = jest.fn(); +const mockedAddons = addons as jest.Mocked; +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)'); + }); +}); diff --git a/addons/a11y/src/manager.tsx b/addons/a11y/src/manager.tsx index 69cb77a7fbf4..ce24d1c9cafd 100644 --- a/addons/a11y/src/manager.tsx +++ b/addons/a11y/src/manager.tsx @@ -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 }) => ( diff --git a/addons/actions/package.json b/addons/actions/package.json index df2f68052584..a3e8624c7287 100644 --- a/addons/actions/package.json +++ b/addons/actions/package.json @@ -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", diff --git a/addons/backgrounds/package.json b/addons/backgrounds/package.json index bd1be9237aff..d4c6bb11b66d 100644 --- a/addons/backgrounds/package.json +++ b/addons/backgrounds/package.json @@ -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", diff --git a/addons/controls/package.json b/addons/controls/package.json index 30952506c613..e6446c0a5307 100644 --- a/addons/controls/package.json +++ b/addons/controls/package.json @@ -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", diff --git a/addons/docs/package.json b/addons/docs/package.json index c0d6aba3a730..e974e4ac8048 100644 --- a/addons/docs/package.json +++ b/addons/docs/package.json @@ -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", diff --git a/addons/docs/src/blocks/Canvas.tsx b/addons/docs/src/blocks/Canvas.tsx index bf5362857f0c..a1217e48939d 100644 --- a/addons/docs/src/blocks/Canvas.tsx +++ b/addons/docs/src/blocks/Canvas.tsx @@ -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 { diff --git a/addons/essentials/package.json b/addons/essentials/package.json index 2c214e0d2e57..c56311c67ffc 100644 --- a/addons/essentials/package.json +++ b/addons/essentials/package.json @@ -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" } diff --git a/addons/interactions/README.md b/addons/interactions/README.md index d3687e084f32..0cd9e62bc3a5 100644 --- a/addons/interactions/README.md +++ b/addons/interactions/README.md @@ -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) => +
+ {args.json &&
{JSON.stringify(args.json, null, 2)}
}
); diff --git a/examples/official-storybook/stories/core/args.stories.js b/examples/official-storybook/stories/core/args.stories.tsx similarity index 61% rename from examples/official-storybook/stories/core/args.stories.js rename to examples/official-storybook/stories/core/args.stories.tsx index 1a1bb9ee72a1..2242a1d1f023 100644 --- a/examples/official-storybook/stories/core/args.stories.js +++ b/examples/official-storybook/stories/core/args.stories.tsx @@ -1,8 +1,20 @@ -import React, { useState } from 'react'; +import React, { FC, useState } from 'react'; import { useArgs } from '@storybook/client-api'; -// eslint-disable-next-line react/prop-types -const ArgUpdater = ({ args, updateArgs, resetArgs }) => { +interface CustomArgs { + first?: string; + last?: string; + foo?: string; +} + +type UpdateArgs = ReturnType[1]; +type ResetArgs = ReturnType[2]; + +const ArgUpdater: FC<{ args: CustomArgs; updateArgs: UpdateArgs; resetArgs: ResetArgs }> = ({ + args, + updateArgs, + resetArgs, +}) => { const [argsInput, updateArgsInput] = useState(JSON.stringify(args)); return ( @@ -30,7 +42,7 @@ export default { title: 'Core/Args', decorators: [ (story) => { - const [args, updateArgs, resetArgs] = useArgs(); + const [args, updateArgs, resetArgs] = useArgs(); return ( <> @@ -63,4 +75,24 @@ export const DifferentSet = Template.bind({}); DifferentSet.args = { foo: 'bar', bar: 2, +} as CustomArgs; + +export const TestUndefinedArgs = Template.bind({}); +TestUndefinedArgs.args = { + first: 'Bob', + last: 'Miller', + foo: 'bar', +} as CustomArgs; +TestUndefinedArgs.argTypes = { + first: { + control: { type: 'select' }, + options: ['Bob', 'Alice'], + }, + last: { + control: { type: 'select' }, + options: ['Miller', 'Meyer'], + }, + foo: { + control: { type: 'text' }, + }, }; diff --git a/examples/official-storybook/stories/title/AutoTitle.stories.js b/examples/official-storybook/stories/title/AutoTitle.stories.js new file mode 100644 index 000000000000..125c1157c1c1 --- /dev/null +++ b/examples/official-storybook/stories/title/AutoTitle.stories.js @@ -0,0 +1,12 @@ +import React from 'react'; +import { Form } from '@storybook/components'; + +const { Button } = Form; + +export default { + // Title not needed due to CSF3 auto-title + // title: 'AutoTitle', + component: Button, +}; + +export const Basic = () => `; -exports[`Storyshots Docs/ButtonMdx Controls 1`] = ` +exports[`Storyshots Demo/Examples / Button CSF 2 Story With Play 1`] = ` `; -exports[`Storyshots Examples / Button Basic 1`] = ` +exports[`Storyshots Demo/Examples / Button Process Env 1`] = ` + +`; + +exports[`Storyshots Demo/Examples / Button Story No Render 1`] = ` + +`; + +exports[`Storyshots Demo/Examples / Button Story Object 1`] = `"hahaha"`; + +exports[`Storyshots Demo/Examples / Button Story With Play 1`] = ` + +`; + +exports[`Storyshots Demo/Examples / Button With Args 1`] = ` +`; + +exports[`Storyshots Demo/Examples / Emoji Button Basic 1`] = ` + `; -exports[`Storyshots Examples / Button CSF 2 Story With Play 1`] = ` +exports[`Storyshots Demo/Examples / Emoji Button With Args 1`] = ` `; -exports[`Storyshots Examples / Button Process Env 1`] = ` +exports[`Storyshots Demo/button2 One 1`] = ` `; -exports[`Storyshots Examples / Button Story No Render 1`] = ` +exports[`Storyshots Demo/button2 Three 1`] = ` `; -exports[`Storyshots Examples / Button Story Object 1`] = `"hahaha"`; +exports[`Storyshots Demo/button2 Two 1`] = ` + +`; -exports[`Storyshots Examples / Button Story With Play 1`] = ` +exports[`Storyshots Demo/button3 Five 1`] = ` `; -exports[`Storyshots Examples / Button With Args 1`] = ` +exports[`Storyshots Demo/button3 Four 1`] = ` `; -exports[`Storyshots Examples / Emoji Button Basic 1`] = ` +exports[`Storyshots Docs/ButtonMdx Basic 1`] = ` `; -exports[`Storyshots Examples / Emoji Button With Args 1`] = ` +exports[`Storyshots Docs/ButtonMdx Controls 1`] = ` `; diff --git a/examples/react-ts/src/button.stories.tsx b/examples/react-ts/src/button.stories.tsx index 2c9d8151b200..9a6162285409 100644 --- a/examples/react-ts/src/button.stories.tsx +++ b/examples/react-ts/src/button.stories.tsx @@ -7,10 +7,23 @@ import { screen } from '@testing-library/dom'; import userEvent from '@testing-library/user-event'; import { Button } from './button'; +const icons = { + foo: () => <>Foo, + bar: () => <>Bar, +}; + export default { component: Button, title: 'Examples / Button', - argTypes: { onClick: { action: 'click ' } }, + argTypes: { + onClick: { action: 'click ' }, + icon: { + description: 'An icon, displayed to the left of the title.', + control: { type: 'select' }, + options: Object.keys(icons), + mapping: icons, + }, + }, // render: () => <>hohoho, } as Meta; diff --git a/examples/react-ts/src/button.tsx b/examples/react-ts/src/button.tsx index f3c9ee6b83ea..714b5db4e0f6 100644 --- a/examples/react-ts/src/button.tsx +++ b/examples/react-ts/src/button.tsx @@ -1,14 +1,26 @@ -import React, { ButtonHTMLAttributes } from 'react'; +import React, { ComponentType, ButtonHTMLAttributes, useEffect } from 'react'; export interface ButtonProps extends ButtonHTMLAttributes { /** * A label to show on the button */ label: string; + + /** + * An icon to show on the left of the label + */ + icon?: ComponentType; } -export const Button = ({ label = 'Hello', ...props }: ButtonProps) => ( - -); +export const Button = ({ label = 'Hello', icon: Icon, ...props }: ButtonProps) => { + useEffect(() => { + const fn = () => console.log(`click ${label}`); + global.window.document.querySelector('body')?.addEventListener('click', fn); + return () => global.window.document.querySelector('body')?.removeEventListener('click', fn); + }); + return ( + + ); +}; diff --git a/examples/react-ts/src/button2.stories.tsx b/examples/react-ts/src/button2.stories.tsx new file mode 100644 index 000000000000..cfaa6df924d4 --- /dev/null +++ b/examples/react-ts/src/button2.stories.tsx @@ -0,0 +1,10 @@ +import { Button } from './button'; + +export default { + component: Button, + title: 'button2', +}; + +export const one = { args: { label: 'one' } }; +export const two = { args: { label: 'two' } }; +export const three = { args: { label: 'three' } }; diff --git a/examples/react-ts/src/button3.stories.tsx b/examples/react-ts/src/button3.stories.tsx new file mode 100644 index 000000000000..941d8f436f3b --- /dev/null +++ b/examples/react-ts/src/button3.stories.tsx @@ -0,0 +1,9 @@ +import { Button } from './button'; + +export default { + component: Button, + title: 'button3', +}; + +export const four = { args: { label: 'four' } }; +export const five = { args: { label: 'five' } }; diff --git a/examples/react-ts/src/title/AutoTitle.stories.js b/examples/react-ts/src/title/AutoTitle.stories.js new file mode 100644 index 000000000000..125c1157c1c1 --- /dev/null +++ b/examples/react-ts/src/title/AutoTitle.stories.js @@ -0,0 +1,12 @@ +import React from 'react'; +import { Form } from '@storybook/components'; + +const { Button } = Form; + +export default { + // Title not needed due to CSF3 auto-title + // title: 'AutoTitle', + component: Button, +}; + +export const Basic = () =>