diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fc347cfe32ef..bd9c892e3310 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -163,7 +163,7 @@ jobs: yarn release:ensure-next-ahead --main-version "${{ steps.version.outputs.current-version }}" git add .. - git commit -m "Bump next to be one minor ahead of main [skip ci]" + git diff --staged --quiet || git commit -m "Bump next to be one minor ahead of main [skip ci]" git push origin next - name: Sync CHANGELOG.md from `main` to `next` diff --git a/CHANGELOG.md b/CHANGELOG.md index 75cb12161dcd..e424acc666c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## 7.5.2 + +- Addon-themes: Fix globals not being set when using absolute path - [#24596](https://github.com/storybookjs/storybook/pull/24596), thanks [@JReinhold](https://github.com/JReinhold)! +- CLI: Allow Yarn v4 in `link` command - [#24551](https://github.com/storybookjs/storybook/pull/24551), thanks [@yannbf](https://github.com/yannbf)! +- Next.js: Support v14.0.0 - [#24593](https://github.com/storybookjs/storybook/pull/24593), thanks [@nikospapcom](https://github.com/nikospapcom)! + +## 7.5.1 + +- Angular: update wrong type for webpackStatsJson in start-storybook schema.json - [#24494](https://github.com/storybookjs/storybook/pull/24494), thanks [@LucaVazz](https://github.com/LucaVazz)! +- Themes: Run postinstall in shell for windows - [#24389](https://github.com/storybookjs/storybook/pull/24389), thanks [@Integrayshaun](https://github.com/Integrayshaun)! + ## 7.5.0 Storybook 7.5 enhances your Storybook experience with several key updates: diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md index 3ebcbefe91ba..5cc2087688c0 100644 --- a/CHANGELOG.prerelease.md +++ b/CHANGELOG.prerelease.md @@ -1,3 +1,21 @@ +## 7.6.0-alpha.4 + +- CLI: Ensure errors with opening the browser are caught - [#24668](https://github.com/storybookjs/storybook/pull/24668), thanks [@xueyawei](https://github.com/xueyawei)! +- Babel: Update all @babel/* dependencies - [#24610](https://github.com/storybookjs/storybook/pull/24610), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! +- CLI: Catch when prettier failed to prettify main and preview config files - [#24604](https://github.com/storybookjs/storybook/pull/24604), thanks [@kasperpeulen](https://github.com/kasperpeulen)! +- CLI: Ignore `addon-onboarding` when checking versions - [#24634](https://github.com/storybookjs/storybook/pull/24634), thanks [@JReinhold](https://github.com/JReinhold)! +- CLI: Use @storybook/test in template stories - [#24393](https://github.com/storybookjs/storybook/pull/24393), thanks [@yannbf](https://github.com/yannbf)! +- Controls: Improve accessibility of BooleanControl for screen readers - [#24418](https://github.com/storybookjs/storybook/pull/24418), thanks [@danielmarcano](https://github.com/danielmarcano)! +- Dependencies: Update @babel/traverse and @babel/core to fix vulnerability - [#24670](https://github.com/storybookjs/storybook/pull/24670), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! +- Dependencies: Update browserify-sign transitive dependency - [#24674](https://github.com/storybookjs/storybook/pull/24674), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! +- Dependencies: Update nx dependencies to v17 - [#24671](https://github.com/storybookjs/storybook/pull/24671), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! +- Maintenance: Split renderers preview entrypoints - [#24623](https://github.com/storybookjs/storybook/pull/24623), thanks [@ndelangen](https://github.com/ndelangen)! +- Next.js: Add avif support - [#24611](https://github.com/storybookjs/storybook/pull/24611), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! +- Next.js: Fix forwarding ref for Image component - [#24648](https://github.com/storybookjs/storybook/pull/24648), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! +- Theming: Add theme variable to set the preview background color - [#24575](https://github.com/storybookjs/storybook/pull/24575), thanks [@JReinhold](https://github.com/JReinhold)! +- UI: Fix button contrast-ratio - [#24525](https://github.com/storybookjs/storybook/pull/24525), thanks [@maheshchandra10](https://github.com/maheshchandra10)! +- UI: Update zIndex on NotificationList to fix the notification not being clickable in certain cases - [#24602](https://github.com/storybookjs/storybook/pull/24602), thanks [@yoshi2no](https://github.com/yoshi2no)! + ## 7.6.0-alpha.3 - Action: Attach spies on actions across stories when defined in meta - [#24451](https://github.com/storybookjs/storybook/pull/24451), thanks [@kasperpeulen](https://github.com/kasperpeulen)! diff --git a/RESOLUTIONS.md b/RESOLUTIONS.md index a21b14b5309f..88adfad30fa0 100644 --- a/RESOLUTIONS.md +++ b/RESOLUTIONS.md @@ -9,3 +9,7 @@ svelte-check@3.4.6 (bug: 3.5.x): Type issues ## code/ui/components/package.json overlayscrollbars@2.2.1 (bug: 2.3.x): The Scrollbar doesn't disappear anymore by default. It might has something to do with the `scrollbars.autoHideSuspend` option, which was introduced in 2.3.0. https://github.com/KingSora/OverlayScrollbars/blob/master/packages/overlayscrollbars/CHANGELOG.md#230 + +## code/package.json + +@babel/core@^7.23.2: Make sure we use the latest version of @babel/traverse, which is a dependency of @babel/core, since it contains a fix for a vulnerability: https://security.snyk.io/vuln/SNYK-JS-BABELTRAVERSE-5962462 diff --git a/code/.gitignore b/code/.gitignore new file mode 100644 index 000000000000..0cba03f65076 --- /dev/null +++ b/code/.gitignore @@ -0,0 +1 @@ +.nx/cache \ No newline at end of file diff --git a/code/.prettierignore b/code/.prettierignore index 1b9355bd30fa..78c3a20024a4 100644 --- a/code/.prettierignore +++ b/code/.prettierignore @@ -1 +1,3 @@ *.mdx + +/.nx/cache \ No newline at end of file diff --git a/code/builders/builder-webpack5/package.json b/code/builders/builder-webpack5/package.json index cd2e563f88f3..d0a5affdbb26 100644 --- a/code/builders/builder-webpack5/package.json +++ b/code/builders/builder-webpack5/package.json @@ -56,7 +56,7 @@ "prep": "../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@babel/core": "^7.22.0", + "@babel/core": "^7.23.2", "@storybook/channels": "workspace:*", "@storybook/client-logger": "workspace:*", "@storybook/core-common": "workspace:*", diff --git a/code/e2e-tests/addon-interactions.spec.ts b/code/e2e-tests/addon-interactions.spec.ts index 981c5bf678a6..d77ef67beeca 100644 --- a/code/e2e-tests/addon-interactions.spec.ts +++ b/code/e2e-tests/addon-interactions.spec.ts @@ -29,7 +29,7 @@ test.describe('addon-interactions', () => { await expect(welcome).toContainText('Welcome, Jane Doe!'); const interactionsTab = await page.locator('#tabbutton-storybook-interactions-panel'); - await expect(interactionsTab).toContainText(/(1)/); + await expect(interactionsTab).toContainText(/(\d)/); await expect(interactionsTab).toBeVisible(); const panel = sbPage.panelContent(); @@ -37,7 +37,7 @@ test.describe('addon-interactions', () => { await expect(panel).toContainText(/userEvent.click/); await expect(panel).toBeVisible(); - const done = await panel.locator('[data-testid=icon-done]'); + const done = await panel.locator('[data-testid=icon-done]').nth(0); await expect(done).toBeVisible(); }); diff --git a/code/frameworks/angular/package.json b/code/frameworks/angular/package.json index 92ec2c0fe365..f53ac7a94f51 100644 --- a/code/frameworks/angular/package.json +++ b/code/frameworks/angular/package.json @@ -81,7 +81,7 @@ "@types/cross-spawn": "^6.0.2", "@types/tmp": "^0.2.3", "cross-spawn": "^7.0.3", - "jest": "^29.3.1", + "jest": "^29.7.0", "jest-preset-angular": "^13.0.1", "jest-specific-snapshot": "^8.0.0", "tmp": "^0.2.1", diff --git a/code/frameworks/angular/template/cli/Page.stories.ts b/code/frameworks/angular/template/cli/Page.stories.ts index e7f3c0e9a3b6..a21cdf7d8304 100644 --- a/code/frameworks/angular/template/cli/Page.stories.ts +++ b/code/frameworks/angular/template/cli/Page.stories.ts @@ -1,7 +1,7 @@ import type { Meta, StoryObj } from '@storybook/angular'; import { moduleMetadata } from '@storybook/angular'; -import { within, userEvent } from '@storybook/testing-library'; import { CommonModule } from '@angular/common'; +import { within, userEvent, expect } from '@storybook/test'; import Button from './button.component'; import Header from './header.component'; @@ -38,9 +38,12 @@ export const LoggedIn: Story = { }), play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const loginButton = await canvas.getByRole('button', { - name: /Log in/i, - }); + const loginButton = canvas.getByRole('button', { name: /Log in/i }); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); + await expect(loginButton).not.toBeInTheDocument(); + + const logoutButton = canvas.getByRole('button', { name: /Log out/i }); + await expect(logoutButton).toBeInTheDocument(); }, }; diff --git a/code/frameworks/nextjs/README.md b/code/frameworks/nextjs/README.md index d2549a159c26..03cc1cbebc62 100644 --- a/code/frameworks/nextjs/README.md +++ b/code/frameworks/nextjs/README.md @@ -14,7 +14,6 @@ - [Next.js's Image Component](#nextjss-image-component) - [Local Images](#local-images) - [Remote Images](#remote-images) - - [AVIF](#avif) - [Next.js Font Optimization](#nextjs-font-optimization) - [next/font/google](#nextfontgoogle) - [next/font/local](#nextfontlocal) @@ -220,10 +219,6 @@ export default function Home() { } ``` -#### AVIF - -This format is not supported by this framework yet. Feel free to [open up an issue](https://github.com/storybookjs/storybook/issues) if this is something you want to see. - ### Next.js Font Optimization [next/font](https://nextjs.org/docs/basic-features/font-optimization) is partially supported in Storybook. The packages `next/font/google` and `next/font/local` are supported. diff --git a/code/frameworks/nextjs/package.json b/code/frameworks/nextjs/package.json index 6864ce99e742..373f512b4efa 100644 --- a/code/frameworks/nextjs/package.json +++ b/code/frameworks/nextjs/package.json @@ -74,7 +74,7 @@ "prep": "../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@babel/core": "^7.22.9", + "@babel/core": "^7.23.2", "@babel/plugin-syntax-bigint": "^7.8.3", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-import-assertions": "^7.22.5", @@ -82,11 +82,11 @@ "@babel/plugin-transform-export-namespace-from": "^7.22.11", "@babel/plugin-transform-numeric-separator": "^7.22.11", "@babel/plugin-transform-object-rest-spread": "^7.22.15", - "@babel/plugin-transform-runtime": "^7.22.9", - "@babel/preset-env": "^7.22.9", - "@babel/preset-react": "^7.22.5", - "@babel/preset-typescript": "^7.22.5", - "@babel/runtime": "^7.22.6", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-react": "^7.22.15", + "@babel/preset-typescript": "^7.23.2", + "@babel/runtime": "^7.23.2", "@storybook/addon-actions": "workspace:*", "@storybook/builder-webpack5": "workspace:*", "@storybook/core-common": "workspace:*", @@ -100,7 +100,7 @@ "find-up": "^5.0.0", "fs-extra": "^11.1.0", "image-size": "^1.0.0", - "loader-utils": "^3.2.0", + "loader-utils": "^3.2.1", "node-polyfill-webpack-plugin": "^2.0.1", "pnp-webpack-plugin": "^1.7.0", "postcss": "^8.4.21", @@ -108,6 +108,7 @@ "resolve-url-loader": "^5.0.0", "sass-loader": "^12.4.0", "semver": "^7.3.5", + "sharp": "^0.32.6", "style-loader": "^3.3.1", "styled-jsx": "5.1.1", "ts-dedent": "^2.0.0", @@ -115,10 +116,11 @@ "tsconfig-paths-webpack-plugin": "^4.0.1" }, "devDependencies": { - "@babel/types": "^7.22.5", + "@babel/types": "^7.23.0", "@types/babel__core": "^7", "@types/babel__plugin-transform-runtime": "^7", "@types/babel__preset-env": "^7", + "@types/loader-utils": "^2.0.5", "next": "^14.0.0", "typescript": "^4.9.3", "webpack": "^5.65.0" diff --git a/code/frameworks/nextjs/src/images/next-image.tsx b/code/frameworks/nextjs/src/images/next-image.tsx index 8fc964785f6b..f2e2d9d1467c 100644 --- a/code/frameworks/nextjs/src/images/next-image.tsx +++ b/code/frameworks/nextjs/src/images/next-image.tsx @@ -13,10 +13,21 @@ import { defaultLoader } from './next-image-default-loader'; const ImageContext = ImageContextValue as typeof ImageContextType; -const MockedNextImage = ({ loader, ...props }: _NextImage.ImageProps) => { - const imageParameters = React.useContext(ImageContext); +const MockedNextImage = React.forwardRef( + ({ loader, ...props }, ref) => { + const imageParameters = React.useContext(ImageContext); - return ; -}; + return ( + + ); + } +); + +MockedNextImage.displayName = 'NextImage'; export default MockedNextImage; diff --git a/code/frameworks/nextjs/src/next-image-loader-stub.ts b/code/frameworks/nextjs/src/next-image-loader-stub.ts index 04a97fdead81..6f69e8e274d3 100644 --- a/code/frameworks/nextjs/src/next-image-loader-stub.ts +++ b/code/frameworks/nextjs/src/next-image-loader-stub.ts @@ -1,29 +1,52 @@ -// @ts-expect-error (loader-utils has no webpack5 compatible types) import { interpolateName } from 'loader-utils'; import imageSizeOf from 'image-size'; import type { RawLoaderDefinition } from 'webpack'; import type { NextConfig } from 'next'; +import sharp from 'sharp'; +import { cpus } from 'os'; interface LoaderOptions { filename: string; nextConfig: NextConfig; } -const nextImageLoaderStub: RawLoaderDefinition = function (content) { +if (sharp.concurrency() > 1) { + // Reducing concurrency reduces the memory usage too. + const divisor = process.env.NODE_ENV === 'development' ? 4 : 2; + sharp.concurrency(Math.floor(Math.max(cpus().length / divisor, 1))); +} + +const nextImageLoaderStub: RawLoaderDefinition = async function NextImageLoader( + content +) { const { filename, nextConfig } = this.getOptions(); - const outputPath = interpolateName(this, filename.replace('[ext]', '.[ext]'), { + const opts = { context: this.rootContext, content, - }); + }; + const outputPath = interpolateName(this, filename.replace('[ext]', '.[ext]'), opts); + const extension = interpolateName(this, '[ext]', opts); this.emitFile(outputPath, content); - const { width, height } = imageSizeOf(this.resourcePath); - if (nextConfig.images?.disableStaticImages) { return `const src = '${outputPath}'; export default src;`; } + let width; + let height; + + if (extension === 'avif') { + const transformer = sharp(content); + const result = await transformer.metadata(); + width = result.width; + height = result.height; + } else { + const result = imageSizeOf(this.resourcePath); + width = result.width; + height = result.height; + } + return `export default ${JSON.stringify({ src: outputPath, height, diff --git a/code/frameworks/nextjs/template/cli/js/Page.stories.js b/code/frameworks/nextjs/template/cli/js/Page.stories.js index 5bd1d6eba765..f339fa246d39 100644 --- a/code/frameworks/nextjs/template/cli/js/Page.stories.js +++ b/code/frameworks/nextjs/template/cli/js/Page.stories.js @@ -1,4 +1,4 @@ -import { within, userEvent } from '@storybook/testing-library'; +import { within, userEvent, expect } from '@storybook/test'; import { Page } from './Page'; export default { @@ -16,9 +16,12 @@ export const LoggedOut = {}; export const LoggedIn = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const loginButton = await canvas.getByRole('button', { - name: /Log in/i, - }); + const loginButton = canvas.getByRole('button', { name: /Log in/i }); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); + await expect(loginButton).not.toBeInTheDocument(); + + const logoutButton = canvas.getByRole('button', { name: /Log out/i }); + await expect(logoutButton).toBeInTheDocument(); }, }; diff --git a/code/frameworks/nextjs/template/cli/ts-3-8/Page.stories.ts b/code/frameworks/nextjs/template/cli/ts-3-8/Page.stories.ts index 0e48941ab149..c072a1cc43ed 100644 --- a/code/frameworks/nextjs/template/cli/ts-3-8/Page.stories.ts +++ b/code/frameworks/nextjs/template/cli/ts-3-8/Page.stories.ts @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { within, userEvent } from '@storybook/testing-library'; +import { within, userEvent, expect } from '@storybook/test'; import { Page } from './Page'; @@ -21,9 +21,12 @@ export const LoggedOut: Story = {}; export const LoggedIn: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const loginButton = await canvas.getByRole('button', { - name: /Log in/i, - }); + const loginButton = canvas.getByRole('button', { name: /Log in/i }); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); + await expect(loginButton).not.toBeInTheDocument(); + + const logoutButton = canvas.getByRole('button', { name: /Log out/i }); + await expect(logoutButton).toBeInTheDocument(); }, }; diff --git a/code/frameworks/nextjs/template/cli/ts-4-9/Page.stories.ts b/code/frameworks/nextjs/template/cli/ts-4-9/Page.stories.ts index 69852a2bdc00..622858e72e7f 100644 --- a/code/frameworks/nextjs/template/cli/ts-4-9/Page.stories.ts +++ b/code/frameworks/nextjs/template/cli/ts-4-9/Page.stories.ts @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { within, userEvent } from '@storybook/testing-library'; +import { within, userEvent, expect } from '@storybook/test'; import { Page } from './Page'; @@ -21,9 +21,12 @@ export const LoggedOut: Story = {}; export const LoggedIn: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const loginButton = await canvas.getByRole('button', { - name: /Log in/i, - }); + const loginButton = canvas.getByRole('button', { name: /Log in/i }); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); + await expect(loginButton).not.toBeInTheDocument(); + + const logoutButton = canvas.getByRole('button', { name: /Log out/i }); + await expect(logoutButton).toBeInTheDocument(); }, }; diff --git a/code/frameworks/nextjs/template/stories/Image.stories.jsx b/code/frameworks/nextjs/template/stories/Image.stories.jsx index 7a8803a6e992..8fa4f6a53de1 100644 --- a/code/frameworks/nextjs/template/stories/Image.stories.jsx +++ b/code/frameworks/nextjs/template/stories/Image.stories.jsx @@ -1,8 +1,9 @@ -import React from 'react'; +import React, { useRef, useState } from 'react'; import Image from 'next/image'; import { waitFor } from '@storybook/testing-library'; import Accessibility from '../../assets/accessibility.svg'; +import AvifImage from '../../assets/avif-test-image.avif'; export default { component: Image, @@ -14,6 +15,13 @@ export default { export const Default = {}; +export const Avif = { + args: { + src: AvifImage, + alt: 'Avif Test Image', + }, +}; + export const BlurredPlaceholder = { args: { placeholder: 'blur', @@ -81,3 +89,16 @@ export const Eager = { }, }, }; + +export const WithRef = { + render() { + const [ref, setRef] = useState(null); + + return ( +
+ Accessibility +

Alt attribute of image: {ref?.alt}

+
+ ); + }, +}; diff --git a/code/frameworks/web-components-webpack5/package.json b/code/frameworks/web-components-webpack5/package.json index 60ebe5a5077f..21244cdbc78d 100644 --- a/code/frameworks/web-components-webpack5/package.json +++ b/code/frameworks/web-components-webpack5/package.json @@ -50,7 +50,7 @@ "prep": "../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@babel/preset-env": "^7.22.9", + "@babel/preset-env": "^7.23.2", "@storybook/builder-webpack5": "workspace:*", "@storybook/core-common": "workspace:*", "@storybook/preset-web-components-webpack": "workspace:*", diff --git a/code/lib/cli/package.json b/code/lib/cli/package.json index 222709055082..9a74701a677e 100644 --- a/code/lib/cli/package.json +++ b/code/lib/cli/package.json @@ -55,9 +55,9 @@ "test": "jest test/**/*.test.js" }, "dependencies": { - "@babel/core": "^7.22.9", - "@babel/preset-env": "^7.22.9", - "@babel/types": "^7.22.5", + "@babel/core": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/types": "^7.23.0", "@ndelangen/get-tarball": "^3.0.7", "@storybook/codemod": "workspace:*", "@storybook/core-common": "workspace:*", diff --git a/code/lib/cli/rendererAssets/common/assets/avif-test-image.avif b/code/lib/cli/rendererAssets/common/assets/avif-test-image.avif new file mode 100644 index 000000000000..530709bc1217 Binary files /dev/null and b/code/lib/cli/rendererAssets/common/assets/avif-test-image.avif differ diff --git a/code/lib/cli/src/generators/baseGenerator.ts b/code/lib/cli/src/generators/baseGenerator.ts index 48b2f8affd5b..a7bdb1764668 100644 --- a/code/lib/cli/src/generators/baseGenerator.ts +++ b/code/lib/cli/src/generators/baseGenerator.ts @@ -251,7 +251,14 @@ export async function baseGenerator( if (hasInteractiveStories(rendererId)) { addons.push('@storybook/addon-interactions'); - addonPackages.push('@storybook/addon-interactions', '@storybook/testing-library@^0.2.0-next.0'); + addonPackages.push('@storybook/addon-interactions'); + + // TODO: migrate template stories in solid and qwik to use @storybook/test + if (['solid', 'qwik'].includes(rendererId)) { + addonPackages.push('@storybook/testing-library'); + } else { + addonPackages.push('@storybook/test'); + } } const files = await fse.readdir(process.cwd()); diff --git a/code/lib/cli/src/generators/configure.ts b/code/lib/cli/src/generators/configure.ts index bc70a8f2bfa2..bbc9992be1ed 100644 --- a/code/lib/cli/src/generators/configure.ts +++ b/code/lib/cli/src/generators/configure.ts @@ -1,6 +1,7 @@ import fse from 'fs-extra'; import path from 'path'; import { dedent } from 'ts-dedent'; +import { logger } from '@storybook/node-logger'; import { externalFrameworks, SupportedLanguage } from '../project_types'; interface ConfigureMainOptions { @@ -33,8 +34,6 @@ interface ConfigurePreviewOptions { rendererId: string; } -const logger = console; - /** * We need to clean up the paths in case of pnp * input: "path.dirname(require.resolve(path.join('@storybook/react-webpack5', 'package.json')))" @@ -96,20 +95,25 @@ export async function configureMain({ finalPrefixes.push(`/** @type { import('${frameworkPackage}').StorybookConfig } */`); } - const mainJsContents = mainConfigTemplate + let mainJsContents = mainConfigTemplate .replace('<>', `${imports.join('\n\n')}\n\n`) .replace('<>', finalPrefixes.length > 0 ? `${finalPrefixes.join('\n\n')}\n` : '') .replace('<>', isTypescript ? ': StorybookConfig' : '') .replace('<>', mainContents); - const prettier = (await import('prettier')).default; - const mainPath = `./${storybookConfigFolder}/main.${isTypescript ? 'ts' : 'js'}`; - const prettyMain = prettier.format(dedent(mainJsContents), { - ...prettier.resolveConfig.sync(process.cwd()), - filepath: mainPath, - }); - await fse.writeFile(mainPath, prettyMain, { encoding: 'utf8' }); + + try { + const prettier = (await import('prettier')).default; + mainJsContents = prettier.format(dedent(mainJsContents), { + ...prettier.resolveConfig.sync(process.cwd()), + filepath: mainPath, + }); + } catch { + logger.verbose(`Failed to prettify ${mainPath}`); + } + + await fse.writeFile(mainPath, mainJsContents, { encoding: 'utf8' }); } export async function configurePreview(options: ConfigurePreviewOptions) { @@ -140,7 +144,7 @@ export async function configurePreview(options: ConfigurePreviewOptions) { .filter(Boolean) .join('\n'); - const preview = dedent` + let preview = dedent` ${prefix}${prefix.length > 0 ? '\n' : ''} ${ !isTypescript && rendererPackage @@ -163,11 +167,15 @@ export async function configurePreview(options: ConfigurePreviewOptions) { .replace(' \n', '') .trim(); - const prettier = (await import('prettier')).default; + try { + const prettier = (await import('prettier')).default; + preview = prettier.format(preview, { + ...prettier.resolveConfig.sync(process.cwd()), + filepath: previewPath, + }); + } catch { + logger.verbose(`Failed to prettify ${previewPath}`); + } - const prettyPreview = prettier.format(preview, { - ...prettier.resolveConfig.sync(process.cwd()), - filepath: previewPath, - }); - await fse.writeFile(previewPath, prettyPreview, { encoding: 'utf8' }); + await fse.writeFile(previewPath, preview, { encoding: 'utf8' }); } diff --git a/code/lib/cli/src/utils.ts b/code/lib/cli/src/utils.ts index 119debec9fb7..fcd861860992 100644 --- a/code/lib/cli/src/utils.ts +++ b/code/lib/cli/src/utils.ts @@ -101,6 +101,7 @@ export const createLogStream = async ( const PACKAGES_EXCLUDED_FROM_CORE = [ '@storybook/addon-bench', '@storybook/addon-console', + '@storybook/addon-onboarding', '@storybook/addon-postcss', '@storybook/addon-designs', '@storybook/addon-styling', diff --git a/code/lib/codemod/package.json b/code/lib/codemod/package.json index c08d402dbe0f..096604690c64 100644 --- a/code/lib/codemod/package.json +++ b/code/lib/codemod/package.json @@ -52,9 +52,9 @@ "prep": "../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@babel/core": "^7.22.9", - "@babel/preset-env": "^7.22.9", - "@babel/types": "^7.22.5", + "@babel/core": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/types": "^7.23.0", "@storybook/csf": "^0.1.0", "@storybook/csf-tools": "workspace:*", "@storybook/node-logger": "workspace:*", @@ -70,7 +70,7 @@ "devDependencies": { "@types/jscodeshift": "^0.11.6", "ansi-regex": "^5.0.1", - "jest": "^29.3.1", + "jest": "^29.7.0", "jest-specific-snapshot": "^8.0.0", "mdast-util-mdx-jsx": "^2.1.2", "mdast-util-mdxjs-esm": "^1.3.1", diff --git a/code/lib/core-server/src/presets/common-preset.ts b/code/lib/core-server/src/presets/common-preset.ts index 39511aef6be4..8d2ac3b7e2d4 100644 --- a/code/lib/core-server/src/presets/common-preset.ts +++ b/code/lib/core-server/src/presets/common-preset.ts @@ -225,10 +225,13 @@ export const frameworkOptions = async ( export const docs = ( docsOptions: StorybookConfig['docs'], { docs: docsMode }: CLIOptions -): StorybookConfig['docs'] => ({ - ...docsOptions, - docsMode, -}); +): StorybookConfig['docs'] => + docsOptions && docsMode !== undefined + ? { + ...docsOptions, + docsMode, + } + : docsOptions; export const managerHead = async (_: any, options: Options) => { const location = join(options.configDir, 'manager-head.html'); diff --git a/code/lib/core-server/src/utils/open-in-browser.ts b/code/lib/core-server/src/utils/open-in-browser.ts index f7e7be3d5a70..ef798cd5242f 100644 --- a/code/lib/core-server/src/utils/open-in-browser.ts +++ b/code/lib/core-server/src/utils/open-in-browser.ts @@ -25,7 +25,7 @@ export function openInBrowser(address: string) { ) { // We use betterOpn for Chrome because it is better at handling which chrome tab // or window the preview loads in. - betterOpn(address); + await betterOpn(address); } else { await open(address, openOptions); } diff --git a/code/lib/csf-tools/package.json b/code/lib/csf-tools/package.json index 9d2726df0e4c..d6192a65472c 100644 --- a/code/lib/csf-tools/package.json +++ b/code/lib/csf-tools/package.json @@ -42,10 +42,10 @@ "prep": "../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@babel/generator": "^7.22.9", - "@babel/parser": "^7.22.7", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", + "@babel/generator": "^7.23.0", + "@babel/parser": "^7.23.0", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0", "@storybook/csf": "^0.1.0", "@storybook/types": "workspace:*", "fs-extra": "^11.1.0", diff --git a/code/lib/docs-tools/package.json b/code/lib/docs-tools/package.json index 338d9c1986f0..b017a4651ce0 100644 --- a/code/lib/docs-tools/package.json +++ b/code/lib/docs-tools/package.json @@ -52,7 +52,7 @@ "lodash": "^4.17.21" }, "devDependencies": { - "@babel/core": "^7.22.9", + "@babel/core": "^7.23.2", "jest-specific-snapshot": "^8.0.0", "require-from-string": "^2.0.2", "typescript": "~4.9.3" diff --git a/code/lib/postinstall/package.json b/code/lib/postinstall/package.json index 72c7604a46fd..1be31fad2e25 100644 --- a/code/lib/postinstall/package.json +++ b/code/lib/postinstall/package.json @@ -45,7 +45,7 @@ "prep": "../../../scripts/prepare/bundle.ts" }, "devDependencies": { - "jest": "^29.3.1", + "jest": "^29.7.0", "jest-specific-snapshot": "^8.0.0", "jscodeshift": "^0.14.0", "typescript": "~4.9.3" diff --git a/code/lib/preview-api/template/stories/args.stories.ts b/code/lib/preview-api/template/stories/args.stories.ts index aa9719c4bf2c..6d397e108c5c 100644 --- a/code/lib/preview-api/template/stories/args.stories.ts +++ b/code/lib/preview-api/template/stories/args.stories.ts @@ -2,7 +2,7 @@ import { global as globalThis } from '@storybook/global'; import type { PartialStoryFn, PlayFunctionContext, StoryContext } from '@storybook/types'; import { within } from '@storybook/testing-library'; import { expect } from '@storybook/jest'; -import { pick } from 'lodash'; +import pick from 'lodash/pick'; import { STORY_ARGS_UPDATED, UPDATE_STORY_ARGS, RESET_STORY_ARGS } from '@storybook/core-events'; export default { diff --git a/code/lib/test/README.md b/code/lib/test/README.md new file mode 100644 index 000000000000..9c28efa303bd --- /dev/null +++ b/code/lib/test/README.md @@ -0,0 +1,41 @@ +# Storybook Test + +The `@storybook/test` package contains utilities for testing your stories inside `play` functions. + +## Installation + +Install the package by adding the `@storybook/test` dev dependency: + +```sh +npm install -D @storybook/test +pnpm add -D @storybook/test +yarn add -D @storybook/test +``` + +Note that this package is not an addon, so you don't have to add it to your `main.js/main.ts` file. + +## Usage + +The test package exports instrumented versions of [@vitest/spy](https://vitest.dev/api/mock.html), [@vitest/expect](https://vitest.dev/api/expect.html) (based on [chai](https://www.chaijs.com/)), [@testing-library/dom](https://testing-library.com/docs/dom-testing-library/intro) and [@testing-library/user-event](https://testing-library.com/docs/user-event/intro). +The instrumentation makes sure you can debug those methods in the [addon-interactions](https://storybook.js.org/addons/@storybook/addon-interactions) panel. + +```ts +// Button.stories.ts +import { Button } from './Button'; +import { within, userEvent, expect, fn } from '@storybook/test'; + +export default { + component: Button, + args: { + onClick: fn(), + }, +}; + +export const Demo = { + play: async ({ args, canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click(canvas.getByRole('button')); + await expect(args.onClick).toHaveBeenCalled(); + }, +}; +``` diff --git a/code/lib/test/src/expect.ts b/code/lib/test/src/expect.ts index d277380a3596..7e8b9d95cf03 100644 --- a/code/lib/test/src/expect.ts +++ b/code/lib/test/src/expect.ts @@ -15,13 +15,13 @@ import { setState, } from '@vitest/expect'; import * as matchers from '@testing-library/jest-dom/matchers'; -import type { TestingLibraryMatchers } from '@testing-library/jest-dom/types/matchers'; import type { PromisifyObject } from './utils'; +type Matchers = PromisifyObject> & + matchers.TestingLibraryMatchers, Promise>; + // We only expose the jest compatible API for now -export interface Assertion - extends PromisifyObject>, - TestingLibraryMatchers, Promise> { +export interface Assertion extends Matchers { toHaveBeenCalledOnce(): Promise; toSatisfy(matcher: (value: E) => boolean, message?: string): Promise; resolves: Assertion; diff --git a/code/lib/theming/src/base.ts b/code/lib/theming/src/base.ts index 5f60c0e812df..a322d46470a4 100644 --- a/code/lib/theming/src/base.ts +++ b/code/lib/theming/src/base.ts @@ -47,6 +47,7 @@ export const background = { app: '#F6F9FC', bar: color.lightest, content: color.lightest, + preview: color.lightest, gridCellSize: 10, hoverable: transparentize(0.9, color.secondary), // hover state for items in a list diff --git a/code/lib/theming/src/convert.ts b/code/lib/theming/src/convert.ts index 05ecaaf1f322..fc23399d4a38 100644 --- a/code/lib/theming/src/convert.ts +++ b/code/lib/theming/src/convert.ts @@ -81,6 +81,7 @@ export const convert = (inherit: ThemeVars = themes[getPreferredColorScheme()]): colorSecondary, appBg, appContentBg, + appPreviewBg, appBorderColor, appBorderRadius, fontBase, @@ -116,6 +117,7 @@ export const convert = (inherit: ThemeVars = themes[getPreferredColorScheme()]): app: appBg, bar: barBg, content: appContentBg, + preview: appPreviewBg, gridCellSize: gridCellSize || background.gridCellSize, hoverable: background.hoverable, positive: background.positive, diff --git a/code/lib/theming/src/themes/dark.ts b/code/lib/theming/src/themes/dark.ts index 49fea0807c25..4cb8f19f29bd 100644 --- a/code/lib/theming/src/themes/dark.ts +++ b/code/lib/theming/src/themes/dark.ts @@ -11,6 +11,7 @@ const theme: ThemeVars = { // UI appBg: '#222425', appContentBg: '#1B1C1D', + appPreviewBg: color.lightest, appBorderColor: 'rgba(255,255,255,.1)', appBorderRadius: 4, diff --git a/code/lib/theming/src/themes/light.ts b/code/lib/theming/src/themes/light.ts index 88508b62b8c4..75d83f1e740a 100644 --- a/code/lib/theming/src/themes/light.ts +++ b/code/lib/theming/src/themes/light.ts @@ -11,6 +11,7 @@ const theme: ThemeVars = { // UI appBg: background.app, appContentBg: color.lightest, + appPreviewBg: color.lightest, appBorderColor: color.border, appBorderRadius: 4, @@ -21,7 +22,7 @@ const theme: ThemeVars = { // Text colors textColor: color.darkest, textInverseColor: color.lightest, - textMutedColor: color.mediumdark, + textMutedColor: color.dark, // Toolbar default and active colors barTextColor: color.mediumdark, diff --git a/code/lib/theming/src/types.ts b/code/lib/theming/src/types.ts index b6f28f8c955f..e7c2827792cb 100644 --- a/code/lib/theming/src/types.ts +++ b/code/lib/theming/src/types.ts @@ -17,6 +17,7 @@ export interface ThemeVarsColors { // UI appBg: string; appContentBg: string; + appPreviewBg: string; appBorderColor: string; appBorderRadius: number; diff --git a/code/migrations.json b/code/migrations.json new file mode 100644 index 000000000000..6a886c649dcc --- /dev/null +++ b/code/migrations.json @@ -0,0 +1,43 @@ +{ + "migrations": [ + { + "cli": "nx", + "version": "16.6.0-beta.6", + "description": "Prefix outputs with {workspaceRoot}/{projectRoot} if needed", + "implementation": "./src/migrations/update-15-0-0/prefix-outputs", + "package": "nx", + "name": "16.6.0-prefix-outputs" + }, + { + "cli": "nx", + "version": "16.8.0-beta.3", + "description": "Escape $ in env variables", + "implementation": "./src/migrations/update-16-8-0/escape-dollar-sign-env-variables", + "package": "nx", + "name": "16.8.0-escape-dollar-sign-env" + }, + { + "cli": "nx", + "version": "17.0.0-beta.1", + "description": "Updates the default cache directory to .nx/cache", + "implementation": "./src/migrations/update-17-0-0/move-cache-directory", + "package": "nx", + "name": "17.0.0-move-cache-directory" + }, + { + "cli": "nx", + "version": "17.0.0-beta.3", + "description": "Use minimal config for tasksRunnerOptions", + "implementation": "./src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options", + "package": "nx", + "name": "17.0.0-use-minimal-config-for-tasks-runner-options" + }, + { + "version": "17.0.0-rc.1", + "description": "Migration for v17.0.0-rc.1", + "implementation": "./src/migrations/update-17-0-0/rm-default-collection-npm-scope", + "package": "nx", + "name": "rm-default-collection-npm-scope" + } + ] +} diff --git a/code/nx.json b/code/nx.json index 40580396505c..ad6c9a817fe7 100644 --- a/code/nx.json +++ b/code/nx.json @@ -1,6 +1,5 @@ { "$schema": "./node_modules/nx/schemas/nx-schema.json", - "npmScope": "storybook", "implicitDependencies": { "package.json": { "dependencies": "*", @@ -14,13 +13,9 @@ }, "tasksRunnerOptions": { "default": { - "runner": "nx-cloud", "options": { - "cacheableOperations": ["prep"], - "accessToken": "NGVmYTkxMmItYzY3OS00MjkxLTk1ZDktZDFmYTFmNmVlNGY4fHJlYWQ=", "canTrackAnalytics": false, - "showUsageWarnings": true, - "parallel": 1 + "showUsageWarnings": true } } }, @@ -52,7 +47,10 @@ "dependencies": true } ], - "outputs": ["{projectRoot}/dist"] + "outputs": ["{projectRoot}/dist"], + "cache": true } - } + }, + "nxCloudAccessToken": "NGVmYTkxMmItYzY3OS00MjkxLTk1ZDktZDFmYTFmNmVlNGY4fHJlYWQ=", + "parallel": 1 } diff --git a/code/package.json b/code/package.json index 999d0928f6c4..d5aac6137349 100644 --- a/code/package.json +++ b/code/package.json @@ -79,7 +79,9 @@ "defaults" ], "resolutions": { + "@babel/core": "^7.23.2", "@playwright/test": "1.36.0", + "@testing-library/jest-dom/aria-query": "5.1.3", "@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/experimental-utils": "^5.45.0", "@typescript-eslint/parser": "^5.45.0", @@ -92,14 +94,14 @@ "type-fest": "~2.19" }, "dependencies": { - "@babel/core": "^7.22.9", - "@babel/preset-env": "^7.22.9", - "@babel/preset-react": "^7.22.5", - "@babel/preset-typescript": "^7.22.5", - "@babel/runtime": "^7.22.6", + "@babel/core": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-react": "^7.22.15", + "@babel/preset-typescript": "^7.23.2", + "@babel/runtime": "^7.23.2", "@emotion/jest": "^11.10.0", "@jest/globals": "^29.3.1", - "@nx/workspace": "16.2.1", + "@nx/workspace": "17.0.2", "@playwright/test": "1.36.0", "@storybook/addon-a11y": "workspace:*", "@storybook/addon-actions": "workspace:*", @@ -234,7 +236,7 @@ "glob": "^10.0.0", "http-server": "^14.1.1", "husky": "^4.3.7", - "jest": "^29.5.0", + "jest": "^29.7.0", "jest-environment-jsdom": "^29.5.0", "jest-image-snapshot": "^6.0.0", "jest-junit": "^16.0.0", @@ -245,8 +247,7 @@ "lint-staged": "^13.2.2", "lodash": "^4.17.21", "node-gyp": "^9.3.1", - "nx": "16.2.1", - "nx-cloud": "16.0.5", + "nx": "17.0.2", "prettier": "2.8.0", "process": "^0.11.10", "raf": "^3.4.1", @@ -328,5 +329,6 @@ "Dependency Upgrades" ] ] - } + }, + "deferredNextVersion": "7.6.0-alpha.4" } diff --git a/code/presets/preact-webpack/package.json b/code/presets/preact-webpack/package.json index beac43efa297..42619ff78004 100644 --- a/code/presets/preact-webpack/package.json +++ b/code/presets/preact-webpack/package.json @@ -49,8 +49,8 @@ "prep": "../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.22.5", - "@babel/preset-typescript": "^7.22.5", + "@babel/plugin-transform-react-jsx": "^7.22.15", + "@babel/preset-typescript": "^7.23.2", "@storybook/core-webpack": "workspace:*", "@types/node": "^18.0.0" }, diff --git a/code/presets/react-webpack/package.json b/code/presets/react-webpack/package.json index 64b325bcd427..2eb5e07a103a 100644 --- a/code/presets/react-webpack/package.json +++ b/code/presets/react-webpack/package.json @@ -64,8 +64,8 @@ "prep": "../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@babel/preset-flow": "^7.22.5", - "@babel/preset-react": "^7.22.5", + "@babel/preset-flow": "^7.22.15", + "@babel/preset-react": "^7.22.15", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.5", "@storybook/core-webpack": "workspace:*", "@storybook/docs-tools": "workspace:*", diff --git a/code/presets/web-components-webpack/package.json b/code/presets/web-components-webpack/package.json index 3c83ee772324..2b152f658feb 100644 --- a/code/presets/web-components-webpack/package.json +++ b/code/presets/web-components-webpack/package.json @@ -54,7 +54,7 @@ "dependencies": { "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/preset-env": "^7.22.9", + "@babel/preset-env": "^7.23.2", "@storybook/core-webpack": "workspace:*", "@types/node": "^18.0.0", "babel-loader": "^7.0.0 || ^8.0.0 || ^9.0.0", diff --git a/code/renderers/html/package.json b/code/renderers/html/package.json index 3d042ea5f214..9dec7bb0745e 100644 --- a/code/renderers/html/package.json +++ b/code/renderers/html/package.json @@ -26,11 +26,9 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./preview": { - "types": "./dist/config.d.ts", - "require": "./dist/config.js", - "import": "./dist/config.mjs" - }, + "./preset": "./preset.js", + "./dist/entry-preview.mjs": "./dist/entry-preview.mjs", + "./dist/entry-preview-docs.mjs": "./dist/entry-preview-docs.mjs", "./package.json": "./package.json" }, "main": "dist/index.js", @@ -71,7 +69,9 @@ "bundler": { "entries": [ "./src/index.ts", - "./src/config.ts" + "./src/preset.ts", + "./src/entry-preview.ts", + "./src/entry-preview-docs.ts" ], "platform": "browser" }, diff --git a/code/renderers/html/preset.js b/code/renderers/html/preset.js new file mode 100644 index 000000000000..a83f95279e7f --- /dev/null +++ b/code/renderers/html/preset.js @@ -0,0 +1 @@ +module.exports = require('./dist/preset'); diff --git a/code/renderers/html/preview.js b/code/renderers/html/preview.js deleted file mode 100644 index fe48d8883ba4..000000000000 --- a/code/renderers/html/preview.js +++ /dev/null @@ -1 +0,0 @@ -export * from './dist/config'; diff --git a/code/renderers/html/src/config.ts b/code/renderers/html/src/config.ts deleted file mode 100644 index a318cb8498a2..000000000000 --- a/code/renderers/html/src/config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { parameters as docsParams } from './docs/config'; -import type { Parameters } from './types'; - -export const parameters: Parameters = { renderer: 'html', ...docsParams }; - -export { decorators, argTypesEnhancers } from './docs/config'; -export { renderToCanvas, render } from './render'; diff --git a/code/renderers/html/src/docs/config.ts b/code/renderers/html/src/docs/config.ts deleted file mode 100644 index 2043949f02e1..000000000000 --- a/code/renderers/html/src/docs/config.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { Addon_DecoratorFunction, ArgTypesEnhancer } from '@storybook/types'; -import { SourceType, enhanceArgTypes } from '@storybook/docs-tools'; - -import { sourceDecorator } from './sourceDecorator'; -import type { Parameters, StoryFnHtmlReturnType } from '../types'; - -export const decorators: Addon_DecoratorFunction[] = [ - sourceDecorator as Addon_DecoratorFunction, -]; - -export const parameters: Partial = { - docs: { - story: { inline: true }, - source: { - type: SourceType.DYNAMIC, - language: 'html', - code: undefined, - excludeDecorators: undefined, - }, - }, -}; - -export const argTypesEnhancers: ArgTypesEnhancer[] = [enhanceArgTypes]; diff --git a/code/renderers/html/src/docs/sourceDecorator.ts b/code/renderers/html/src/docs/sourceDecorator.ts index da1160fdc4f7..6318c9203312 100644 --- a/code/renderers/html/src/docs/sourceDecorator.ts +++ b/code/renderers/html/src/docs/sourceDecorator.ts @@ -2,13 +2,13 @@ import { SNIPPET_RENDERED, SourceType } from '@storybook/docs-tools'; import { addons, useEffect } from '@storybook/preview-api'; -import type { PartialStoryFn } from '@storybook/types'; +import type { DecoratorFunction } from '@storybook/types'; -import type { HtmlRenderer, StoryContext } from '../types'; +import type { HtmlRenderer } from '../types'; import type { StoryFn } from '../public-types'; -function skipSourceRender(context: StoryContext) { +function skipSourceRender(context: Parameters>[1]) { const sourceParams = context?.parameters.docs?.source; const isArgsStory = context?.parameters.__isArgsStory; @@ -22,7 +22,7 @@ function skipSourceRender(context: StoryContext) { return !isArgsStory || sourceParams?.code || sourceParams?.type === SourceType.CODE; } -export function sourceDecorator(storyFn: PartialStoryFn, context: StoryContext) { +export const sourceDecorator: DecoratorFunction = (storyFn, context) => { const story = storyFn(); const renderedForSource = context?.parameters.docs?.source?.excludeDecorators ? (context.originalStoryFn as StoryFn)(context.args, context) @@ -42,4 +42,4 @@ export function sourceDecorator(storyFn: PartialStoryFn, context: }); return story; -} +}; diff --git a/code/renderers/html/src/entry-preview-docs.ts b/code/renderers/html/src/entry-preview-docs.ts new file mode 100644 index 000000000000..872b95e0e077 --- /dev/null +++ b/code/renderers/html/src/entry-preview-docs.ts @@ -0,0 +1,21 @@ +import type { ArgTypesEnhancer, DecoratorFunction } from '@storybook/types'; +import { SourceType, enhanceArgTypes } from '@storybook/docs-tools'; + +import { sourceDecorator } from './docs/sourceDecorator'; +import type { HtmlRenderer } from './types'; + +export const decorators: DecoratorFunction[] = [sourceDecorator]; + +export const parameters = { + docs: { + story: { inline: true }, + source: { + type: SourceType.DYNAMIC, + language: 'html', + code: undefined, + excludeDecorators: undefined, + }, + }, +}; + +export const argTypesEnhancers: ArgTypesEnhancer[] = [enhanceArgTypes]; diff --git a/code/renderers/html/src/entry-preview.ts b/code/renderers/html/src/entry-preview.ts new file mode 100644 index 000000000000..ab53ac0c984a --- /dev/null +++ b/code/renderers/html/src/entry-preview.ts @@ -0,0 +1,5 @@ +import type { Parameters } from './types'; + +export const parameters: Parameters = { renderer: 'html' }; + +export { renderToCanvas, render } from './render'; diff --git a/code/renderers/html/src/preset.ts b/code/renderers/html/src/preset.ts new file mode 100644 index 000000000000..b60ba9208510 --- /dev/null +++ b/code/renderers/html/src/preset.ts @@ -0,0 +1,12 @@ +import type { StorybookConfig } from '@storybook/types'; +import { join } from 'path'; + +export const previewAnnotations: StorybookConfig['previewAnnotations'] = async (input, options) => { + const docsEnabled = Object.keys(await options.presets.apply('docs', {}, options)).length > 0; + const result: string[] = []; + + return result + .concat(input) + .concat([join(__dirname, 'entry-preview.mjs')]) + .concat(docsEnabled ? [join(__dirname, 'entry-preview-docs.mjs')] : []); +}; diff --git a/code/renderers/html/template/cli/js/Page.stories.js b/code/renderers/html/template/cli/js/Page.stories.js index a80a4c4a9d24..fcab88181bee 100644 --- a/code/renderers/html/template/cli/js/Page.stories.js +++ b/code/renderers/html/template/cli/js/Page.stories.js @@ -1,4 +1,4 @@ -import { within, userEvent } from '@storybook/testing-library'; +import { within, userEvent, expect } from '@storybook/test'; import { createPage } from './Page'; export default { @@ -16,9 +16,12 @@ export const LoggedOut = {}; export const LoggedIn = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const loginButton = await canvas.getByRole('button', { - name: /Log in/i, - }); + const loginButton = canvas.getByRole('button', { name: /Log in/i }); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); + await expect(loginButton).not.toBeInTheDocument(); + + const logoutButton = canvas.getByRole('button', { name: /Log out/i }); + await expect(logoutButton).toBeInTheDocument(); }, }; diff --git a/code/renderers/html/template/cli/ts-3-8/Page.stories.ts b/code/renderers/html/template/cli/ts-3-8/Page.stories.ts index 04b893f795fc..0c2e79e8e907 100644 --- a/code/renderers/html/template/cli/ts-3-8/Page.stories.ts +++ b/code/renderers/html/template/cli/ts-3-8/Page.stories.ts @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/html'; -import { within, userEvent } from '@storybook/testing-library'; +import { within, userEvent, expect } from '@storybook/test'; import { createPage } from './Page'; const meta: Meta = { @@ -19,9 +19,12 @@ export const LoggedOut: StoryObj = {}; export const LoggedIn: StoryObj = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const loginButton = await canvas.getByRole('button', { - name: /Log in/i, - }); + const loginButton = canvas.getByRole('button', { name: /Log in/i }); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); + await expect(loginButton).not.toBeInTheDocument(); + + const logoutButton = canvas.getByRole('button', { name: /Log out/i }); + await expect(logoutButton).toBeInTheDocument(); }, }; diff --git a/code/renderers/html/template/cli/ts-4-9/Page.stories.ts b/code/renderers/html/template/cli/ts-4-9/Page.stories.ts index 6de6300e49a0..c64a88fbb84f 100644 --- a/code/renderers/html/template/cli/ts-4-9/Page.stories.ts +++ b/code/renderers/html/template/cli/ts-4-9/Page.stories.ts @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/html'; -import { within, userEvent } from '@storybook/testing-library'; +import { within, userEvent, expect } from '@storybook/test'; import { createPage } from './Page'; const meta = { @@ -19,9 +19,12 @@ export const LoggedOut: StoryObj = {}; export const LoggedIn: StoryObj = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const loginButton = await canvas.getByRole('button', { - name: /Log in/i, - }); + const loginButton = canvas.getByRole('button', { name: /Log in/i }); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); + await expect(loginButton).not.toBeInTheDocument(); + + const logoutButton = canvas.getByRole('button', { name: /Log out/i }); + await expect(logoutButton).toBeInTheDocument(); }, }; diff --git a/code/renderers/preact/package.json b/code/renderers/preact/package.json index eed4e9bdf033..9d5330d468eb 100644 --- a/code/renderers/preact/package.json +++ b/code/renderers/preact/package.json @@ -26,11 +26,9 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./preview": { - "types": "./dist/config.d.ts", - "require": "./dist/config.js", - "import": "./dist/config.mjs" - }, + "./preset": "./preset.js", + "./dist/entry-preview.mjs": "./dist/entry-preview.mjs", + "./dist/entry-preview-docs.mjs": "./dist/entry-preview-docs.mjs", "./package.json": "./package.json" }, "main": "dist/index.js", @@ -71,7 +69,9 @@ "bundler": { "entries": [ "./src/index.ts", - "./src/config.ts" + "./src/preset.ts", + "./src/entry-preview.ts", + "./src/entry-preview-docs.ts" ], "platform": "browser" }, diff --git a/code/renderers/preact/preset.js b/code/renderers/preact/preset.js new file mode 100644 index 000000000000..a83f95279e7f --- /dev/null +++ b/code/renderers/preact/preset.js @@ -0,0 +1 @@ +module.exports = require('./dist/preset'); diff --git a/code/renderers/preact/preview.js b/code/renderers/preact/preview.js deleted file mode 100644 index fe48d8883ba4..000000000000 --- a/code/renderers/preact/preview.js +++ /dev/null @@ -1 +0,0 @@ -export * from './dist/config'; diff --git a/code/renderers/preact/src/config.ts b/code/renderers/preact/src/config.ts deleted file mode 100644 index 6c72da1bc702..000000000000 --- a/code/renderers/preact/src/config.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { parameters as docsParams } from './docs/config'; - -export { renderToCanvas, render } from './render'; - -export const parameters: {} = { renderer: 'preact' as const, ...docsParams }; diff --git a/code/renderers/preact/src/docs/config.ts b/code/renderers/preact/src/entry-preview-docs.ts similarity index 100% rename from code/renderers/preact/src/docs/config.ts rename to code/renderers/preact/src/entry-preview-docs.ts diff --git a/code/renderers/preact/src/entry-preview.ts b/code/renderers/preact/src/entry-preview.ts new file mode 100644 index 000000000000..b09bdf943bcf --- /dev/null +++ b/code/renderers/preact/src/entry-preview.ts @@ -0,0 +1,3 @@ +export { renderToCanvas, render } from './render'; + +export const parameters = { renderer: 'preact' }; diff --git a/code/renderers/preact/src/preset.ts b/code/renderers/preact/src/preset.ts new file mode 100644 index 000000000000..b60ba9208510 --- /dev/null +++ b/code/renderers/preact/src/preset.ts @@ -0,0 +1,12 @@ +import type { StorybookConfig } from '@storybook/types'; +import { join } from 'path'; + +export const previewAnnotations: StorybookConfig['previewAnnotations'] = async (input, options) => { + const docsEnabled = Object.keys(await options.presets.apply('docs', {}, options)).length > 0; + const result: string[] = []; + + return result + .concat(input) + .concat([join(__dirname, 'entry-preview.mjs')]) + .concat(docsEnabled ? [join(__dirname, 'entry-preview-docs.mjs')] : []); +}; diff --git a/code/renderers/preact/template/cli/Page.stories.jsx b/code/renderers/preact/template/cli/Page.stories.jsx index d49ca397aaee..46669ac1c89d 100644 --- a/code/renderers/preact/template/cli/Page.stories.jsx +++ b/code/renderers/preact/template/cli/Page.stories.jsx @@ -1,4 +1,4 @@ -import { within, userEvent } from '@storybook/testing-library'; +import { within, userEvent } from '@storybook/test'; import { Page } from './Page'; diff --git a/code/renderers/react/package.json b/code/renderers/react/package.json index 76a07b4f6752..08c29dd01229 100644 --- a/code/renderers/react/package.json +++ b/code/renderers/react/package.json @@ -26,15 +26,9 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./preview": { - "types": "./dist/config.d.ts", - "require": "./dist/config.js", - "import": "./dist/config.mjs" - }, - "./dist/preset": { - "types": "./dist/preset.d.ts", - "require": "./dist/preset.js" - }, + "./preset": "./preset.js", + "./dist/entry-preview.mjs": "./dist/entry-preview.mjs", + "./dist/entry-preview-docs.mjs": "./dist/entry-preview-docs.mjs", "./package.json": "./package.json" }, "main": "dist/index.js", @@ -76,7 +70,7 @@ "util-deprecate": "^1.0.2" }, "devDependencies": { - "@babel/core": "^7.22.9", + "@babel/core": "^7.23.2", "@storybook/test": "workspace:*", "@types/util-deprecate": "^1.0.0", "expect-type": "^0.15.0", @@ -102,8 +96,9 @@ "bundler": { "entries": [ "./src/index.ts", - "./src/config.ts", - "./src/preset.ts" + "./src/preset.ts", + "./src/entry-preview.ts", + "./src/entry-preview-docs.ts" ], "platform": "browser" }, diff --git a/code/renderers/react/preview.js b/code/renderers/react/preview.js deleted file mode 100644 index f00f03b4e2e4..000000000000 --- a/code/renderers/react/preview.js +++ /dev/null @@ -1 +0,0 @@ -export * from './dist/config.mjs'; diff --git a/code/renderers/react/src/config.ts b/code/renderers/react/src/config.ts deleted file mode 100644 index ee07fe8687a4..000000000000 --- a/code/renderers/react/src/config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { parameters as docsParams } from './docs/config'; - -export const parameters: {} = { renderer: 'react', ...docsParams }; - -export { decorators, argTypesEnhancers } from './docs/config'; - -export { render, renderToCanvas } from './render'; - -export { applyDecorators } from './applyDecorators'; diff --git a/code/renderers/react/src/applyDecorators.ts b/code/renderers/react/src/docs/applyDecorators.ts similarity index 88% rename from code/renderers/react/src/applyDecorators.ts rename to code/renderers/react/src/docs/applyDecorators.ts index cf5c44fa5f80..109b5057f0c4 100644 --- a/code/renderers/react/src/applyDecorators.ts +++ b/code/renderers/react/src/docs/applyDecorators.ts @@ -1,8 +1,8 @@ import { defaultDecorateStory } from '@storybook/preview-api'; import type { LegacyStoryFn, DecoratorFunction } from '@storybook/types'; -import type { ReactRenderer } from './types'; -import { jsxDecorator } from './docs/jsxDecorator'; +import type { ReactRenderer } from '../types'; +import { jsxDecorator } from './jsxDecorator'; export const applyDecorators = ( storyFn: LegacyStoryFn, diff --git a/code/renderers/react/src/docs/config.ts b/code/renderers/react/src/docs/config.ts deleted file mode 100644 index adc3af6d2318..000000000000 --- a/code/renderers/react/src/docs/config.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Addon_DecoratorFunction, ArgTypesEnhancer } from '@storybook/types'; -import { extractComponentDescription, enhanceArgTypes } from '@storybook/docs-tools'; - -import { extractArgTypes } from './extractArgTypes'; -import { jsxDecorator } from './jsxDecorator'; -import type { StoryFnReactReturnType } from '../types'; - -export const parameters: {} = { - docs: { - story: { inline: true }, - extractArgTypes, - extractComponentDescription, - }, -}; - -export const decorators: Addon_DecoratorFunction[] = [jsxDecorator]; - -export const argTypesEnhancers: ArgTypesEnhancer[] = [enhanceArgTypes]; diff --git a/code/renderers/react/src/entry-preview-docs.ts b/code/renderers/react/src/entry-preview-docs.ts new file mode 100644 index 000000000000..51e347b0f03d --- /dev/null +++ b/code/renderers/react/src/entry-preview-docs.ts @@ -0,0 +1,20 @@ +import type { ArgTypesEnhancer, DecoratorFunction } from '@storybook/types'; +import { extractComponentDescription, enhanceArgTypes } from '@storybook/docs-tools'; + +import { extractArgTypes } from './docs/extractArgTypes'; +import { jsxDecorator } from './docs/jsxDecorator'; +import type { ReactRenderer } from './types'; + +export const parameters = { + docs: { + story: { inline: true }, + extractArgTypes, + extractComponentDescription, + }, +}; + +export const decorators: DecoratorFunction[] = [jsxDecorator]; + +export const argTypesEnhancers: ArgTypesEnhancer[] = [enhanceArgTypes]; + +export { applyDecorators } from './docs/applyDecorators'; diff --git a/code/renderers/react/src/entry-preview.ts b/code/renderers/react/src/entry-preview.ts new file mode 100644 index 000000000000..fe96bc623da7 --- /dev/null +++ b/code/renderers/react/src/entry-preview.ts @@ -0,0 +1,2 @@ +export const parameters: {} = { renderer: 'react' }; +export { render, renderToCanvas } from './render'; diff --git a/code/renderers/react/src/preset.ts b/code/renderers/react/src/preset.ts index 5194fd3a23f8..6da62d30bbf2 100644 --- a/code/renderers/react/src/preset.ts +++ b/code/renderers/react/src/preset.ts @@ -1,5 +1,18 @@ import type { StorybookConfig } from '@storybook/types'; +import { join } from 'path'; + export const addons: StorybookConfig['addons'] = [ require.resolve('@storybook/react-dom-shim/dist/preset'), ]; + +export const previewAnnotations: StorybookConfig['previewAnnotations'] = async (input, options) => { + const docsConfig = await options.presets.apply('docs', {}, options); + const docsEnabled = Object.keys(docsConfig).length > 0; + const result: string[] = []; + + return result + .concat(input) + .concat([join(__dirname, 'entry-preview.mjs')]) + .concat(docsEnabled ? [join(__dirname, 'entry-preview-docs.mjs')] : []); +}; diff --git a/code/renderers/react/template/cli/js/Page.stories.js b/code/renderers/react/template/cli/js/Page.stories.js index 7467c98cf48a..204189f6d21d 100644 --- a/code/renderers/react/template/cli/js/Page.stories.js +++ b/code/renderers/react/template/cli/js/Page.stories.js @@ -1,4 +1,4 @@ -import { within, userEvent } from '@storybook/testing-library'; +import { within, userEvent, expect } from '@storybook/test'; import { Page } from './Page'; @@ -17,9 +17,12 @@ export const LoggedOut = {}; export const LoggedIn = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const loginButton = await canvas.getByRole('button', { - name: /Log in/i, - }); + const loginButton = canvas.getByRole('button', { name: /Log in/i }); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); + await expect(loginButton).not.toBeInTheDocument(); + + const logoutButton = canvas.getByRole('button', { name: /Log out/i }); + await expect(logoutButton).toBeInTheDocument(); }, }; diff --git a/code/renderers/react/template/cli/ts-3-8/Page.stories.ts b/code/renderers/react/template/cli/ts-3-8/Page.stories.ts index 52c4cb406233..adbab5d615f2 100644 --- a/code/renderers/react/template/cli/ts-3-8/Page.stories.ts +++ b/code/renderers/react/template/cli/ts-3-8/Page.stories.ts @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { within, userEvent } from '@storybook/testing-library'; +import { within, userEvent, expect } from '@storybook/test'; import { Page } from './Page'; @@ -21,9 +21,12 @@ export const LoggedOut: Story = {}; export const LoggedIn: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const loginButton = await canvas.getByRole('button', { - name: /Log in/i, - }); + const loginButton = canvas.getByRole('button', { name: /Log in/i }); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); + await expect(loginButton).not.toBeInTheDocument(); + + const logoutButton = canvas.getByRole('button', { name: /Log out/i }); + await expect(logoutButton).toBeInTheDocument(); }, }; diff --git a/code/renderers/react/template/cli/ts-4-9/Page.stories.ts b/code/renderers/react/template/cli/ts-4-9/Page.stories.ts index 69852a2bdc00..622858e72e7f 100644 --- a/code/renderers/react/template/cli/ts-4-9/Page.stories.ts +++ b/code/renderers/react/template/cli/ts-4-9/Page.stories.ts @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { within, userEvent } from '@storybook/testing-library'; +import { within, userEvent, expect } from '@storybook/test'; import { Page } from './Page'; @@ -21,9 +21,12 @@ export const LoggedOut: Story = {}; export const LoggedIn: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const loginButton = await canvas.getByRole('button', { - name: /Log in/i, - }); + const loginButton = canvas.getByRole('button', { name: /Log in/i }); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); + await expect(loginButton).not.toBeInTheDocument(); + + const logoutButton = canvas.getByRole('button', { name: /Log out/i }); + await expect(logoutButton).toBeInTheDocument(); }, }; diff --git a/code/renderers/server/package.json b/code/renderers/server/package.json index 879e51a778e6..47d9cced041e 100644 --- a/code/renderers/server/package.json +++ b/code/renderers/server/package.json @@ -26,16 +26,8 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./preview": { - "types": "./dist/config.d.ts", - "require": "./dist/config.js", - "import": "./dist/config.mjs" - }, - "./preset": { - "types": "./dist/preset.d.ts", - "require": "./dist/preset.js", - "import": "./dist/preset.mjs" - }, + "./preset": "./preset.js", + "./dist/entry-preview.mjs": "./dist/entry-preview.mjs", "./package.json": "./package.json" }, "main": "dist/index.js", @@ -77,8 +69,8 @@ "bundler": { "entries": [ "./src/index.ts", - "./src/config.ts", - "./src/preset.ts" + "./src/preset.ts", + "./src/entry-preview.ts" ], "platform": "browser" }, diff --git a/code/renderers/server/preview.js b/code/renderers/server/preview.js deleted file mode 100644 index fe48d8883ba4..000000000000 --- a/code/renderers/server/preview.js +++ /dev/null @@ -1 +0,0 @@ -export * from './dist/config'; diff --git a/code/renderers/server/src/config.ts b/code/renderers/server/src/entry-preview.ts similarity index 100% rename from code/renderers/server/src/config.ts rename to code/renderers/server/src/entry-preview.ts diff --git a/code/renderers/server/src/preset.ts b/code/renderers/server/src/preset.ts index 63cc1aca491a..01039ebfb980 100644 --- a/code/renderers/server/src/preset.ts +++ b/code/renderers/server/src/preset.ts @@ -2,6 +2,8 @@ import fs from 'fs-extra'; import yaml from 'yaml'; import type { StorybookConfig, Tag, StoryName, ComponentTitle } from '@storybook/types'; +import { join } from 'path'; + type FileContent = { title: ComponentTitle; tags?: Tag[]; @@ -34,3 +36,13 @@ export const experimental_indexers: StorybookConfig['experimental_indexers'] = ( }, ...(existingIndexers || []), ]; + +export const previewAnnotations: StorybookConfig['previewAnnotations'] = async (input, options) => { + const { presetsList } = options; + if (!presetsList) { + return input; + } + const result: string[] = []; + + return result.concat(input).concat([join(__dirname, 'entry-preview.mjs')]); +}; diff --git a/code/renderers/svelte/package.json b/code/renderers/svelte/package.json index 4ad9ad3801f5..99a84af9c552 100644 --- a/code/renderers/svelte/package.json +++ b/code/renderers/svelte/package.json @@ -26,11 +26,9 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./preview": { - "types": "./dist/config.d.ts", - "require": "./dist/config.js", - "import": "./dist/config.mjs" - }, + "./preset": "./preset.js", + "./dist/entry-preview.mjs": "./dist/entry-preview.mjs", + "./dist/entry-preview-docs.mjs": "./dist/entry-preview-docs.mjs", "./package.json": "./package.json", "./templates/HOC.svelte": "./templates/HOC.svelte", "./templates/PreviewRender.svelte": "./templates/PreviewRender.svelte", @@ -81,7 +79,9 @@ "bundler": { "entries": [ "./src/index.ts", - "./src/config.ts" + "./src/preset.ts", + "./src/entry-preview.ts", + "./src/entry-preview-docs.ts" ], "platform": "browser" }, diff --git a/code/renderers/svelte/preset.js b/code/renderers/svelte/preset.js new file mode 100644 index 000000000000..a83f95279e7f --- /dev/null +++ b/code/renderers/svelte/preset.js @@ -0,0 +1 @@ +module.exports = require('./dist/preset'); diff --git a/code/renderers/svelte/preview.js b/code/renderers/svelte/preview.js deleted file mode 100644 index f00f03b4e2e4..000000000000 --- a/code/renderers/svelte/preview.js +++ /dev/null @@ -1 +0,0 @@ -export * from './dist/config.mjs'; diff --git a/code/renderers/svelte/src/config.ts b/code/renderers/svelte/src/config.ts deleted file mode 100644 index 78323a1ce32c..000000000000 --- a/code/renderers/svelte/src/config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { parameters as docsParams } from './docs/config'; - -export const parameters: {} = { renderer: 'svelte' as const, ...docsParams }; -export { decorators, argTypesEnhancers } from './docs/config'; - -export { render, renderToCanvas } from './render'; -export { decorateStory as applyDecorators } from './decorators'; diff --git a/code/renderers/svelte/src/docs/config.ts b/code/renderers/svelte/src/docs/config.ts deleted file mode 100644 index 563235ec7f2d..000000000000 --- a/code/renderers/svelte/src/docs/config.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { Addon_DecoratorFunction, ArgTypesEnhancer } from '@storybook/types'; -import { enhanceArgTypes } from '@storybook/docs-tools'; -import { extractArgTypes } from './extractArgTypes'; -import { extractComponentDescription } from './extractComponentDescription'; -import { sourceDecorator } from './sourceDecorator'; - -export const parameters: {} = { - docs: { - story: { inline: true }, - extractArgTypes, - extractComponentDescription, - }, -}; - -export const decorators: Addon_DecoratorFunction[] = [sourceDecorator]; - -export const argTypesEnhancers: ArgTypesEnhancer[] = [enhanceArgTypes]; diff --git a/code/renderers/svelte/src/docs/sourceDecorator.ts b/code/renderers/svelte/src/docs/sourceDecorator.ts index 7b61eea6a7e9..62b27773058a 100644 --- a/code/renderers/svelte/src/docs/sourceDecorator.ts +++ b/code/renderers/svelte/src/docs/sourceDecorator.ts @@ -1,16 +1,17 @@ /* eslint-disable no-underscore-dangle */ import { addons, useEffect } from '@storybook/preview-api'; import { deprecate } from '@storybook/client-logger'; -import type { ArgTypes, Args, StoryContext, Renderer } from '@storybook/types'; +import type { ArgTypes, Args, StoryContext } from '@storybook/types'; import { SourceType, SNIPPET_RENDERED } from '@storybook/docs-tools'; +import type { SvelteRenderer } from '../types'; /** - * Check if the sourcecode should be generated. + * Check if the source-code should be generated. * * @param context StoryContext */ -const skipSourceRender = (context: StoryContext) => { +const skipSourceRender = (context: StoryContext) => { const sourceParams = context?.parameters.docs?.source; const isArgsStory = context?.parameters.__isArgsStory; @@ -162,7 +163,7 @@ function getWrapperProperties(component: any) { * @param storyFn Fn * @param context StoryContext */ -export const sourceDecorator = (storyFn: any, context: StoryContext) => { +export const sourceDecorator = (storyFn: any, context: StoryContext) => { const channel = addons.getChannel(); const skip = skipSourceRender(context); const story = storyFn(); diff --git a/code/renderers/svelte/src/entry-preview-docs.ts b/code/renderers/svelte/src/entry-preview-docs.ts new file mode 100644 index 000000000000..8cc14c3b191f --- /dev/null +++ b/code/renderers/svelte/src/entry-preview-docs.ts @@ -0,0 +1,18 @@ +import type { ArgTypesEnhancer, DecoratorFunction } from '@storybook/types'; +import { enhanceArgTypes } from '@storybook/docs-tools'; +import { extractArgTypes } from './docs/extractArgTypes'; +import { extractComponentDescription } from './docs/extractComponentDescription'; +import { sourceDecorator } from './docs/sourceDecorator'; +import type { SvelteRenderer } from './types'; + +export const parameters = { + docs: { + story: { inline: true }, + extractArgTypes, + extractComponentDescription, + }, +}; + +export const decorators: DecoratorFunction[] = [sourceDecorator]; + +export const argTypesEnhancers: ArgTypesEnhancer[] = [enhanceArgTypes]; diff --git a/code/renderers/svelte/src/entry-preview.ts b/code/renderers/svelte/src/entry-preview.ts new file mode 100644 index 000000000000..4d8b883dbffe --- /dev/null +++ b/code/renderers/svelte/src/entry-preview.ts @@ -0,0 +1,4 @@ +export const parameters: {} = { renderer: 'svelte' }; + +export { render, renderToCanvas } from './render'; +export { decorateStory as applyDecorators } from './decorators'; diff --git a/code/renderers/svelte/src/preset.ts b/code/renderers/svelte/src/preset.ts new file mode 100644 index 000000000000..b60ba9208510 --- /dev/null +++ b/code/renderers/svelte/src/preset.ts @@ -0,0 +1,12 @@ +import type { StorybookConfig } from '@storybook/types'; +import { join } from 'path'; + +export const previewAnnotations: StorybookConfig['previewAnnotations'] = async (input, options) => { + const docsEnabled = Object.keys(await options.presets.apply('docs', {}, options)).length > 0; + const result: string[] = []; + + return result + .concat(input) + .concat([join(__dirname, 'entry-preview.mjs')]) + .concat(docsEnabled ? [join(__dirname, 'entry-preview-docs.mjs')] : []); +}; diff --git a/code/renderers/svelte/template/cli/js/Page.stories.js b/code/renderers/svelte/template/cli/js/Page.stories.js index 9c6008b4c705..a4492ae66631 100644 --- a/code/renderers/svelte/template/cli/js/Page.stories.js +++ b/code/renderers/svelte/template/cli/js/Page.stories.js @@ -1,4 +1,4 @@ -import { within, userEvent } from '@storybook/testing-library'; +import { within, userEvent, expect } from '@storybook/test'; import Page from './Page.svelte'; @@ -17,9 +17,12 @@ export const LoggedOut = {}; export const LoggedIn = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const loginButton = await canvas.getByRole('button', { - name: /Log in/i, - }); + const loginButton = canvas.getByRole('button', { name: /Log in/i }); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); + await expect(loginButton).not.toBeInTheDocument(); + + const logoutButton = canvas.getByRole('button', { name: /Log out/i }); + await expect(logoutButton).toBeInTheDocument(); }, }; diff --git a/code/renderers/svelte/template/cli/ts-3-8/Page.stories.ts b/code/renderers/svelte/template/cli/ts-3-8/Page.stories.ts index 3aba1302bf24..82e49cd3be09 100644 --- a/code/renderers/svelte/template/cli/ts-3-8/Page.stories.ts +++ b/code/renderers/svelte/template/cli/ts-3-8/Page.stories.ts @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/svelte'; -import { within, userEvent } from '@storybook/testing-library'; +import { within, userEvent, expect } from '@storybook/test'; import Page from './Page.svelte'; @@ -21,9 +21,12 @@ export const LoggedOut: Story = {}; export const LoggedIn: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const loginButton = await canvas.getByRole('button', { - name: /Log in/i, - }); + const loginButton = canvas.getByRole('button', { name: /Log in/i }); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); + await expect(loginButton).not.toBeInTheDocument(); + + const logoutButton = canvas.getByRole('button', { name: /Log out/i }); + await expect(logoutButton).toBeInTheDocument(); }, }; diff --git a/code/renderers/svelte/template/cli/ts-4-9/Page.stories.ts b/code/renderers/svelte/template/cli/ts-4-9/Page.stories.ts index 78665a28bfae..d7e97628ea24 100644 --- a/code/renderers/svelte/template/cli/ts-4-9/Page.stories.ts +++ b/code/renderers/svelte/template/cli/ts-4-9/Page.stories.ts @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/svelte'; -import { within, userEvent } from '@storybook/testing-library'; +import { within, userEvent, expect } from '@storybook/test'; import Page from './Page.svelte'; @@ -21,9 +21,12 @@ export const LoggedOut: Story = {}; export const LoggedIn: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const loginButton = await canvas.getByRole('button', { - name: /Log in/i, - }); + const loginButton = canvas.getByRole('button', { name: /Log in/i }); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); + await expect(loginButton).not.toBeInTheDocument(); + + const logoutButton = canvas.getByRole('button', { name: /Log out/i }); + await expect(logoutButton).toBeInTheDocument(); }, }; diff --git a/code/renderers/vue/package.json b/code/renderers/vue/package.json index a6df7ed76983..e9b16f82f4ae 100644 --- a/code/renderers/vue/package.json +++ b/code/renderers/vue/package.json @@ -26,11 +26,9 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./preview": { - "types": "./dist/config.d.ts", - "require": "./dist/config.js", - "import": "./dist/config.mjs" - }, + "./preset": "./preset.js", + "./dist/entry-preview.mjs": "./dist/entry-preview.mjs", + "./dist/entry-preview-docs.mjs": "./dist/entry-preview-docs.mjs", "./package.json": "./package.json" }, "main": "dist/index.js", @@ -83,7 +81,9 @@ "bundler": { "entries": [ "./src/index.ts", - "./src/config.ts" + "./src/preset.ts", + "./src/entry-preview.ts", + "./src/entry-preview-docs.ts" ], "platform": "browser" }, diff --git a/code/renderers/vue/preset.js b/code/renderers/vue/preset.js new file mode 100644 index 000000000000..a83f95279e7f --- /dev/null +++ b/code/renderers/vue/preset.js @@ -0,0 +1 @@ +module.exports = require('./dist/preset'); diff --git a/code/renderers/vue/preview.js b/code/renderers/vue/preview.js deleted file mode 100644 index fe48d8883ba4..000000000000 --- a/code/renderers/vue/preview.js +++ /dev/null @@ -1 +0,0 @@ -export * from './dist/config'; diff --git a/code/renderers/vue/src/config.ts b/code/renderers/vue/src/config.ts deleted file mode 100644 index bd9a2fb152cd..000000000000 --- a/code/renderers/vue/src/config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { parameters as docsParams } from './docs/config'; - -export const parameters: {} = { renderer: 'vue' as const, ...docsParams }; -export { decorators, argTypesEnhancers } from './docs/config'; - -export { render, renderToCanvas } from './render'; -export { decorateStory as applyDecorators } from './decorateStory'; diff --git a/code/renderers/vue/src/docs/config.ts b/code/renderers/vue/src/docs/config.ts deleted file mode 100644 index 2e4d23aa3ad7..000000000000 --- a/code/renderers/vue/src/docs/config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { Addon_DecoratorFunction, ArgTypesEnhancer } from '@storybook/types'; -import { extractComponentDescription, enhanceArgTypes } from '@storybook/docs-tools'; -import { extractArgTypes } from './extractArgTypes'; -import { sourceDecorator } from './sourceDecorator'; - -export const parameters: {} = { - docs: { - story: { inline: true, iframeHeight: '120px' }, - extractArgTypes, - extractComponentDescription, - }, -}; - -export const decorators: Addon_DecoratorFunction[] = [sourceDecorator]; - -export const argTypesEnhancers: ArgTypesEnhancer[] = [enhanceArgTypes]; diff --git a/code/renderers/vue/src/entry-preview-docs.ts b/code/renderers/vue/src/entry-preview-docs.ts new file mode 100644 index 000000000000..bd5af4e3f916 --- /dev/null +++ b/code/renderers/vue/src/entry-preview-docs.ts @@ -0,0 +1,17 @@ +import type { ArgTypesEnhancer, DecoratorFunction } from '@storybook/types'; +import { extractComponentDescription, enhanceArgTypes } from '@storybook/docs-tools'; +import { extractArgTypes } from './docs/extractArgTypes'; +import { sourceDecorator } from './docs/sourceDecorator'; +import type { VueRenderer } from './types'; + +export const parameters = { + docs: { + story: { inline: true, iframeHeight: '120px' }, + extractArgTypes, + extractComponentDescription, + }, +}; + +export const decorators: DecoratorFunction[] = [sourceDecorator]; + +export const argTypesEnhancers: ArgTypesEnhancer[] = [enhanceArgTypes]; diff --git a/code/renderers/vue/src/entry-preview.ts b/code/renderers/vue/src/entry-preview.ts new file mode 100644 index 000000000000..2110e13bcf0c --- /dev/null +++ b/code/renderers/vue/src/entry-preview.ts @@ -0,0 +1,3 @@ +export const parameters: {} = { renderer: 'vue' }; +export { render, renderToCanvas } from './render'; +export { decorateStory as applyDecorators } from './decorateStory'; diff --git a/code/renderers/vue/src/preset.ts b/code/renderers/vue/src/preset.ts new file mode 100644 index 000000000000..b60ba9208510 --- /dev/null +++ b/code/renderers/vue/src/preset.ts @@ -0,0 +1,12 @@ +import type { StorybookConfig } from '@storybook/types'; +import { join } from 'path'; + +export const previewAnnotations: StorybookConfig['previewAnnotations'] = async (input, options) => { + const docsEnabled = Object.keys(await options.presets.apply('docs', {}, options)).length > 0; + const result: string[] = []; + + return result + .concat(input) + .concat([join(__dirname, 'entry-preview.mjs')]) + .concat(docsEnabled ? [join(__dirname, 'entry-preview-docs.mjs')] : []); +}; diff --git a/code/renderers/vue/template/cli/Page.stories.js b/code/renderers/vue/template/cli/Page.stories.js index 8bd022352948..5e9b6ffa7318 100644 --- a/code/renderers/vue/template/cli/Page.stories.js +++ b/code/renderers/vue/template/cli/Page.stories.js @@ -1,4 +1,4 @@ -import { within, userEvent } from '@storybook/testing-library'; +import { within, userEvent, expect } from '@storybook/test'; import MyPage from './Page.vue'; export default { @@ -19,9 +19,12 @@ export const LoggedOut = {}; export const LoggedIn = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const loginButton = await canvas.getByRole('button', { - name: /Log in/i, - }); + const loginButton = canvas.getByRole('button', { name: /Log in/i }); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); + await expect(loginButton).not.toBeInTheDocument(); + + const logoutButton = canvas.getByRole('button', { name: /Log out/i }); + await expect(logoutButton).toBeInTheDocument(); }, }; diff --git a/code/renderers/vue3/package.json b/code/renderers/vue3/package.json index 69d61bafad51..3ea92f413b5f 100644 --- a/code/renderers/vue3/package.json +++ b/code/renderers/vue3/package.json @@ -26,11 +26,9 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./preview": { - "types": "./dist/config.d.ts", - "require": "./dist/config.js", - "import": "./dist/config.mjs" - }, + "./preset": "./preset.js", + "./dist/entry-preview.mjs": "./dist/entry-preview.mjs", + "./dist/entry-preview-docs.mjs": "./dist/entry-preview-docs.mjs", "./package.json": "./package.json" }, "main": "dist/index.js", @@ -64,6 +62,7 @@ "@types/prettier": "2.7.2", "@vue/compiler-core": "^3.3.4", "@vue/vue3-jest": "29", + "babel-jest": "^29.7.0", "typescript": "~4.9.3", "vue": "^3.2.47", "vue-tsc": "latest" @@ -81,7 +80,9 @@ "bundler": { "entries": [ "./src/index.ts", - "./src/config.ts" + "./src/preset.ts", + "./src/entry-preview.ts", + "./src/entry-preview-docs.ts" ], "platform": "browser" }, diff --git a/code/renderers/vue3/preset.js b/code/renderers/vue3/preset.js new file mode 100644 index 000000000000..a83f95279e7f --- /dev/null +++ b/code/renderers/vue3/preset.js @@ -0,0 +1 @@ +module.exports = require('./dist/preset'); diff --git a/code/renderers/vue3/preview.js b/code/renderers/vue3/preview.js deleted file mode 100644 index f00f03b4e2e4..000000000000 --- a/code/renderers/vue3/preview.js +++ /dev/null @@ -1 +0,0 @@ -export * from './dist/config.mjs'; diff --git a/code/renderers/vue3/src/config.ts b/code/renderers/vue3/src/config.ts deleted file mode 100644 index f132fb510296..000000000000 --- a/code/renderers/vue3/src/config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { parameters as docsParams } from './docs/config'; - -export const parameters: {} = { renderer: 'vue3' as const, ...docsParams }; -export { decorators, argTypesEnhancers } from './docs/config'; - -export { render, renderToCanvas } from './render'; -export { decorateStory as applyDecorators } from './decorateStory'; diff --git a/code/renderers/vue3/src/docs/config.ts b/code/renderers/vue3/src/docs/config.ts deleted file mode 100644 index ba0a4949b53f..000000000000 --- a/code/renderers/vue3/src/docs/config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { Addon_DecoratorFunction, ArgTypesEnhancer } from '@storybook/types'; -import { extractComponentDescription, enhanceArgTypes } from '@storybook/docs-tools'; -import { extractArgTypes } from './extractArgTypes'; -import { sourceDecorator } from './sourceDecorator'; - -export const parameters: {} = { - docs: { - story: { inline: true }, - extractArgTypes, - extractComponentDescription, - }, -}; - -export const decorators: Addon_DecoratorFunction[] = [sourceDecorator]; - -export const argTypesEnhancers: ArgTypesEnhancer[] = [enhanceArgTypes]; diff --git a/code/renderers/vue3/src/docs/sourceDecorator.ts b/code/renderers/vue3/src/docs/sourceDecorator.ts index f0e254f082ae..e0820db75cbd 100644 --- a/code/renderers/vue3/src/docs/sourceDecorator.ts +++ b/code/renderers/vue3/src/docs/sourceDecorator.ts @@ -1,7 +1,7 @@ /* eslint-disable no-eval */ /* eslint-disable no-underscore-dangle */ import { addons } from '@storybook/preview-api'; -import type { ArgTypes, Args, StoryContext, Renderer } from '@storybook/types'; +import type { ArgTypes, Args, StoryContext } from '@storybook/types'; import { SourceType, SNIPPET_RENDERED } from '@storybook/docs-tools'; @@ -25,13 +25,14 @@ import { replaceValueWithRef, generateExpression, } from './utils'; +import type { VueRenderer } from '../types'; /** * Check if the sourcecode should be generated. * * @param context StoryContext */ -const skipSourceRender = (context: StoryContext) => { +const skipSourceRender = (context: StoryContext) => { const sourceParams = context?.parameters.docs?.source; const isArgsStory = context?.parameters.__isArgsStory; const isDocsViewMode = context?.viewMode === 'docs'; @@ -153,7 +154,7 @@ function generateScriptSetup(args: Args, argTypes: ArgTypes, components: any[]): */ function getTemplateComponents( renderFn: any, - context?: StoryContext + context?: StoryContext ): (TemplateChildNode | VNode)[] { try { const originalStoryFn = renderFn; @@ -274,7 +275,7 @@ export function generateTemplateSource( * @param storyFn Fn * @param context StoryContext */ -export const sourceDecorator = (storyFn: any, context: StoryContext) => { +export const sourceDecorator = (storyFn: any, context: StoryContext) => { const skip = skipSourceRender(context); const story = storyFn(); @@ -290,7 +291,7 @@ export const sourceDecorator = (storyFn: any, context: StoryContext) = return story; }; -export function generateSource(context: StoryContext) { +export function generateSource(context: StoryContext) { const channel = addons.getChannel(); const { args = {}, argTypes = {}, id } = context || {}; const storyComponents = getTemplateComponents(context?.originalStoryFn, context); diff --git a/code/renderers/vue3/src/entry-preview-docs.ts b/code/renderers/vue3/src/entry-preview-docs.ts new file mode 100644 index 000000000000..0c598ffdec49 --- /dev/null +++ b/code/renderers/vue3/src/entry-preview-docs.ts @@ -0,0 +1,17 @@ +import type { ArgTypesEnhancer, DecoratorFunction } from '@storybook/types'; +import { extractComponentDescription, enhanceArgTypes } from '@storybook/docs-tools'; +import { extractArgTypes } from './docs/extractArgTypes'; +import { sourceDecorator } from './docs/sourceDecorator'; +import type { VueRenderer } from './types'; + +export const parameters = { + docs: { + story: { inline: true }, + extractArgTypes, + extractComponentDescription, + }, +}; + +export const decorators: DecoratorFunction[] = [sourceDecorator]; + +export const argTypesEnhancers: ArgTypesEnhancer[] = [enhanceArgTypes]; diff --git a/code/renderers/vue3/src/entry-preview.ts b/code/renderers/vue3/src/entry-preview.ts new file mode 100644 index 000000000000..a53fd937a5fa --- /dev/null +++ b/code/renderers/vue3/src/entry-preview.ts @@ -0,0 +1,3 @@ +export const parameters: {} = { renderer: 'vue3' }; +export { render, renderToCanvas } from './render'; +export { decorateStory as applyDecorators } from './decorateStory'; diff --git a/code/renderers/vue3/src/preset.ts b/code/renderers/vue3/src/preset.ts new file mode 100644 index 000000000000..b60ba9208510 --- /dev/null +++ b/code/renderers/vue3/src/preset.ts @@ -0,0 +1,12 @@ +import type { StorybookConfig } from '@storybook/types'; +import { join } from 'path'; + +export const previewAnnotations: StorybookConfig['previewAnnotations'] = async (input, options) => { + const docsEnabled = Object.keys(await options.presets.apply('docs', {}, options)).length > 0; + const result: string[] = []; + + return result + .concat(input) + .concat([join(__dirname, 'entry-preview.mjs')]) + .concat(docsEnabled ? [join(__dirname, 'entry-preview-docs.mjs')] : []); +}; diff --git a/code/renderers/vue3/template/cli/js/Page.stories.js b/code/renderers/vue3/template/cli/js/Page.stories.js index f11ff44e5bdf..3c4ce9312539 100644 --- a/code/renderers/vue3/template/cli/js/Page.stories.js +++ b/code/renderers/vue3/template/cli/js/Page.stories.js @@ -1,4 +1,4 @@ -import { within, userEvent } from '@storybook/testing-library'; +import { within, userEvent, expect } from '@storybook/test'; import MyPage from './Page.vue'; export default { @@ -22,9 +22,12 @@ export const LoggedIn = { }), play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const loginButton = await canvas.getByRole('button', { - name: /Log in/i, - }); + const loginButton = canvas.getByRole('button', { name: /Log in/i }); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); + await expect(loginButton).not.toBeInTheDocument(); + + const logoutButton = canvas.getByRole('button', { name: /Log out/i }); + await expect(logoutButton).toBeInTheDocument(); }, }; diff --git a/code/renderers/vue3/template/cli/ts-3-8/Page.stories.ts b/code/renderers/vue3/template/cli/ts-3-8/Page.stories.ts index 1ac73cf4433d..c26e06d2d0de 100644 --- a/code/renderers/vue3/template/cli/ts-3-8/Page.stories.ts +++ b/code/renderers/vue3/template/cli/ts-3-8/Page.stories.ts @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/vue3'; -import { within, userEvent } from '@storybook/testing-library'; +import { within, userEvent, expect } from '@storybook/test'; import MyPage from './Page.vue'; const meta: Meta = { @@ -24,10 +24,13 @@ type Story = StoryObj; export const LoggedIn: Story = { play: async ({ canvasElement }: any) => { const canvas = within(canvasElement); - const loginButton = await canvas.getByRole('button', { - name: /Log in/i, - }); + const loginButton = canvas.getByRole('button', { name: /Log in/i }); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); + await expect(loginButton).not.toBeInTheDocument(); + + const logoutButton = canvas.getByRole('button', { name: /Log out/i }); + await expect(logoutButton).toBeInTheDocument(); }, }; diff --git a/code/renderers/vue3/template/cli/ts-4-9/Page.stories.ts b/code/renderers/vue3/template/cli/ts-4-9/Page.stories.ts index e9988015819d..2bf745b21f21 100644 --- a/code/renderers/vue3/template/cli/ts-4-9/Page.stories.ts +++ b/code/renderers/vue3/template/cli/ts-4-9/Page.stories.ts @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/vue3'; -import { within, userEvent } from '@storybook/testing-library'; +import { within, userEvent, expect } from '@storybook/test'; import MyPage from './Page.vue'; const meta = { @@ -24,10 +24,13 @@ type Story = StoryObj; export const LoggedIn: Story = { play: async ({ canvasElement }: any) => { const canvas = within(canvasElement); - const loginButton = await canvas.getByRole('button', { - name: /Log in/i, - }); + const loginButton = canvas.getByRole('button', { name: /Log in/i }); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); + await expect(loginButton).not.toBeInTheDocument(); + + const logoutButton = canvas.getByRole('button', { name: /Log out/i }); + await expect(logoutButton).toBeInTheDocument(); }, }; diff --git a/code/renderers/web-components/package.json b/code/renderers/web-components/package.json index 43a43b404127..6ac0d96131dd 100644 --- a/code/renderers/web-components/package.json +++ b/code/renderers/web-components/package.json @@ -29,11 +29,9 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./preview": { - "types": "./dist/config.d.ts", - "require": "./dist/config.js", - "import": "./dist/config.mjs" - }, + "./preset": "./preset.js", + "./dist/entry-preview.mjs": "./dist/entry-preview.mjs", + "./dist/entry-preview-docs.mjs": "./dist/entry-preview-docs.mjs", "./package.json": "./package.json" }, "main": "dist/index.js", @@ -82,7 +80,9 @@ "bundler": { "entries": [ "./src/index.ts", - "./src/config.ts" + "./src/preset.ts", + "./src/entry-preview.ts", + "./src/entry-preview-docs.ts" ], "platform": "browser" }, diff --git a/code/renderers/web-components/preset.js b/code/renderers/web-components/preset.js new file mode 100644 index 000000000000..a83f95279e7f --- /dev/null +++ b/code/renderers/web-components/preset.js @@ -0,0 +1 @@ +module.exports = require('./dist/preset'); diff --git a/code/renderers/web-components/preview.js b/code/renderers/web-components/preview.js deleted file mode 100644 index fe48d8883ba4..000000000000 --- a/code/renderers/web-components/preview.js +++ /dev/null @@ -1 +0,0 @@ -export * from './dist/config'; diff --git a/code/renderers/web-components/src/config.ts b/code/renderers/web-components/src/config.ts deleted file mode 100644 index abf93dbc12f2..000000000000 --- a/code/renderers/web-components/src/config.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { parameters as docsParams } from './docs/config'; - -export const parameters: {} = { renderer: 'web-components' as const, ...docsParams }; -export { decorators, argTypesEnhancers } from './docs/config'; -export { render, renderToCanvas } from './render'; diff --git a/code/renderers/web-components/src/docs/config.ts b/code/renderers/web-components/src/docs/config.ts deleted file mode 100644 index 3597f99cd4cc..000000000000 --- a/code/renderers/web-components/src/docs/config.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { Addon_DecoratorFunction, ArgTypesEnhancer } from '@storybook/types'; -import { SourceType, enhanceArgTypes } from '@storybook/docs-tools'; -import { extractArgTypes, extractComponentDescription } from './custom-elements'; -import { sourceDecorator } from './sourceDecorator'; -import type { StoryFnHtmlReturnType } from '../types'; - -export const decorators: Addon_DecoratorFunction[] = [sourceDecorator]; - -export const parameters: object = { - docs: { - extractArgTypes, - extractComponentDescription, - story: { inline: true }, - source: { - type: SourceType.DYNAMIC, - language: 'html', - }, - }, -}; - -export const argTypesEnhancers: ArgTypesEnhancer[] = [enhanceArgTypes]; diff --git a/code/renderers/web-components/src/entry-preview-docs.ts b/code/renderers/web-components/src/entry-preview-docs.ts new file mode 100644 index 000000000000..917c0c17eb84 --- /dev/null +++ b/code/renderers/web-components/src/entry-preview-docs.ts @@ -0,0 +1,21 @@ +import type { ArgTypesEnhancer, DecoratorFunction } from '@storybook/types'; +import { SourceType, enhanceArgTypes } from '@storybook/docs-tools'; +import { extractArgTypes, extractComponentDescription } from './docs/custom-elements'; +import { sourceDecorator } from './docs/sourceDecorator'; +import type { WebComponentsRenderer } from './types'; + +export const decorators: DecoratorFunction[] = [sourceDecorator]; + +export const parameters = { + docs: { + extractArgTypes, + extractComponentDescription, + story: { inline: true }, + source: { + type: SourceType.DYNAMIC, + language: 'html', + }, + }, +}; + +export const argTypesEnhancers: ArgTypesEnhancer[] = [enhanceArgTypes]; diff --git a/code/renderers/web-components/src/entry-preview.ts b/code/renderers/web-components/src/entry-preview.ts new file mode 100644 index 000000000000..c0a759dcab8a --- /dev/null +++ b/code/renderers/web-components/src/entry-preview.ts @@ -0,0 +1,2 @@ +export const parameters: {} = { renderer: 'web-components' }; +export { render, renderToCanvas } from './render'; diff --git a/code/renderers/web-components/src/preset.ts b/code/renderers/web-components/src/preset.ts new file mode 100644 index 000000000000..b60ba9208510 --- /dev/null +++ b/code/renderers/web-components/src/preset.ts @@ -0,0 +1,12 @@ +import type { StorybookConfig } from '@storybook/types'; +import { join } from 'path'; + +export const previewAnnotations: StorybookConfig['previewAnnotations'] = async (input, options) => { + const docsEnabled = Object.keys(await options.presets.apply('docs', {}, options)).length > 0; + const result: string[] = []; + + return result + .concat(input) + .concat([join(__dirname, 'entry-preview.mjs')]) + .concat(docsEnabled ? [join(__dirname, 'entry-preview-docs.mjs')] : []); +}; diff --git a/code/ui/blocks/src/controls/Boolean.stories.tsx b/code/ui/blocks/src/controls/Boolean.stories.tsx index 001a13dcaef7..54344b5850d4 100644 --- a/code/ui/blocks/src/controls/Boolean.stories.tsx +++ b/code/ui/blocks/src/controls/Boolean.stories.tsx @@ -1,6 +1,6 @@ import { expect } from '@storybook/jest'; import type { Meta, StoryObj } from '@storybook/react'; -import { within, fireEvent } from '@storybook/testing-library'; +import { within, fireEvent, waitFor } from '@storybook/testing-library'; import { addons } from '@storybook/preview-api'; import { RESET_STORY_ARGS, STORY_ARGS_UPDATED } from '@storybook/core-events'; import { BooleanControl } from './Boolean'; @@ -15,7 +15,6 @@ const meta = { info: 'This is info for the Boolean control stories', jsx: { useBooleanShorthandSyntax: false }, }, - args: { name: 'boolean' }, } as Meta; export default meta; @@ -23,25 +22,29 @@ export default meta; export const True: StoryObj = { args: { value: true, + name: 'True', }, }; export const False: StoryObj = { args: { value: false, + name: 'False', }, }; export const Undefined: StoryObj = { args: { value: undefined, + name: 'Undefined', }, }; export const Toggling: StoryObj = { args: { value: undefined, + name: 'Toggling', }, - play: async ({ canvasElement, id }) => { + play: async ({ canvasElement, id, args, step }) => { const channel = addons.getChannel(); channel.emit(RESET_STORY_ARGS, { storyId: id }); @@ -50,28 +53,37 @@ export const Toggling: StoryObj = { }); const canvas = within(canvasElement); + await step('Change from Undefined to False', async () => { + const setBooleanControl = canvas.getByText('Set boolean'); + await fireEvent.click(setBooleanControl); - // from Undefined to False - const setBooleanControl = canvas.getByText('Set boolean'); - await fireEvent.click(setBooleanControl); - - let toggle = await canvas.findByTitle('Change to true'); - expect(toggle).toBeInTheDocument(); + const toggle = await canvas.findByLabelText(args.name); + await expect(toggle).toBeVisible(); + }); - // from False to True - await fireEvent.click(toggle); - toggle = await canvas.findByTitle('Change to false'); - expect(toggle).toBeInTheDocument(); + await step('Change from False to True', async () => { + const toggle = canvas.getByRole('switch'); + await fireEvent.click(toggle); + await waitFor(async () => { + await expect(toggle).toBeChecked(); + }); + }); - // from True to False - await fireEvent.click(toggle); - toggle = await canvas.findByTitle('Change to true'); - expect(toggle).toBeInTheDocument(); + await step('Change from True to False', async () => { + const toggle = canvas.getByRole('switch'); + await fireEvent.click(toggle); + await waitFor(async () => { + await expect(toggle).not.toBeChecked(); + }); + }); }, }; export const TogglingInDocs: StoryObj = { ...Toggling, + args: { + name: 'Toggling In Docs', + }, parameters: { docs: { autoplay: true, diff --git a/code/ui/blocks/src/controls/Boolean.tsx b/code/ui/blocks/src/controls/Boolean.tsx index 9d591d1a9c75..54b71b0b30b8 100644 --- a/code/ui/blocks/src/controls/Boolean.tsx +++ b/code/ui/blocks/src/controls/Boolean.tsx @@ -112,16 +112,17 @@ export const BooleanControl: FC = ({ name, value, onChange, onBlur const parsedValue = typeof value === 'string' ? parse(value) : value; return ( -