Merge remote-tracking branch 'origin/next' into jeppe/fix-svelte-in-docs

This commit is contained in:
Tom Coleman 2022-11-04 10:34:35 +11:00
commit ce95c52e32
33 changed files with 635 additions and 179 deletions

View File

@ -1,5 +1,12 @@
version: 2.1
parameters:
workflow:
description: Which workflow to run
type: enum
enum: ['tests', 'daily-tests']
default: 'tests'
executors:
sb_node_16_classic:
parameters:
@ -169,7 +176,7 @@ jobs:
destination: sb-bench.tar.gz
react-vite-bench:
executor:
class: medium
class: large
name: sb_playwright
working_directory: /tmp/storybook
steps:
@ -246,12 +253,12 @@ jobs:
name: Test
command: |
cd scripts
yarn test --coverage --runInBand --ci
yarn test --coverage --ci
- store_test_results:
path: scripts/junit.xml
unit-tests:
executor:
class: medium+
class: xlarge
name: sb_node_16_browsers
steps:
- git-shallow-clone/checkout_advanced:
@ -262,7 +269,7 @@ jobs:
name: Test
command: |
cd code
yarn test --coverage --runInBand --ci
yarn test --coverage --ci
- store_test_results:
path: code/junit.xml
- persist_to_workspace:
@ -302,10 +309,18 @@ jobs:
path: test-results
## new workflow
create-sandboxes:
parameters:
parallelism:
type: integer
default: 9
cadence:
type: enum
enum: [ "ci", "daily", "weekly" ]
default: "ci"
executor:
class: medium
name: sb_node_16_browsers
parallelism: 9
parallelism: << parameters.parallelism >>
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
@ -313,7 +328,7 @@ jobs:
at: .
- run:
name: Creating Sandboxes
command: yarn task --task sandbox --template $(yarn get-template ci create) --no-link --start-from=never --junit
command: yarn task --task sandbox --template $(yarn get-template << parameters.cadence >> create) --no-link --start-from=never --junit
- persist_to_workspace:
root: .
paths:
@ -321,10 +336,18 @@ jobs:
- store_test_results:
path: test-results
smoke-test-sandboxes:
parameters:
parallelism:
type: integer
default: 9
cadence:
type: enum
enum: [ "ci", "daily", "weekly" ]
default: "ci"
executor:
class: medium
name: sb_node_16_browsers
parallelism: 9
parallelism: << parameters.parallelism >>
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
@ -332,14 +355,22 @@ jobs:
at: .
- run:
name: Smoke Testing Sandboxes
command: yarn task --task smoke-test --template $(yarn get-template ci smoke-test) --no-link --start-from=never --junit
command: yarn task --task smoke-test --template $(yarn get-template << parameters.cadence >> smoke-test) --no-link --start-from=never --junit
- store_test_results:
path: test-results
build-sandboxes:
parameters:
parallelism:
type: integer
default: 9
cadence:
type: enum
enum: [ "ci", "daily", "weekly" ]
default: "ci"
executor:
class: medium+
name: sb_node_16_browsers
parallelism: 9
parallelism: << parameters.parallelism >>
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
@ -347,7 +378,7 @@ jobs:
at: .
- run:
name: Building Sandboxes
command: yarn task --task build --template $(yarn get-template ci build) --no-link --start-from=never --junit
command: yarn task --task build --template $(yarn get-template << parameters.cadence >> build) --no-link --start-from=never --junit
- store_test_results:
path: test-results
- persist_to_workspace:
@ -355,10 +386,18 @@ jobs:
paths:
- sandbox/*/storybook-static
test-runner-sandboxes:
parameters:
parallelism:
type: integer
default: 9
cadence:
type: enum
enum: [ "ci", "daily", "weekly" ]
default: "ci"
executor:
class: medium
name: sb_playwright
parallelism: 9
parallelism: << parameters.parallelism >>
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
@ -366,29 +405,44 @@ jobs:
at: .
- run:
name: Running Test Runner
command: yarn task --task test-runner --template $(yarn get-template ci test-runner) --no-link --start-from=never --junit
command: yarn task --task test-runner --template $(yarn get-template << parameters.cadence >> test-runner) --no-link --start-from=never --junit
- store_test_results:
path: test-results
chromatic-sandboxes:
parameters:
parallelism:
type: integer
default: 9
cadence:
type: enum
enum: ["ci", "daily", "weekly"]
default: "ci"
executor:
class: medium
name: sb_node_16_browsers
parallelism: 9
parallelism: << parameters.parallelism >>
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
- checkout
- attach_workspace:
at: .
- run:
name: Running Chromatic
command: yarn task --task chromatic --template $(yarn get-template ci chromatic) --no-link --start-from=never --junit
command: yarn task --task chromatic --template $(yarn get-template << parameters.cadence >> chromatic) --no-link --start-from=never --junit
- store_test_results:
path: test-results
e2e-sandboxes:
parameters:
parallelism:
type: integer
default: 9
cadence:
type: enum
enum: ["ci", "daily", "weekly"]
default: "ci"
executor:
class: medium
name: sb_playwright
parallelism: 9
parallelism: << parameters.parallelism >>
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
@ -396,7 +450,7 @@ jobs:
at: .
- run:
name: Running E2E Tests
command: yarn task --task e2e-tests --template $(yarn get-template ci e2e-tests) --no-link --start-from=never --junit
command: yarn task --task e2e-tests --template $(yarn get-template << parameters.cadence >> e2e-tests) --no-link --start-from=never --junit
- store_test_results:
path: test-results
- store_artifacts: # this is where playwright puts more complex stuff
@ -404,7 +458,45 @@ jobs:
destination: playwright
workflows:
daily-tests:
when:
equal: [ daily-tests, << pipeline.parameters.workflow >> ]
jobs:
- build
- publish:
requires:
- build
- create-sandboxes:
parallelism: 24
cadence: "daily"
requires:
- publish
# - smoke-test-sandboxes: # disabled for now
# requires:
# - create-sandboxes
- build-sandboxes:
parallelism: 24
cadence: "daily"
requires:
- create-sandboxes
- test-runner-sandboxes:
parallelism: 24
cadence: "daily"
requires:
- build-sandboxes
- chromatic-sandboxes:
parallelism: 24
cadence: "daily"
requires:
- build-sandboxes
- e2e-sandboxes:
parallelism: 24
cadence: "daily"
requires:
- build-sandboxes
test:
when:
equal: [ tests, << pipeline.parameters.workflow >> ]
jobs:
- build
- lint:

View File

@ -1,8 +0,0 @@
{
"printWidth": 100,
"tabWidth": 2,
"bracketSpacing": true,
"trailingComma": "es5",
"singleQuote": true,
"arrowParens": "always"
}

View File

@ -40,7 +40,7 @@
"./react": {
"require": "./dist/react/index.js",
"import": "./dist/react/index.mjs",
"types": "./dist/react.d.ts"
"types": "./dist/react/index.d.ts"
},
"./register": {
"require": "./dist/manager.js",

View File

@ -41,7 +41,7 @@
"./preview": {
"require": "./dist/preset/preview.js",
"import": "./dist/preset/preview.mjs",
"types": "./dist/preview.d.ts"
"types": "./dist/preset/preview.d.ts"
},
"./register": {
"require": "./dist/manager.js",

View File

@ -32,7 +32,7 @@ window.__STORYBOOK_CLIENT_API__ = new ClientApi({ storyStore: preview.storyStore
preview.initialize({ importFn, getProjectAnnotations });
if (module.hot) {
if (import.meta.webpackHot) {
import.meta.webpackHot.accept('./{{storiesFilename}}', () => {
// importFn has changed so we need to patch the new one in
preview.onStoriesChanged({ importFn });

View File

@ -0,0 +1,61 @@
import chalk from 'chalk';
import { dedent } from 'ts-dedent';
import type { ConfigFile } from '@storybook/csf-tools';
import { readConfig, writeConfig } from '@storybook/csf-tools';
import { getStorybookInfo } from '@storybook/core-common';
import type { Fix } from '../types';
const logger = console;
interface DocsPageAutomaticFrameworkRunOptions {
main: ConfigFile;
}
/**
* Set the docs.docsPage option to automatic if it isn't already set
*/
export const docsPageAutomatic: Fix<DocsPageAutomaticFrameworkRunOptions> = {
id: 'docsPageAutomatic',
async check({ packageManager }) {
const packageJson = packageManager.retrievePackageJson();
const { mainConfig } = getStorybookInfo(packageJson);
if (!mainConfig) {
logger.warn('Unable to find storybook main.js config, skipping');
return null;
}
const main = await readConfig(mainConfig);
const docs = main.getFieldValue(['docs']);
return docs?.docsPage === undefined ? { main } : null;
},
prompt() {
const docsPageAutomaticFormatted = chalk.cyan(`docs: { docsPage: 'automatic' }`);
return dedent`
We've detected that your main.js configuration file has not configured docsPage. In 6.x we
we defaulted to having a docsPage for every story, in 7.x you need to opt in per-component.
However, we can set the \`docs.docsPage\` to 'automatic' to approximate the old behaviour:
${docsPageAutomaticFormatted}
More info: ${chalk.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#docs-page'
)}
`;
},
async run({ result: { main }, dryRun }) {
logger.info(`✅ Setting 'docs.docsPage' to 'automatic' in main.js`);
if (!dryRun) {
main.setFieldValue(['docsPage', 'docs'], 'automatic');
await writeConfig(main);
}
},
};

View File

@ -12,6 +12,7 @@ import { sbScripts } from './sb-scripts';
import { newFrameworks } from './new-frameworks';
import { removedGlobalClientAPIs } from './remove-global-client-apis';
import { mdx1to2 } from './mdx-1-to-2';
import { docsPageAutomatic } from './docsPage-automatic';
export * from '../types';
@ -28,4 +29,5 @@ export const fixes: Fix[] = [
newFrameworks,
removedGlobalClientAPIs,
mdx1to2,
docsPageAutomatic,
];

View File

@ -191,12 +191,12 @@ export class StoryStoreFacade<TFramework extends AnyFramework> {
// NOTE: this logic is equivalent to the `extractStories` function of `StoryIndexGenerator`
const docsOptions = (global.DOCS_OPTIONS || {}) as DocsOptions;
const docsPageOptedIn =
docsOptions.docsPage === 'automatic' ||
(docsOptions.docsPage && componentTags.includes('docsPage'));
if (docsOptions.enabled && storyExports.length) {
// We will use tags soon and this crappy filename test will go away
if (
fileName.match(/\.mdx$/) ||
(docsOptions.docsPage && componentTags.includes('docsPage'))
) {
if (fileName.match(/\.mdx$/) || docsPageOptedIn) {
const name = docsOptions.defaultName;
const docsId = toId(componentId || title, name);
this.entries[docsId] = {

View File

@ -11,4 +11,6 @@ export {
setGlobalRender,
} from './ClientApi';
export * from '@storybook/store';
export * from './queryparams';

View File

@ -46,7 +46,7 @@
"globby": "^11.0.2",
"jscodeshift": "^0.13.1",
"lodash": "^4.17.21",
"prettier": ">=2.2.1 <=2.3.0",
"prettier": ">=2.2.1 <=3.0.0",
"recast": "^0.19.0",
"util": "^0.12.4"
},

View File

@ -1295,6 +1295,50 @@ describe('start', () => {
`);
});
});
describe('when docsOptions.docsPage = automatic', () => {
beforeEach(() => {
global.DOCS_OPTIONS = { enabled: true, docsPage: 'automatic', defaultName: 'Docs' };
});
it('adds stories for each component with docsPage tag', async () => {
const renderToDOM = jest.fn();
const { configure, clientApi } = start(renderToDOM);
configure('test', () => {
(clientApi as any).addParameters({
docs: { renderer: () => ({ render: jest.fn((_, _2, _3, d) => d()) }) },
});
clientApi
.storiesOf('Component A', { id: 'file1' } as NodeModule)
.add('Story One', jest.fn())
.add('Story Two', jest.fn());
clientApi
.storiesOf('Component B', { id: 'file2' } as NodeModule)
.addParameters({ tags: ['docsPage'] })
.add('Story Three', jest.fn());
return [componentCExports];
});
await waitForRender();
const setIndexData = mockChannel.emit.mock.calls.find(
(call: [string, any]) => call[0] === SET_INDEX
)[1];
expect(Object.keys(setIndexData.entries)).toMatchInlineSnapshot(`
Array [
"component-a--docs",
"component-a--story-one",
"component-a--story-two",
"component-b--docs",
"component-b--story-three",
"component-c--docs",
"component-c--story-one",
"component-c--story-two",
]
`);
});
});
});
describe('auto-title', () => {

View File

@ -441,6 +441,39 @@ describe('StoryIndexGenerator', () => {
`);
});
it('generates an entry for every CSF file when docsOptions.docsPage = automatic', async () => {
const specifier: CoreCommon_NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./src/**/*.stories.(ts|js|jsx)',
options
);
const generator = new StoryIndexGenerator([specifier], {
...docsPageOptions,
docs: {
...docsPageOptions.docs,
docsPage: 'automatic',
},
});
await generator.initialize();
expect(Object.keys((await generator.getIndex()).entries)).toMatchInlineSnapshot(`
Array [
"a--docs",
"a--story-one",
"b--docs",
"b--story-one",
"d--docs",
"d--story-one",
"first-nested-deeply-f--docs",
"first-nested-deeply-f--story-one",
"nested-button--docs",
"nested-button--story-one",
"second-nested-g--docs",
"second-nested-g--story-one",
]
`);
});
it('does not generate a docs page entry if there is a standalone entry with the same name', async () => {
const csfSpecifier: CoreCommon_NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./src/A.stories.js',

View File

@ -224,12 +224,12 @@ export class StoryIndexGenerator {
});
if (this.options.docs.enabled && csf.stories.length) {
const { docsPage } = this.options.docs;
const docsPageOptedIn =
docsPage === 'automatic' || (docsPage && componentTags.includes('docsPage'));
// We always add a template for *.stories.mdx, but only if docs page is enabled for
// regular CSF files
if (
storyIndexer.addDocsTemplate ||
(this.options.docs.docsPage && componentTags.includes('docsPage'))
) {
if (storyIndexer.addDocsTemplate || docsPageOptedIn) {
const name = this.options.docs.defaultName;
const id = toId(csf.meta.title, name);
entries.unshift({

View File

@ -1,3 +1,5 @@
/// <reference types="@types/jest" />;
import { dedent } from 'ts-dedent';
import { formatConfig, loadConfig } from './ConfigFile';
@ -83,6 +85,7 @@ describe('ConfigFile', () => {
).toEqual('webpack5');
});
});
describe('module exports', () => {
it('missing export', () => {
expect(
@ -142,6 +145,66 @@ describe('ConfigFile', () => {
).toEqual([{ directory: '../src', titlePrefix: 'Demo' }]);
});
});
describe('default export', () => {
it('missing export', () => {
expect(
getField(
['core', 'builder'],
dedent`
export default { foo: { builder: 'webpack5' } }
`
)
).toBeUndefined();
});
it('found scalar', () => {
expect(
getField(
['core', 'builder'],
dedent`
export default { core: { builder: 'webpack5' } }
`
)
).toEqual('webpack5');
});
it('variable ref export', () => {
expect(
getField(
['core', 'builder'],
dedent`
const core = { builder: 'webpack5' };
export default { core };
`
)
).toEqual('webpack5');
});
it('variable rename', () => {
expect(
getField(
['core', 'builder'],
dedent`
const coreVar = { builder: 'webpack5' };
export default { core: coreVar };
`
)
).toEqual('webpack5');
});
it('variable exports', () => {
expect(
getField(
['stories'],
dedent`
import type { StorybookConfig } from '@storybook/react-webpack5';
const config: StorybookConfig = {
stories: [{ directory: '../src', titlePrefix: 'Demo' }],
}
export default config;
`
)
).toEqual([{ directory: '../src', titlePrefix: 'Demo' }]);
});
});
});
describe('setField', () => {
@ -228,6 +291,7 @@ describe('ConfigFile', () => {
`);
});
});
describe('module exports', () => {
it('missing export', () => {
expect(
@ -283,6 +347,63 @@ describe('ConfigFile', () => {
`);
});
});
describe('default export', () => {
it('missing export', () => {
expect(
setField(
['core', 'builder'],
'webpack5',
dedent`
export default { addons: [] };
`
)
).toMatchInlineSnapshot(`
export default {
addons: [],
core: {
builder: "webpack5"
}
};
`);
});
it('missing field', () => {
expect(
setField(
['core', 'builder'],
'webpack5',
dedent`
export default { core: { foo: 'bar' }};
`
)
).toMatchInlineSnapshot(`
export default {
core: {
foo: 'bar',
builder: 'webpack5'
}
};
`);
});
it('found scalar', () => {
expect(
setField(
['core', 'builder'],
'webpack5',
dedent`
export default { core: { builder: 'webpack4' } };
`
)
).toMatchInlineSnapshot(`
export default {
core: {
builder: 'webpack5'
}
};
`);
});
});
describe('quotes', () => {
it('no quotes', () => {
expect(setField(['foo', 'bar'], 'baz', '')).toMatchInlineSnapshot(`

View File

@ -108,6 +108,30 @@ export class ConfigFile {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
traverse.default(this._ast, {
ExportDefaultDeclaration: {
enter({ node, parent }) {
const decl =
t.isIdentifier(node.declaration) && t.isProgram(parent)
? _findVarInitialization(node.declaration.name, parent)
: node.declaration;
if (t.isObjectExpression(decl)) {
self._exportsObject = decl;
decl.properties.forEach((p: t.ObjectProperty) => {
const exportName = propKey(p);
if (exportName) {
let exportVal = p.value;
if (t.isIdentifier(exportVal)) {
exportVal = _findVarInitialization(exportVal.name, parent as t.Program);
}
self._exports[exportName] = exportVal as t.Expression;
}
});
} else {
logger.warn(`Unexpected ${JSON.stringify(node)}`);
}
},
},
ExportNamedDeclaration: {
enter({ node, parent }) {
if (t.isVariableDeclaration(node.declaration)) {

View File

@ -47,7 +47,7 @@
"@storybook/types": "7.0.0-alpha.47",
"estraverse": "^5.2.0",
"lodash": "^4.17.21",
"prettier": ">=2.2.1 <=2.3.0"
"prettier": ">=2.2.1 <=3.0.0"
},
"devDependencies": {
"jest-specific-snapshot": "^4.0.0",

View File

@ -1,6 +1,37 @@
import { SHARED_STATE_CHANGED, SHARED_STATE_SET } from '@storybook/core-events';
import { addons, useMemo, useState, useEffect, useChannel } from '@storybook/addons';
import {
addons,
HooksContext,
applyHooks,
useMemo,
useCallback,
useRef,
useState,
useReducer,
useEffect,
useChannel,
useStoryContext,
useParameter,
useArgs,
useGlobals,
} from '@storybook/addons';
export {
HooksContext,
applyHooks,
useMemo,
useCallback,
useRef,
useState,
useReducer,
useEffect,
useChannel,
useStoryContext,
useParameter,
useArgs,
useGlobals,
};
export function useSharedState<S>(sharedId: string, defaultState?: S): [S, (s: S) => void] {
const channel = addons.getChannel();

View File

@ -0,0 +1,50 @@
import type { PackageJson, StorybookConfig } from '@storybook/types';
import { getActualPackageJson } from './package-json';
const knownRenderers = [
'html',
'react',
'svelte',
'vue3',
'preact',
'server',
'vue',
'web-components',
'angular',
'ember',
];
const knownBuilders = ['builder-webpack5', 'builder-vite'];
function findMatchingPackage(packageJson: PackageJson, suffixes: string[]) {
const { name = '', version, dependencies, devDependencies, peerDependencies } = packageJson;
const allDependencies = {
// We include the framework itself because it may be a renderer too (e.g. angular)
[name]: version,
...dependencies,
...devDependencies,
...peerDependencies,
};
return suffixes.map((suffix) => `@storybook/${suffix}`).find((pkg) => allDependencies[pkg]);
}
export async function getFrameworkInfo(mainConfig: StorybookConfig) {
const { framework: frameworkInput } = mainConfig;
if (!frameworkInput) return {};
const framework = typeof frameworkInput === 'string' ? { name: frameworkInput } : frameworkInput;
const frameworkPackageJson = await getActualPackageJson(framework.name);
const builder = findMatchingPackage(frameworkPackageJson, knownBuilders);
const renderer = findMatchingPackage(frameworkPackageJson, knownRenderers);
return {
framework,
builder,
renderer,
};
}

View File

@ -9,8 +9,7 @@ export const getActualPackageVersions = async (packages: Record<string, Partial<
export const getActualPackageVersion = async (packageName: string) => {
try {
// eslint-disable-next-line import/no-dynamic-require,global-require
const packageJson = require(path.join(packageName, 'package.json'));
const packageJson = await getActualPackageJson(packageName);
return {
name: packageName,
version: packageJson.version,
@ -19,3 +18,9 @@ export const getActualPackageVersion = async (packageName: string) => {
return { name: packageName, version: null };
}
};
export const getActualPackageJson = async (packageName: string) => {
// eslint-disable-next-line import/no-dynamic-require,global-require
const packageJson = require(path.join(packageName, 'package.json'));
return packageJson;
};

View File

@ -12,7 +12,7 @@ const mainJsMock: StorybookConfig = {
stories: [],
};
jest.mock('./package-versions', () => {
jest.mock('./package-json', () => {
const getActualPackageVersion = jest.fn((name) =>
Promise.resolve({
name,
@ -24,9 +24,17 @@ jest.mock('./package-versions', () => {
Promise.all(Object.keys(packages).map(getActualPackageVersion))
);
const getActualPackageJson = jest.fn((name) => ({
dependencies: {
'@storybook/react': 'x.x.x',
'@storybook/builder-vite': 'x.x.x',
},
}));
return {
getActualPackageVersions,
getActualPackageVersion,
getActualPackageJson,
};
});
@ -96,40 +104,39 @@ describe('sanitizeAddonName', () => {
describe('await computeStorybookMetadata', () => {
test('should return frameworkOptions from mainjs', async () => {
const reactResult = await computeStorybookMetadata({
packageJson: {
...packageJsonMock,
devDependencies: {
'@storybook/react': 'x.x.x',
},
},
packageJson: packageJsonMock,
mainConfig: {
...mainJsMock,
reactOptions: {
fastRefresh: false,
framework: {
name: '@storybook/react-vite',
options: {
fastRefresh: false,
},
},
},
});
expect(reactResult.framework).toEqual({ name: 'react', options: { fastRefresh: false } });
expect(reactResult.framework).toEqual({
name: '@storybook/react-vite',
options: { fastRefresh: false },
});
const angularResult = await computeStorybookMetadata({
packageJson: {
...packageJsonMock,
devDependencies: {
'@storybook/angular': 'x.x.x',
},
},
packageJson: packageJsonMock,
mainConfig: {
...mainJsMock,
angularOptions: {
enableIvy: true,
enableNgcc: true,
framework: {
name: '@storybook/angular',
options: {
enableIvy: true,
enableNgcc: true,
},
},
},
});
expect(angularResult.framework).toEqual({
name: 'angular',
name: '@storybook/angular',
options: { enableIvy: true, enableNgcc: true },
});
});
@ -196,40 +203,20 @@ describe('await computeStorybookMetadata', () => {
expect(result.features).toEqual(features);
});
test('should handle different types of builders', async () => {
const simpleBuilder = 'webpack4';
const complexBuilder = {
name: 'webpack5',
options: {
lazyCompilation: true,
},
};
test('should infer builder and renderer from framework package.json', async () => {
expect(
(
await computeStorybookMetadata({
packageJson: packageJsonMock,
mainConfig: {
...mainJsMock,
core: {
builder: complexBuilder,
},
},
})
).builder
).toEqual(complexBuilder);
expect(
(
await computeStorybookMetadata({
packageJson: packageJsonMock,
mainConfig: {
...mainJsMock,
core: {
builder: simpleBuilder,
},
},
})
).builder
).toEqual({ name: simpleBuilder });
await computeStorybookMetadata({
packageJson: packageJsonMock,
mainConfig: {
...mainJsMock,
framework: '@storybook/react-vite',
},
})
).toMatchObject({
framework: { name: '@storybook/react-vite' },
renderer: '@storybook/react',
builder: '@storybook/builder-vite',
});
});
test('should return the number of refs', async () => {

View File

@ -9,9 +9,10 @@ import {
import type { StorybookConfig, PackageJson } from '@storybook/types';
import type { StorybookMetadata, Dependency, StorybookAddon } from './types';
import { getActualPackageVersion, getActualPackageVersions } from './package-versions';
import { getActualPackageVersion, getActualPackageVersions } from './package-json';
import { getMonorepoType } from './get-monorepo-type';
import { cleanPaths } from './sanitize';
import { getFrameworkInfo } from './get-framework-info';
export const metaFrameworks = {
next: 'Next',
@ -23,31 +24,6 @@ export const metaFrameworks = {
'@sveltejs/kit': 'svelte-kit',
} as Record<string, string>;
// @TODO: This should be removed in 7.0 as the framework.options field in main.js will replace this
const getFrameworkOptions = (mainConfig: any) => {
const possibleOptions = [
'angular',
'ember',
'html',
'preact',
'react',
'server',
'svelte',
'vue',
'vue3',
'webComponents',
].map((opt) => `${opt}Options`);
// eslint-disable-next-line no-restricted-syntax
for (const opt of possibleOptions) {
if (opt in mainConfig) {
return mainConfig[opt] as any;
}
}
return undefined;
};
export const sanitizeAddonName = (name: string) => {
return cleanPaths(name)
.replace(/\/dist\/.*/, '')
@ -68,7 +44,6 @@ export const computeStorybookMetadata = async ({
}): Promise<StorybookMetadata> => {
const metadata: Partial<StorybookMetadata> = {
generatedAt: new Date().getTime(),
builder: { name: 'webpack5' },
hasCustomBabel: false,
hasCustomWebpack: false,
hasStaticDirs: false,
@ -118,14 +93,7 @@ export const computeStorybookMetadata = async ({
metadata.typescriptOptions = mainConfig.typescript;
}
if (mainConfig.core?.builder) {
const { builder } = mainConfig.core;
metadata.builder = {
name: typeof builder === 'string' ? builder : builder.name,
options: typeof builder === 'string' ? undefined : builder?.options ?? undefined,
};
}
const frameworkInfo = await getFrameworkInfo(mainConfig);
if (mainConfig.refs) {
metadata.refCount = Object.keys(mainConfig.refs).length;
@ -181,22 +149,16 @@ export const computeStorybookMetadata = async ({
const hasStorybookEslint = !!allDependencies['eslint-plugin-storybook'];
// FIXME: resolve framework/renderer split in 7.0
// OR should be getting this from mainConfig instead?
const storybookInfo = getStorybookInfo(packageJson);
const storybookVersion =
storybookPackages[storybookInfo.frameworkPackage]?.version || storybookInfo.version;
return {
...metadata,
...frameworkInfo,
storybookVersion,
language,
storybookPackages,
framework: {
name: storybookInfo.framework,
options: getFrameworkOptions(mainConfig),
},
addons,
hasStorybookEslint,
};

View File

@ -17,14 +17,12 @@ export type StorybookMetadata = {
storybookVersion: string;
generatedAt?: number;
language: 'typescript' | 'javascript';
framework: {
framework?: {
name: string;
options?: any;
};
builder?: {
name: string;
options?: Record<string, any>;
};
builder?: string;
renderer?: string;
monorepo?: MonorepoType;
packageManager?: {
type: PM;

View File

@ -296,9 +296,10 @@ export type DocsOptions = {
*/
defaultName?: string;
/**
* Should we generate a docs entry per CSF file?
* Should we generate a docs entry per CSF file with the `docsPage` tag?
* Set to 'automatic' to generate an entry irrespective of tag.
*/
docsPage?: boolean;
docsPage?: boolean | 'automatic';
/**
* Only show doc entries in the side bar (usually set with the `--docs` CLI flag)
*/

View File

@ -1,3 +1,5 @@
import { parameters as docsParams } from './docs/config';
export { renderToDOM, render } from './render';
export const parameters = { framework: 'preact' as const };
export const parameters = { framework: 'preact' as const, ...docsParams };

View File

@ -0,0 +1,5 @@
export const parameters = {
docs: {
inlineStories: true,
},
};

View File

@ -36,6 +36,7 @@ const config: StorybookConfig = {
viteFinal: (vite) => ({
...vite,
plugins: [...(vite.plugins || []), csfPlugin({})],
optimizeDeps: { ...vite.optimizeDeps, force: true },
}),
};

View File

@ -11,6 +11,12 @@ import {
useTheme,
} from '@storybook/theming';
import { Symbols } from '@storybook/components';
import type { PreviewWeb } from '@storybook/preview-web';
import { DocsContext } from '@storybook/preview-web';
import type { ReactFramework } from '@storybook/react';
import type { Channel } from '@storybook/channels';
import { DocsContainer } from '../blocks/src/blocks/DocsContainer';
const { document } = global;
@ -86,7 +92,49 @@ const ThemedSetRoot = () => {
return null;
};
// eslint-disable-next-line no-underscore-dangle
const preview = (window as any).__STORYBOOK_PREVIEW__ as PreviewWeb<ReactFramework>;
const channel = (window as any).__STORYBOOK_ADDONS_CHANNEL__ as Channel;
export const loaders = [
async () => ({ globalValue: 1 }),
async ({ parameters: { relativeCsfPaths } }) => {
if (!relativeCsfPaths) return {};
const csfFiles = await Promise.all(
(relativeCsfPaths as string[]).map(async (relativePath) => {
const webpackPath = `./ui/blocks/src/${relativePath.replace(/^..\//, '')}.tsx`;
const entry = preview.storyStore.storyIndex!.importPathToEntry(webpackPath);
if (!entry) {
throw new Error(`Couldn't find story file at ${webpackPath} (passed as ${relativePath})`);
}
return preview.storyStore.loadCSFFileByStoryId(entry.id);
})
);
return {
docsContext: new DocsContext(
channel,
preview.storyStore,
preview.renderStoryToElement.bind(preview),
csfFiles,
false
),
};
},
];
export const decorators = [
(Story, { loaded: { docsContext } }) =>
docsContext ? (
<DocsContainer context={docsContext}>
<Story />
</DocsContainer>
) : (
<Story />
),
(StoryFn, { globals, parameters, playFunction }) => {
const defaultTheme = isChromatic() && !playFunction ? 'stacked' : 'light';
const theme = globals.theme || parameters.theme || defaultTheme;
@ -242,7 +290,5 @@ export const globalTypes = {
},
};
export const loaders = [async () => ({ globalValue: 1 })];
export const argTypes = { color: { control: 'color' } };
export const args = { color: 'red' };

View File

@ -0,0 +1,20 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Story as StoryComponent } from './Story';
import * as BooleanStories from '../controls/Boolean.stories';
const meta: Meta<typeof StoryComponent> = {
component: StoryComponent,
parameters: {
relativeCsfPaths: ['../controls/Boolean.stories'],
},
};
export default meta;
type Story = StoryObj<typeof meta>;
export const BasicOf: Story = {
args: {
of: BooleanStories.Undefined,
},
};

View File

@ -6428,7 +6428,7 @@ __metadata:
jest-specific-snapshot: ^4.0.0
jscodeshift: ^0.13.1
lodash: ^4.17.21
prettier: ">=2.2.1 <=2.3.0"
prettier: ">=2.2.1 <=3.0.0"
recast: ^0.19.0
typescript: ~4.6.3
util: ^0.12.4
@ -7724,7 +7724,7 @@ __metadata:
estraverse: ^5.2.0
jest-specific-snapshot: ^4.0.0
lodash: ^4.17.21
prettier: ">=2.2.1 <=2.3.0"
prettier: ">=2.2.1 <=3.0.0"
typescript: ~4.6.3
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
@ -28674,7 +28674,7 @@ __metadata:
languageName: node
linkType: hard
"prettier@npm:2.7.1, prettier@npm:>=2.5, prettier@npm:^1.18.2 || ^2.0.0":
"prettier@npm:2.7.1, prettier@npm:>=2.2.1 <=3.0.0, prettier@npm:>=2.5, prettier@npm:^1.18.2 || ^2.0.0":
version: 2.7.1
resolution: "prettier@npm:2.7.1"
bin:
@ -28683,15 +28683,6 @@ __metadata:
languageName: node
linkType: hard
"prettier@npm:>=2.2.1 <=2.3.0":
version: 2.3.0
resolution: "prettier@npm:2.3.0"
bin:
prettier: bin-prettier.js
checksum: b9f434af2f25a37aad0b133894827e980885eb8bf317444c9dde0401ed2c7f463f9996d691f5ee5a0a4450ab46a894cd6557516b561e2522821522ce1f4c6668
languageName: node
linkType: hard
"pretty-bytes@npm:^5.3.0":
version: 5.6.0
resolution: "pretty-bytes@npm:5.6.0"

View File

@ -1,8 +0,0 @@
{
"printWidth": 100,
"tabWidth": 2,
"bracketSpacing": true,
"trailingComma": "es5",
"singleQuote": true,
"arrowParens": "always"
}

View File

@ -147,7 +147,7 @@
"npmlog": "^5.0.1",
"nx": "14.6.1",
"p-limit": "^3.1.0",
"prettier": ">=2.2.1 <=2.3.0",
"prettier": ">=2.2.1 <=3.0.0",
"pretty-hrtime": "^1.0.0",
"process": "^0.11.10",
"prompts": "^2.4.0",

View File

@ -3,10 +3,4 @@ const base = require('@storybook/linter-config/prettier.config');
module.exports = {
...base,
arrowParens: 'always',
overrides: [
{
files: '*.html',
options: { parser: 'babel' },
},
],
};

View File

@ -3431,7 +3431,7 @@ __metadata:
npmlog: ^5.0.1
nx: 14.6.1
p-limit: ^3.1.0
prettier: ">=2.2.1 <=2.3.0"
prettier: ">=2.2.1 <=3.0.0"
pretty-hrtime: ^1.0.0
process: ^0.11.10
prompts: ^2.4.0
@ -15669,12 +15669,12 @@ __metadata:
languageName: node
linkType: hard
"prettier@npm:>=2.2.1 <=2.3.0":
version: 2.3.0
resolution: "prettier@npm:2.3.0"
"prettier@npm:>=2.2.1 <=3.0.0":
version: 2.7.1
resolution: "prettier@npm:2.7.1"
bin:
prettier: bin-prettier.js
checksum: b9f434af2f25a37aad0b133894827e980885eb8bf317444c9dde0401ed2c7f463f9996d691f5ee5a0a4450ab46a894cd6557516b561e2522821522ce1f4c6668
checksum: 359d2b7ecf36bd52924a48331cae506d335f18637fde6c686212f952b9ce678ce9f554a80571049b36ec2897a8a6c40094b776dea371cc5c04c481cf5b78504b
languageName: node
linkType: hard