This repository has been archived by the owner on Jul 14, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from webstudio-is/24-support-component-story-fo…
…rmat-csf Provide stories with argTypes for primitives
- Loading branch information
Showing
36 changed files
with
31,973 additions
and
171 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module.exports = { | ||
"stories": [ | ||
"../src/**/*.stories.mdx", | ||
"../src/**/*.stories.@(js|jsx|ts|tsx)" | ||
], | ||
"addons": [ | ||
"@storybook/addon-links", | ||
"@storybook/addon-essentials", | ||
"@storybook/addon-interactions" | ||
], | ||
"framework": "@storybook/react" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export const parameters = { | ||
actions: { argTypesRegex: "^on[A-Z].*" }, | ||
controls: { | ||
matchers: { | ||
color: /(background|color)$/i, | ||
date: /Date$/, | ||
}, | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
#!/usr/bin/env node --experimental-loader esbuild-node-loader | ||
|
||
import path from "path"; | ||
import docgen from "react-docgen-typescript"; | ||
import fg from "fast-glob" | ||
import fs from "fs-extra" | ||
import {propsToArgTypes} from "../src/arg-types/utils"; | ||
|
||
const options = { | ||
shouldExtractLiteralValuesFromEnum: true, | ||
shouldRemoveUndefinedFromOptional: true, | ||
} | ||
|
||
const componentsGlobString = process.argv.pop(); | ||
const tsConfigPath = path.resolve(process.cwd(), "./tsconfig.json"); | ||
|
||
if (typeof componentsGlobString === "undefined") { | ||
throw new Error("Please provide glob patterns (space separated) as arguments to match your components"); | ||
} | ||
|
||
// Search for components | ||
const globs = componentsGlobString.split(" "); | ||
const componentFiles = fg.sync(globs); | ||
|
||
console.log(`Resolved tscofig.json at ${tsConfigPath}\n`); | ||
console.log(`Glob patterns used: \n${globs.join('\n')}\n`) | ||
console.log(`Found files to process: \n${componentFiles.join('\n')}\n`) | ||
|
||
if (componentFiles.length === 0) { | ||
throw new Error("No component files found"); | ||
} | ||
|
||
// Create a parser with using your typescript config | ||
const tsConfigParser = docgen.withCustomConfig(tsConfigPath, options); | ||
|
||
// For each component file generate argTypes based on the propTypes | ||
componentFiles.forEach(filePath => { | ||
const jsonPath = filePath.replace('.tsx', '.props.json') | ||
const res = tsConfigParser.parse(filePath) | ||
const argTypes = propsToArgTypes(res[0].props) | ||
fs.ensureFileSync(jsonPath) | ||
fs.writeJsonSync(jsonPath, argTypes, {spaces: 2}) | ||
console.log(`Done generating argTypes for ${jsonPath}`) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import type {ArgTypes} from "@storybook/csf" | ||
import {PropItem} from "react-docgen-typescript"; | ||
|
||
export type FilterPredicate = (prop: PropItem) => boolean | ||
|
||
const validAttributes = (prop: PropItem) => { | ||
if (prop.parent) { | ||
// Pass *HTML (both ButtonHTMLAttributes and HTMLAttributes), Aria, and SVG attributes through | ||
const matcher = /.?(HTML|SVG|Aria)Attributes/; | ||
// TODO: Add a test for this | ||
return prop.parent.name.match(matcher); | ||
} | ||
// Always allow component's own props | ||
return true | ||
} | ||
|
||
export const propsToArgTypes = (props: Record<string, PropItem>, filter?: FilterPredicate): ArgTypes => { | ||
const filterFn = filter ?? validAttributes | ||
const entries = Object.entries(props); | ||
return entries | ||
.reduce((result, current) => { | ||
const [propName, prop] = current | ||
|
||
// Filter out props | ||
if (!filterFn(prop)) { | ||
return result | ||
} | ||
|
||
const control = mapControlForType(prop) | ||
result[propName] = {...prop, ...control} | ||
return result | ||
}, {} as ArgTypes); | ||
} | ||
|
||
const matchers = { | ||
color: new RegExp('(background|color)', 'i'), | ||
date: /Date$/ | ||
} | ||
|
||
export const mapControlForType = (propItem: PropItem): any => { | ||
const {type, name} = propItem; | ||
if (!type) { | ||
return undefined; | ||
} | ||
|
||
// args that end with background or color e.g. iconColor | ||
if (matchers.color && matchers.color.test(name)) { | ||
const controlType = propItem.type.name; | ||
|
||
if (controlType === 'string') { | ||
return { control: { type: 'color' }, defaultValue: propItem.defaultValue?.value }; | ||
} | ||
} | ||
|
||
// args that end with date e.g. purchaseDate | ||
if (matchers.date && matchers.date.test(name)) { | ||
return { control: { type: 'date' } }; | ||
} | ||
|
||
switch (type?.name) { | ||
case 'array': | ||
return {control: {type: 'object'}}; | ||
case 'boolean': | ||
case 'Booleanish': | ||
return {control: {type: 'boolean'}}; | ||
case 'string': | ||
return {control: {type: 'text'}}; | ||
case 'number': | ||
return {control: {type: 'number'}}; | ||
case 'enum': { | ||
const {value} = type; | ||
// @ts-expect-error Original type has `any` type | ||
const values = value.map(val => val.value) | ||
return {control: {type: values?.length <= 5 ? 'radio' : 'select'}, options: values}; | ||
} | ||
case 'function': | ||
case 'symbol': | ||
return null | ||
default: | ||
return {control: {type: 'text'}}; | ||
} | ||
}; |
Oops, something went wrong.