mirror of
https://github.com/storybookjs/storybook.git
synced 2025-03-20 05:02:37 +08:00
Add basic contributing instructions and improve prompts
This commit is contained in:
parent
843811a33d
commit
431162b321
@ -2,17 +2,34 @@
|
||||
|
||||
- Ensure you have node version 14 installed (suggestion: v14.18.1).
|
||||
- Ensure if you are using Windows to use the Windows Subsystem for Linux (WSL).
|
||||
- Run `./bootstrap.sh` to install the dependencies, and get the repo ready to be developed on.
|
||||
- Run `yarn start` inside of the `code` directory to start the development server.
|
||||
- Run `yarn install` to initialize the repo (it shouldn't install anything).
|
||||
- Run `yarn start` directory to run a basic test Storybook "sandbox".
|
||||
|
||||
# Generating reproductions
|
||||
# Running against different sandbox templates
|
||||
|
||||
The monorepo has a script that generates Storybook reproductions based on configurations set in the `code/lib/cli/src/repro-templates.ts` file. This makes it possible to quickly bootstrap examples with and without Storybook, for given configurations (e.g. CRA, Angular, Vue, etc.)
|
||||
The `yarn start` script will generate a Create React App TypeScript sandbox with a set of test stories inside it, as well as taking all steps required to get it running (building the various packages we need etc).
|
||||
|
||||
You can also pick a specific template to use as your sandbox by running `yarn task`, which will prompt you to make further choices about which template you want and which task you want to run.
|
||||
|
||||
# Making code changes
|
||||
|
||||
If you want to make code changes to Storybook packages while running a sandbox, you'll need to do the following:
|
||||
|
||||
1. In a second terminal run `yarn build --watch <package-1> <package-2>` in the `code/` directory. The package names is the bit after the `@storybook/` in the published package. For instance:
|
||||
|
||||
```bash
|
||||
cd code
|
||||
yarn build --watch react core-server api addon-docs
|
||||
```
|
||||
|
||||
2. If you are running the sandbox in "linked" mode (the default), you should see the changes reflected on a refresh (you may need to restart it if changing server packages)
|
||||
|
||||
3. If you are running the sandbox in "unlinked" mode you'll need to re-run the sandbox from the `publish` step to see the changes:
|
||||
|
||||
```
|
||||
yarn task --task dev --template <your template> --start-from=publish
|
||||
```
|
||||
|
||||
To do so:
|
||||
- Check the `code/lib/cli/src/repro-templates.ts` if you want to see what will be generated
|
||||
- Run `./generate-repros.sh`
|
||||
- Check the result in the `repros` directory
|
||||
|
||||
# Contributing to Storybook
|
||||
|
||||
|
@ -58,6 +58,10 @@ export type TemplateDetails = {
|
||||
type MaybePromise<T> = T | Promise<T>;
|
||||
|
||||
export type Task = {
|
||||
/**
|
||||
* A description of the task for a prompt
|
||||
*/
|
||||
description: string;
|
||||
/**
|
||||
* Does this task represent a service for another task?
|
||||
*
|
||||
@ -104,29 +108,45 @@ export const tasks = {
|
||||
chromatic,
|
||||
'e2e-tests': e2eTests,
|
||||
};
|
||||
|
||||
type TaskKey = keyof typeof tasks;
|
||||
|
||||
export const sandboxOptions = createOptions({
|
||||
template: {
|
||||
function isSandboxTask(taskKey: TaskKey) {
|
||||
return !['install', 'compile', 'publish', 'run-registry'].includes(taskKey);
|
||||
}
|
||||
|
||||
export const options = createOptions({
|
||||
task: {
|
||||
type: 'string',
|
||||
description: 'What template are you running against?',
|
||||
values: Object.keys(TEMPLATES) as TemplateKey[],
|
||||
description: 'Which task would you like to run?',
|
||||
values: Object.keys(tasks) as TaskKey[],
|
||||
valueDescriptions: Object.values(tasks).map((t) => `${t.description} (${getTaskKey(t)})`),
|
||||
required: true,
|
||||
},
|
||||
// TODO -- feature flags
|
||||
sandboxDir: {
|
||||
startFrom: {
|
||||
type: 'string',
|
||||
description: 'What is the name of the directory the sandbox runs in?',
|
||||
description: 'Which task should we reset back to?',
|
||||
values: [...(Object.keys(tasks) as TaskKey[]), 'never', 'auto'] as const,
|
||||
// This is prompted later based on information about what's ready
|
||||
promptType: false,
|
||||
},
|
||||
template: {
|
||||
type: 'string',
|
||||
description: 'What template would you like to make a sandbox for?',
|
||||
values: Object.keys(TEMPLATES) as TemplateKey[],
|
||||
promptType: (_, { task }) => isSandboxTask(task),
|
||||
},
|
||||
// // TODO -- feature flags
|
||||
// sandboxDir: {
|
||||
// type: 'string',
|
||||
// description: 'What is the name of the directory the sandbox runs in?',
|
||||
// promptType: false,
|
||||
// },
|
||||
addon: {
|
||||
type: 'string[]',
|
||||
description: 'Which extra addons (beyond the CLI defaults) would you like installed?',
|
||||
values: extraAddons,
|
||||
promptType: (_, { task }) => isSandboxTask(task),
|
||||
},
|
||||
});
|
||||
|
||||
export const runOptions = createOptions({
|
||||
link: {
|
||||
type: 'boolean',
|
||||
description: 'Link the storybook to the local code?',
|
||||
@ -135,6 +155,7 @@ export const runOptions = createOptions({
|
||||
fromLocalRepro: {
|
||||
type: 'boolean',
|
||||
description: 'Create the template from a local repro (rather than degitting it)?',
|
||||
promptType: (_, { task }) => isSandboxTask(task),
|
||||
},
|
||||
dryRun: {
|
||||
type: 'boolean',
|
||||
@ -146,27 +167,14 @@ export const runOptions = createOptions({
|
||||
description: 'Print all the logs to the console',
|
||||
promptType: false,
|
||||
},
|
||||
});
|
||||
|
||||
export const taskOptions = createOptions({
|
||||
task: {
|
||||
type: 'string',
|
||||
description: 'What task are you performing (corresponds to CI job)?',
|
||||
values: Object.keys(tasks) as TaskKey[],
|
||||
required: true,
|
||||
},
|
||||
startFrom: {
|
||||
type: 'string',
|
||||
description: 'Which task should we reset back to?',
|
||||
values: [...(Object.keys(tasks) as TaskKey[]), 'never', 'auto'] as const,
|
||||
},
|
||||
junit: {
|
||||
type: 'boolean',
|
||||
description: 'Store results in junit format?',
|
||||
promptType: false,
|
||||
},
|
||||
});
|
||||
|
||||
type PassedOptionValues = OptionValues<typeof sandboxOptions & typeof runOptions>;
|
||||
type PassedOptionValues = Omit<OptionValues<typeof options>, 'task' | 'startFrom' | 'junit'>;
|
||||
|
||||
const logger = console;
|
||||
|
||||
@ -293,12 +301,7 @@ async function runTask(task: Task, details: TemplateDetails, optionValues: Passe
|
||||
}
|
||||
|
||||
async function run() {
|
||||
const allOptions = {
|
||||
...sandboxOptions,
|
||||
...runOptions,
|
||||
...taskOptions,
|
||||
};
|
||||
const allOptionValues = await getOptionsOrPrompt('yarn task', allOptions);
|
||||
const allOptionValues = await getOptionsOrPrompt('yarn task', options);
|
||||
|
||||
const { task: taskKey, startFrom, junit, ...optionValues } = allOptionValues;
|
||||
|
||||
@ -363,20 +366,24 @@ async function run() {
|
||||
setUnready(tasks[startFrom]);
|
||||
} else if (firstUnready === sortedTasks[0]) {
|
||||
// We need to do everything, no need to change anything
|
||||
} else if (sortedTasks.length === 1) {
|
||||
setUnready(sortedTasks[0]);
|
||||
} else {
|
||||
// We don't know what to do! Let's ask
|
||||
const { startFromTask } = await prompt({
|
||||
type: 'select',
|
||||
message: `We need to run all tasks after ${getTaskKey(
|
||||
firstUnready
|
||||
)}, would you like to go further back?`,
|
||||
message: firstUnready
|
||||
? `We need to run all tasks after ${getTaskKey(
|
||||
firstUnready
|
||||
)}, would you like to go further back?`
|
||||
: `Which task would you like to start from?`,
|
||||
name: 'startFromTask',
|
||||
choices: sortedTasks
|
||||
.slice(0, sortedTasks.indexOf(firstUnready) + 1)
|
||||
.slice(0, firstUnready && sortedTasks.indexOf(firstUnready) + 1)
|
||||
.filter((t) => !t.service)
|
||||
.reverse()
|
||||
.map((t) => ({
|
||||
title: getTaskKey(t),
|
||||
title: `${t.description} (${getTaskKey(t)})`,
|
||||
value: t,
|
||||
})),
|
||||
});
|
||||
@ -412,7 +419,7 @@ async function run() {
|
||||
dedent`
|
||||
To reproduce this error locally, run:
|
||||
|
||||
${getCommand('yarn task', allOptions, {
|
||||
${getCommand('yarn task', options, {
|
||||
...allOptionValues,
|
||||
link: true,
|
||||
startFrom: 'auto',
|
||||
@ -420,7 +427,7 @@ async function run() {
|
||||
|
||||
Note this uses locally linking which in rare cases behaves differently to CI. For a closer match, run:
|
||||
|
||||
${getCommand('yarn task', allOptions, {
|
||||
${getCommand('yarn task', options, {
|
||||
...allOptionValues,
|
||||
startFrom: 'auto',
|
||||
})}`,
|
||||
|
@ -3,6 +3,7 @@ import type { Task } from '../task';
|
||||
import { exec } from '../utils/exec';
|
||||
|
||||
export const build: Task = {
|
||||
description: 'Build the static version of the sandbox',
|
||||
before: ['sandbox'],
|
||||
async ready({ builtSandboxDir }) {
|
||||
return pathExists(builtSandboxDir);
|
||||
|
@ -2,6 +2,7 @@ import type { Task } from '../task';
|
||||
import { exec } from '../utils/exec';
|
||||
|
||||
export const chromatic: Task = {
|
||||
description: 'Run Chromatic against the sandbox',
|
||||
before: ['build'],
|
||||
junit: true,
|
||||
async ready() {
|
||||
|
@ -12,6 +12,7 @@ const noLinkCommand = `nx run-many --target="prep" --all --parallel=8 ${
|
||||
} -- --reset --optimized`;
|
||||
|
||||
export const compile: Task = {
|
||||
description: 'Compile the source code of the monorepo',
|
||||
before: ['install'],
|
||||
async ready({ codeDir }, { link }) {
|
||||
try {
|
||||
|
@ -7,6 +7,7 @@ import { exec } from '../utils/exec';
|
||||
export const START_PORT = 6006;
|
||||
|
||||
export const dev: Task = {
|
||||
description: 'Run the sandbox in development mode',
|
||||
service: true,
|
||||
before: ['sandbox'],
|
||||
async ready() {
|
||||
|
@ -3,6 +3,7 @@ import { exec } from '../utils/exec';
|
||||
import { PORT } from './serve';
|
||||
|
||||
export const e2eTests: Task = {
|
||||
description: 'Run the end-to-end test suite against the sandbox',
|
||||
before: ['serve'],
|
||||
junit: true,
|
||||
async ready() {
|
||||
|
@ -4,6 +4,7 @@ import type { Task } from '../task';
|
||||
import { exec } from '../utils/exec';
|
||||
|
||||
export const install: Task = {
|
||||
description: 'Install the dependencies of the monorepo',
|
||||
async ready({ codeDir }) {
|
||||
return pathExists(join(codeDir, 'node_modules'));
|
||||
},
|
||||
|
@ -7,6 +7,7 @@ import type { Task } from '../task';
|
||||
const verdaccioCacheDir = resolve(__dirname, '../../.verdaccio-cache');
|
||||
|
||||
export const publish: Task = {
|
||||
description: 'Publish the packages of the monorepo to an internal npm server',
|
||||
before: ['compile'],
|
||||
async ready() {
|
||||
return pathExists(verdaccioCacheDir);
|
||||
|
@ -25,6 +25,7 @@ export async function runRegistry({ dryRun, debug }: { dryRun?: boolean; debug?:
|
||||
|
||||
const REGISTRY_PORT = 6001;
|
||||
export const runRegistryTask: Task = {
|
||||
description: 'Run the internal npm server',
|
||||
service: true,
|
||||
before: ['publish'],
|
||||
async ready() {
|
||||
|
@ -5,6 +5,7 @@ import { Task } from '../task';
|
||||
const logger = console;
|
||||
|
||||
export const sandbox: Task = {
|
||||
description: 'Create the sandbox from a template',
|
||||
before: ({ link }) => (link ? ['compile'] : ['compile', 'run-registry']),
|
||||
async ready({ sandboxDir }) {
|
||||
return pathExists(sandboxDir);
|
||||
|
@ -6,6 +6,7 @@ import { exec } from '../utils/exec';
|
||||
|
||||
export const PORT = 8001;
|
||||
export const serve: Task = {
|
||||
description: 'Serve the build storybook for a sandbox',
|
||||
service: true,
|
||||
before: ['build'],
|
||||
async ready() {
|
||||
|
@ -2,6 +2,7 @@ import type { Task } from '../task';
|
||||
import { exec } from '../utils/exec';
|
||||
|
||||
export const smokeTest: Task = {
|
||||
description: 'Run the smoke tests of a sandbox',
|
||||
before: ['sandbox'],
|
||||
async ready() {
|
||||
return false;
|
||||
|
@ -3,6 +3,7 @@ import { exec } from '../utils/exec';
|
||||
import { PORT } from './serve';
|
||||
|
||||
export const testRunner: Task = {
|
||||
description: 'Run the test runner against a sandbox',
|
||||
junit: true,
|
||||
before: ['run-registry', 'build'],
|
||||
async ready() {
|
||||
|
@ -40,6 +40,10 @@ export type StringOption = BaseOption & {
|
||||
* What values are allowed for this option?
|
||||
*/
|
||||
values?: readonly string[];
|
||||
/**
|
||||
* How to describe the values when selecting them
|
||||
*/
|
||||
valueDescriptions?: readonly string[];
|
||||
/**
|
||||
* Is a value required for this option?
|
||||
*/
|
||||
@ -52,6 +56,10 @@ export type StringArrayOption = BaseOption & {
|
||||
* What values are allowed for this option?
|
||||
*/
|
||||
values?: readonly string[];
|
||||
/**
|
||||
* How to describe the values when selecting them
|
||||
*/
|
||||
valueDescriptions?: readonly string[];
|
||||
};
|
||||
|
||||
export type Option = BooleanOption | StringOption | StringArrayOption;
|
||||
@ -203,7 +211,7 @@ export async function promptOptions<TOptions extends OptionSpecifier>(
|
||||
const chosenType = passedType(...args);
|
||||
return chosenType === true ? defaultType : chosenType;
|
||||
};
|
||||
} else if (passedType) {
|
||||
} else if (typeof passedType !== 'undefined') {
|
||||
type = passedType;
|
||||
}
|
||||
|
||||
@ -215,8 +223,8 @@ export async function promptOptions<TOptions extends OptionSpecifier>(
|
||||
name: key,
|
||||
// warn: ' ',
|
||||
// pageSize: Object.keys(tasks).length + Object.keys(groups).length,
|
||||
choices: option.values?.map((value) => ({
|
||||
title: value,
|
||||
choices: option.values?.map((value, index) => ({
|
||||
title: option.valueDescriptions?.[index] || value,
|
||||
value,
|
||||
selected:
|
||||
currentValue === value ||
|
||||
|
Loading…
x
Reference in New Issue
Block a user