Merge branch 'next' into pr/Spy-Seth/6589

# Conflicts:
#	app/react/package.json
#	yarn.lock
This commit is contained in:
Norbert de Langen 2019-05-23 09:07:21 +02:00
commit d4ae753259
521 changed files with 20706 additions and 11332 deletions

View File

@ -1,6 +1,20 @@
const withTests = {
presets: [
[
'@babel/preset-env',
{ shippedProposals: true, useBuiltIns: 'usage', corejs: '3', targets: { node: 'current' } },
],
],
plugins: [
'babel-plugin-require-context-hook',
'babel-plugin-dynamic-import-node',
'@babel/plugin-transform-runtime',
],
};
module.exports = { module.exports = {
presets: [ presets: [
['@babel/preset-env', { shippedProposals: true, useBuiltIns: 'usage', corejs: '2' }], ['@babel/preset-env', { shippedProposals: true, useBuiltIns: 'usage', corejs: '3' }],
'@babel/preset-typescript', '@babel/preset-typescript',
'@babel/preset-react', '@babel/preset-react',
'@babel/preset-flow', '@babel/preset-flow',
@ -20,26 +34,20 @@ module.exports = {
['emotion', { sourceMap: true, autoLabel: true }], ['emotion', { sourceMap: true, autoLabel: true }],
], ],
env: { env: {
test: { test: withTests,
presets: [
['@babel/preset-env', { shippedProposals: true, useBuiltIns: 'usage', corejs: '2' }],
],
plugins: [
'babel-plugin-require-context-hook',
'babel-plugin-dynamic-import-node',
'@babel/plugin-transform-runtime',
],
},
}, },
overrides: [ overrides: [
{ {
test: './examples/vue-kitchen-sink', test: './examples/vue-kitchen-sink',
presets: ['babel-preset-vue'], presets: ['babel-preset-vue'],
env: {
test: withTests,
},
}, },
{ {
test: './lib', test: './lib',
presets: [ presets: [
['@babel/preset-env', { shippedProposals: true, useBuiltIns: 'usage', corejs: '2' }], ['@babel/preset-env', { shippedProposals: true, useBuiltIns: 'usage', corejs: '3' }],
'@babel/preset-react', '@babel/preset-react',
], ],
plugins: [ plugins: [
@ -52,6 +60,9 @@ module.exports = {
'@babel/plugin-transform-react-constant-elements', '@babel/plugin-transform-react-constant-elements',
'babel-plugin-add-react-displayname', 'babel-plugin-add-react-displayname',
], ],
env: {
test: withTests,
},
}, },
{ {
test: [ test: [
@ -72,7 +83,7 @@ module.exports = {
targets: { targets: {
node: '8.11', node: '8.11',
}, },
corejs: '2', corejs: '3',
}, },
], ],
], ],
@ -83,6 +94,9 @@ module.exports = {
'@babel/plugin-proposal-object-rest-spread', '@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-proposal-export-default-from', '@babel/plugin-proposal-export-default-from',
], ],
env: {
test: withTests,
},
}, },
], ],
}; };

View File

@ -40,6 +40,15 @@ jobs:
- addons - addons
- app - app
- lib - lib
chromatic:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: .
- run:
name: Run chromatic on the pre-built storybook
command: yarn chromatic -- -d ./storybook-static
examples: examples:
<<: *defaults <<: *defaults
steps: steps:
@ -109,6 +118,11 @@ jobs:
command: | command: |
cd examples/preact-kitchen-sink cd examples/preact-kitchen-sink
yarn build-storybook yarn build-storybook
- run:
name: Build cra react15
command: |
cd examples/cra-react15
yarn build-storybook
- run: - run:
name: Build official-storybook name: Build official-storybook
command: | command: |
@ -120,6 +134,14 @@ jobs:
- store_artifacts: - store_artifacts:
path: examples/official-storybook/image-snapshots/__image_snapshots__ path: examples/official-storybook/image-snapshots/__image_snapshots__
destination: official_storybook_image_snapshots destination: official_storybook_image_snapshots
- persist_to_workspace:
root: .
paths:
- node_modules
- examples
- addons
- app
- lib
smoke-tests: smoke-tests:
<<: *defaults <<: *defaults
steps: steps:
@ -186,6 +208,11 @@ jobs:
command: | command: |
cd examples/preact-kitchen-sink cd examples/preact-kitchen-sink
yarn storybook --smoke-test --quiet yarn storybook --smoke-test --quiet
- run:
name: Run cra reac15 (smoke test)
command: |
cd examples/cra-react15
yarn storybook --smoke-test --quiet
native-smoke-tests: native-smoke-tests:
<<: *defaults <<: *defaults
steps: steps:
@ -337,3 +364,6 @@ workflows:
- cli-test-latest-cra: - cli-test-latest-cra:
requires: requires:
- build - build
- chromatic:
requires:
- examples

View File

@ -7,10 +7,9 @@ docs/public
storybook-static storybook-static
built-storybooks built-storybooks
lib/cli/test lib/cli/test
scripts/storage
*.bundle.js *.bundle.js
*.js.map *.js.map
*.ts
*.tsx
!.remarkrc.js !.remarkrc.js
!.babelrc.js !.babelrc.js

View File

@ -1,26 +1,68 @@
const error = 2; const error = 2;
const warn = 1; const warn = 1;
const ignore = 0; const ignore = 0;
module.exports = { module.exports = {
root: true, root: true,
extends: [ extends: [
'airbnb', 'airbnb',
'plugin:jest/recommended', 'plugin:jest/recommended',
'plugin:import/react-native', 'plugin:import/react-native',
'plugin:@typescript-eslint/recommended',
'prettier', 'prettier',
'prettier/react', 'prettier/react',
'prettier/@typescript-eslint',
], ],
plugins: ['prettier', 'jest', 'import', 'react', 'jsx-a11y', 'json', 'html'], plugins: [
parser: 'babel-eslint', '@typescript-eslint',
parserOptions: { ecmaVersion: 8, sourceType: 'module' }, 'prettier',
'jest',
'import',
'react',
'jsx-a11y',
'json',
'html',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 8,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
env: { es6: true, node: true, 'jest/globals': true }, env: { es6: true, node: true, 'jest/globals': true },
settings: { settings: {
'import/core-modules': ['enzyme'], 'import/core-modules': ['enzyme'],
'import/ignore': ['node_modules\\/(?!@storybook)'], 'import/ignore': ['node_modules\\/(?!@storybook)'],
'import/resolver': { node: { extensions: ['.js', '.ts'] } }, 'import/resolver': { node: { extensions: ['.js', '.ts', '.tsx', '.mjs'] } },
'html/html-extensions': ['.html'], 'html/html-extensions': ['.html'],
}, },
rules: { rules: {
'no-restricted-imports': [
error,
{
paths: [
{
name: 'lodash.isequal',
message:
'Lodash modularised (and lodash < 4.17.11) have CVE vulnerabilities. Please use tree-shakeable imports like lodash/xxx instead',
},
{
name: 'lodash.mergewith',
message:
'Lodash modularised (and lodash < 4.17.11) have CVE vulnerabilities. Please use tree-shakeable imports like lodash/xxx instead',
},
{
name: 'lodash.pick',
message:
'Lodash modularised (and lodash < 4.17.11) have CVE vulnerabilities. Please use tree-shakeable imports like lodash/xxx instead',
},
],
// catch-all for any lodash modularised. The CVE is listed against the entire family for lodash < 4.17.11
patterns: ['lodash.*'],
},
],
'prettier/prettier': [warn], 'prettier/prettier': [warn],
'no-debugger': process.env.NODE_ENV === 'production' ? error : ignore, 'no-debugger': process.env.NODE_ENV === 'production' ? error : ignore,
'class-methods-use-this': ignore, 'class-methods-use-this': ignore,
@ -30,6 +72,7 @@ module.exports = {
{ {
js: 'never', js: 'never',
ts: 'never', ts: 'never',
tsx: 'never',
mjs: 'never', mjs: 'never',
}, },
], ],
@ -42,11 +85,11 @@ module.exports = {
'**/example/**', '**/example/**',
'*.js', '*.js',
'**/*.test.js', '**/*.test.js',
'**/*.stories.js', '**/*.stories.*',
'**/scripts/*.js', '**/scripts/*.js',
'**/stories/**/*.js', '**/stories/**/*.js',
'**/__tests__/**/*.js', '**/__tests__/**/*.js',
'**/.storybook/**/*.js', '**/.storybook/**/*.*',
], ],
peerDependencies: true, peerDependencies: true,
}, },
@ -94,13 +137,21 @@ module.exports = {
error, error,
{ allow: ['__STORYBOOK_CLIENT_API__', '__STORYBOOK_ADDONS_CHANNEL__'] }, { allow: ['__STORYBOOK_CLIENT_API__', '__STORYBOOK_ADDONS_CHANNEL__'] },
], ],
'@typescript-eslint/no-var-requires': ignore,
'@typescript-eslint/camelcase': ignore,
'@typescript-eslint/no-unused-vars': ignore,
'@typescript-eslint/explicit-member-accessibility': ignore,
'@typescript-eslint/explicit-function-return-type': ignore,
'@typescript-eslint/no-explicit-any': ignore, // would prefer to enable this
'@typescript-eslint/no-use-before-define': ignore, // this is duplicated
'@typescript-eslint/interface-name-prefix': ignore, // I don't agree
}, },
overrides: [ overrides: [
{ {
files: [ files: [
'**/__tests__/**', '**/__tests__/**',
'**/*.test.js', '**/*.test.*',
'**/*.stories.js', '**/*.stories.*',
'**/storyshots/**/stories/**', '**/storyshots/**/stories/**',
'docs/src/new-components/lib/StoryLinkWrapper.js', 'docs/src/new-components/lib/StoryLinkWrapper.js',
'docs/src/stories/**', 'docs/src/stories/**',
@ -110,5 +161,26 @@ module.exports = {
}, },
}, },
{ files: '**/.storybook/config.js', rules: { 'global-require': ignore } }, { files: '**/.storybook/config.js', rules: { 'global-require': ignore } },
{
files: ['**/*.stories.*'],
rules: {
'no-console': ignore,
},
},
{
files: ['**/*.tsx', '**/*.ts'],
rules: {
'react/prop-types': ignore, // we should use types
'no-dupe-class-members': ignore, // this is called overloads in typescript
},
},
{
files: ['**/*.d.ts'],
rules: {
'vars-on-top': ignore,
'no-var': ignore, // this is how typescript works
'spaced-comment': ignore,
},
},
], ],
}; };

View File

@ -1,4 +1,4 @@
'app: angular': ['kroeder', 'igor-dv'] 'app: angular': ['kroeder', 'igor-dv', 'MaximSagan']
'app: ember': ['gabrielcsapo'] 'app: ember': ['gabrielcsapo']
'app: html': ['Hypnosphi'] 'app: html': ['Hypnosphi']
'app: marko': ['nm123github'] 'app: marko': ['nm123github']
@ -10,9 +10,10 @@
'api: addons': ['ndelangen'] 'api: addons': ['ndelangen']
'addon: a11y': ['CodeByAlex', 'Armanio', 'jsomsanith'] 'addon: a11y': ['CodeByAlex', 'Armanio', 'jsomsanith']
'addon: contexts': ['leoyli'] 'addon: contexts': ['leoyli']
'addon: storysource': ['igor-dv', 'libetl'] 'addon: docs': ['shilman', 'elevatebart']
'addon: info': ['shilman', 'elevatebart'] 'addon: info': ['shilman', 'elevatebart']
'addon: knobs': ['leoyli', 'Armanio'] 'addon: knobs': ['leoyli', 'Armanio']
'addon: storysource': ['igor-dv', 'libetl']
typescript: ['kroeder', 'gaetanmaisse', 'ndelangen'] typescript: ['kroeder', 'gaetanmaisse', 'ndelangen']
theming: ['ndelangen', 'domyen'] theming: ['ndelangen', 'domyen']
cra: ['mrmckeb'] cra: ['mrmckeb']

2
.gitignore vendored
View File

@ -25,3 +25,5 @@ integration/__image_snapshots__/__diff_output__
lib/*.jar lib/*.jar
lib/**/dll lib/**/dll
.expo/packager-info.json .expo/packager-info.json
scripts/storage
htpasswd

View File

@ -23,7 +23,6 @@ object Project : Project({
buildType(OpenSourceProjects_Storybook_Examples) buildType(OpenSourceProjects_Storybook_Examples)
buildType(OpenSourceProjects_Storybook_Docs) buildType(OpenSourceProjects_Storybook_Docs)
buildType(OpenSourceProjects_Storybook_Build_2) buildType(OpenSourceProjects_Storybook_Build_2)
buildType(OpenSourceProjects_Storybook_CliTest)
buildType(OpenSourceProjects_Storybook_Test) buildType(OpenSourceProjects_Storybook_Test)
buildType(OpenSourceProjects_Storybook_Lint) buildType(OpenSourceProjects_Storybook_Lint)
buildType(OpenSourceProjects_Storybook_Lint_Warnings) buildType(OpenSourceProjects_Storybook_Lint_Warnings)

View File

@ -111,10 +111,5 @@ object OpenSourceProjects_Storybook_Build_2 : BuildType({
onDependencyCancel = FailureAction.ADD_PROBLEM onDependencyCancel = FailureAction.ADD_PROBLEM
} }
} }
dependency(OpenSourceProjects_Storybook.buildTypes.OpenSourceProjects_Storybook_CliTest) {
snapshot {
onDependencyCancel = FailureAction.ADD_PROBLEM
}
}
} }
}) })

View File

@ -26,10 +26,10 @@ object OpenSourceProjects_Storybook_Chromatic : BuildType({
scriptContent = """ scriptContent = """
#!/bin/sh #!/bin/sh
set -e -x # set -e -x
# yarn
yarn # yarn chromatic
yarn chromatic echo "chromatic moved to cirlce CI"
""".trimIndent() """.trimIndent()
dockerImage = "node:%docker.node.version%" dockerImage = "node:%docker.node.version%"
} }

View File

@ -1,63 +0,0 @@
package OpenSourceProjects_Storybook.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.buildFeatures.commitStatusPublisher
import jetbrains.buildServer.configs.kotlin.v2017_2.buildSteps.script
object OpenSourceProjects_Storybook_CliTest : BuildType({
uuid = "b1db1a3a-a4cf-46ea-8f55-98b86611f92e"
id = "OpenSourceProjects_Storybook_CliTest"
name = "CLI test"
vcs {
root(OpenSourceProjects_Storybook.vcsRoots.OpenSourceProjects_Storybook_HttpsGithubComStorybooksStorybookRefsHeadsMaster)
}
steps {
script {
name = "Test"
scriptContent = """
#!/bin/sh
set -e -x
yarn
yarn test --cli --teamcity
""".trimIndent()
dockerImage = "node:%docker.node.version%"
}
}
features {
commitStatusPublisher {
publisher = github {
githubUrl = "https://api.github.com"
authType = personalToken {
token = "credentialsJSON:5ffe2d7e-531e-4f6f-b1fc-a41bfea26eaa"
}
}
param("github_oauth_user", "Hypnosphi")
}
}
dependencies {
dependency(OpenSourceProjects_Storybook.buildTypes.OpenSourceProjects_Storybook_Bootstrap) {
snapshot {
onDependencyFailure = FailureAction.FAIL_TO_START
}
artifacts {
artifactRules = "dist.zip!**"
}
}
}
requirements {
doesNotContain("env.OS", "Windows")
}
cleanup {
artifacts(days = 1)
}
})

View File

@ -9,6 +9,7 @@ import jetbrains.buildServer.configs.kotlin.v2017_2.failureConditions.failOnMetr
enum class StorybookApp(val appName: String, val exampleDir: String, val merged: Boolean = true) { enum class StorybookApp(val appName: String, val exampleDir: String, val merged: Boolean = true) {
CRA("CRA", "cra-kitchen-sink"), CRA("CRA", "cra-kitchen-sink"),
CRA_TS("CRA TS", "cra-ts-kitchen-sink"), CRA_TS("CRA TS", "cra-ts-kitchen-sink"),
CRA_REACT15("CRA REACT15", "cra-react15"),
VUE("Vue", "vue-kitchen-sink"), VUE("Vue", "vue-kitchen-sink"),
ANGULAR("Angular", "angular-cli"), ANGULAR("Angular", "angular-cli"),
POLYMER("Polymer", "polymer-cli"), POLYMER("Polymer", "polymer-cli"),

View File

@ -5,13 +5,12 @@ import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/* /*
This patch script was generated by TeamCity on settings change in UI. This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = 'b1db1a3a-a4cf-46ea-8f55-98b86611f92e' (id = 'OpenSourceProjects_Storybook_CliTest') To apply the patch, change the buildType with uuid = '1bda59b5-d08d-4fd8-b317-953e7d79d881' (id = 'OpenSourceProjects_Storybook_Docs')
accordingly, and delete the patch script. accordingly, and delete the patch script.
*/ */
changeBuildType("b1db1a3a-a4cf-46ea-8f55-98b86611f92e") { changeBuildType("1bda59b5-d08d-4fd8-b317-953e7d79d881") {
params { check(paused == false) {
add { "Unexpected paused: '$paused'"
param("docker.node.version", "latest")
}
} }
paused = true
} }

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '1ea2b5bd-28f6-44f5-8ab3-6c659ce8fbd6' (id = 'OpenSourceProjects_Storybook_SmokeTests')
accordingly, and delete the patch script.
*/
changeBuildType("1ea2b5bd-28f6-44f5-8ab3-6c659ce8fbd6") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '2b9c73e2-0a6e-47ca-95ae-729cac42be2b' (id = 'OpenSourceProjects_Storybook_Build_2')
accordingly, and delete the patch script.
*/
changeBuildType("2b9c73e2-0a6e-47ca-95ae-729cac42be2b") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '42cfbb9a-f35b-4f96-afae-0b508927a737' (id = 'OpenSourceProjects_Storybook_Lint')
accordingly, and delete the patch script.
*/
changeBuildType("42cfbb9a-f35b-4f96-afae-0b508927a737") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '42cfbb9a-f35b-4f96-afae-0b508927a738' (id = 'OpenSourceProjects_Storybook_Lint_Warnings')
accordingly, and delete the patch script.
*/
changeBuildType("42cfbb9a-f35b-4f96-afae-0b508927a738") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '8cc5f747-4ca7-4f0d-940d-b0c422f501a6-angular' (id = 'OpenSourceProjects_Storybook_Angular')
accordingly, and delete the patch script.
*/
changeBuildType("8cc5f747-4ca7-4f0d-940d-b0c422f501a6-angular") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '8cc5f747-4ca7-4f0d-940d-b0c422f501a6-chromatic' (id = 'OpenSourceProjects_Storybook_Chromatic')
accordingly, and delete the patch script.
*/
changeBuildType("8cc5f747-4ca7-4f0d-940d-b0c422f501a6-chromatic") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '8cc5f747-4ca7-4f0d-940d-b0c422f501a6-cra' (id = 'OpenSourceProjects_Storybook_CRA')
accordingly, and delete the patch script.
*/
changeBuildType("8cc5f747-4ca7-4f0d-940d-b0c422f501a6-cra") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '8cc5f747-4ca7-4f0d-940d-b0c422f501a6-cra_react15' (id = 'OpenSourceProjects_Storybook_CRA_REACT15')
accordingly, and delete the patch script.
*/
changeBuildType("8cc5f747-4ca7-4f0d-940d-b0c422f501a6-cra_react15") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '8cc5f747-4ca7-4f0d-940d-b0c422f501a6-cra_ts' (id = 'OpenSourceProjects_Storybook_CRA_TS')
accordingly, and delete the patch script.
*/
changeBuildType("8cc5f747-4ca7-4f0d-940d-b0c422f501a6-cra_ts") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '8cc5f747-4ca7-4f0d-940d-b0c422f501a6-ember' (id = 'OpenSourceProjects_Storybook_Ember')
accordingly, and delete the patch script.
*/
changeBuildType("8cc5f747-4ca7-4f0d-940d-b0c422f501a6-ember") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '8cc5f747-4ca7-4f0d-940d-b0c422f501a6-html' (id = 'OpenSourceProjects_Storybook_HTML')
accordingly, and delete the patch script.
*/
changeBuildType("8cc5f747-4ca7-4f0d-940d-b0c422f501a6-html") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '8cc5f747-4ca7-4f0d-940d-b0c422f501a6-marko' (id = 'OpenSourceProjects_Storybook_Marko')
accordingly, and delete the patch script.
*/
changeBuildType("8cc5f747-4ca7-4f0d-940d-b0c422f501a6-marko") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '8cc5f747-4ca7-4f0d-940d-b0c422f501a6-mithril' (id = 'OpenSourceProjects_Storybook_Mithril')
accordingly, and delete the patch script.
*/
changeBuildType("8cc5f747-4ca7-4f0d-940d-b0c422f501a6-mithril") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '8cc5f747-4ca7-4f0d-940d-b0c422f501a6-polymer' (id = 'OpenSourceProjects_Storybook_Polymer')
accordingly, and delete the patch script.
*/
changeBuildType("8cc5f747-4ca7-4f0d-940d-b0c422f501a6-polymer") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '8cc5f747-4ca7-4f0d-940d-b0c422f501a6-preact' (id = 'OpenSourceProjects_Storybook_Preact')
accordingly, and delete the patch script.
*/
changeBuildType("8cc5f747-4ca7-4f0d-940d-b0c422f501a6-preact") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '8cc5f747-4ca7-4f0d-940d-b0c422f501a6-riot' (id = 'OpenSourceProjects_Storybook_Riot')
accordingly, and delete the patch script.
*/
changeBuildType("8cc5f747-4ca7-4f0d-940d-b0c422f501a6-riot") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '8cc5f747-4ca7-4f0d-940d-b0c422f501a6-svelte' (id = 'OpenSourceProjects_Storybook_Svelte')
accordingly, and delete the patch script.
*/
changeBuildType("8cc5f747-4ca7-4f0d-940d-b0c422f501a6-svelte") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '8cc5f747-4ca7-4f0d-940d-b0c422f501a6-vue' (id = 'OpenSourceProjects_Storybook_Vue')
accordingly, and delete the patch script.
*/
changeBuildType("8cc5f747-4ca7-4f0d-940d-b0c422f501a6-vue") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '8cc5f747-4ca7-4f0d-940d-b0c422f501a6' (id = 'OpenSourceProjects_Storybook_Examples')
accordingly, and delete the patch script.
*/
changeBuildType("8cc5f747-4ca7-4f0d-940d-b0c422f501a6") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -9,6 +9,11 @@ To apply the patch, change the buildType with uuid = '9f9177e7-9ec9-4e2e-aabb-d3
accordingly, and delete the patch script. accordingly, and delete the patch script.
*/ */
changeBuildType("9f9177e7-9ec9-4e2e-aabb-d304fd667711") { changeBuildType("9f9177e7-9ec9-4e2e-aabb-d304fd667711") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
params { params {
add { add {
param("docker.node.version", "10.13") param("docker.node.version", "10.13")

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = '9f9177e7-9ec9-4e2e-aabb-d304fd667712' (id = 'OpenSourceProjects_Storybook_Bootstrap')
accordingly, and delete the patch script.
*/
changeBuildType("9f9177e7-9ec9-4e2e-aabb-d304fd667712") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,16 @@
package OpenSourceProjects_Storybook.patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with uuid = 'd4320bd8-6094-4dd6-9bed-e13d6f0d12e2' (id = 'OpenSourceProjects_Storybook_CliTestLatestCra')
accordingly, and delete the patch script.
*/
changeBuildType("d4320bd8-6094-4dd6-9bed-e13d6f0d12e2") {
check(paused == false) {
"Unexpected paused: '$paused'"
}
paused = true
}

View File

@ -0,0 +1,17 @@
package OpenSourceProjects_Storybook.patches.projects
import jetbrains.buildServer.configs.kotlin.v2017_2.*
import jetbrains.buildServer.configs.kotlin.v2017_2.Project
import jetbrains.buildServer.configs.kotlin.v2017_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the project with uuid = '69382d9b-7791-418a-9ff6-1c83b86ed6b5' (id = 'OpenSourceProjects_Storybook')
accordingly, and delete the patch script.
*/
changeProject("69382d9b-7791-418a-9ff6-1c83b86ed6b5") {
check(archived == false) {
"Unexpected archived: '$archived'"
}
archived = true
}

View File

@ -2,11 +2,11 @@
| | [React](app/react)|[React Native](app/react-native)|[Vue](app/vue)|[Angular](app/angular)| [Polymer](app/polymer)| [Mithril](app/mithril)| [HTML](app/html)| [Marko](app/marko)| [Svelte](app/svelte)| [Riot](app/riot)| [Ember](app/ember)| [Preact](app/preact)| | | [React](app/react)|[React Native](app/react-native)|[Vue](app/vue)|[Angular](app/angular)| [Polymer](app/polymer)| [Mithril](app/mithril)| [HTML](app/html)| [Marko](app/marko)| [Svelte](app/svelte)| [Riot](app/riot)| [Ember](app/ember)| [Preact](app/preact)|
| ----------- |:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|:-------:| | ----------- |:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|
|[a11y](addons/a11y) |+| |+|+|+|+|+|+| | |+|+| |[a11y](addons/a11y) |+| |+|+|+|+|+|+|+|+|+|+|
|[actions](addons/actions) |+|+|+|+|+|+|+|+|+|+|+|+| |[actions](addons/actions) |+|+*|+|+|+|+|+|+|+|+|+|+|
|[backgrounds](addons/backgrounds) |+|*|+|+|+|+|+|+|+|+|+|+| |[backgrounds](addons/backgrounds) |+|*|+|+|+|+|+|+|+|+|+|+|
|[centered](addons/centered) |+| |+|+| |+|+| |+| |+|+| |[centered](addons/centered) |+| |+|+| |+|+| |+| |+|+|
|[contexts](addons/contexts) |+| |+| | | | | | | | | | |[contexts](addons/contexts) |+| |+| | | | | | | | |+|
|[events](addons/events) |+| |+|+|+|+|+|+| | |+|+| |[events](addons/events) |+| |+|+|+|+|+|+| | |+|+|
|[graphql](addons/graphql) |+| | | | | | | | | | | | |[graphql](addons/graphql) |+| | | | | | | | | | | |
|[google-analytics](addons/google-analytics) |+|+|+|+|+|+|+|+|+|+|+|+| |[google-analytics](addons/google-analytics) |+|+|+|+|+|+|+|+|+|+|+|+|

View File

@ -1,35 +1,210 @@
## 5.1.0-rc.0 (May 21, 2019)
### Bug Fixes
* UI: Fix initial bottom panel size ([#6822](https://github.com/storybooks/storybook/pull/6822))
* UI: Fix syntaxthighlighter themes ([#6814](https://github.com/storybooks/storybook/pull/6814))
* Addon-knobs: Fix Boolean knob (#6366) ([#6830](https://github.com/storybooks/storybook/pull/6830))
* Theming: Change lib/theming so it no longer depends on react-inspector ([#6818](https://github.com/storybooks/storybook/pull/6818))
* Core: Handle loading `.storybook/babel.config.js` (#6633) ([#6634](https://github.com/storybooks/storybook/pull/6634))
* CLI: Fix init in create-react-library projects ([#6815](https://github.com/storybooks/storybook/pull/6815))
* HTML: support knobs for both cached and uncached nodes ([#6783](https://github.com/storybooks/storybook/pull/6783))
* Uncorrupt yarn lock ([#6811](https://github.com/storybooks/storybook/pull/6811))
* Core: set a better value for process in manager webpack config ([#6767](https://github.com/storybooks/storybook/pull/6767))
### Maintenance
* Typescript: Migrate addon-centered ([#6772](https://github.com/storybooks/storybook/pull/6772))
* Add engine field to package.json in apps ([#6809](https://github.com/storybooks/storybook/pull/6809))
* Fix required engine for apps ([#6810](https://github.com/storybooks/storybook/pull/6810))
### Dependency Upgrades
* Upgrade lodash to latest ([#6832](https://github.com/storybooks/storybook/pull/6832))
* Bump svelte from 3.4.1 to 3.4.2 ([#6838](https://github.com/storybooks/storybook/pull/6838))
* Misc upgrades ([#6820](https://github.com/storybooks/storybook/pull/6820))
## 5.1.0-beta.1 (May 16, 2019)
### Bug Fixes
* UI: Scrollbar supports theming again ([#6794](https://github.com/storybooks/storybook/pull/6794))
* UI: Fix scrolling styling ([#6785](https://github.com/storybooks/storybook/pull/6785))
* UI: Fix iframe refresh ([#6787](https://github.com/storybooks/storybook/pull/6787))
* UI: Preserve dimensions on resizing for panel ([#6696](https://github.com/storybooks/storybook/pull/6696))
### Maintenance
* Move chromatic to circle ci ([#6752](https://github.com/storybooks/storybook/pull/6752))
### Dependency Upgrades
* Bump fs-extra from 7.0.1 to 8.0.1 ([#6776](https://github.com/storybooks/storybook/pull/6776))
## 5.1.0-beta.0 (May 10, 2019)
Welcome to the 5.1 beta! Feature development's done; `beta.0` kicks off the stabilization process for the 5.1 final release. 🚀
## 5.1.0-alpha.40 (May 8, 2019)
### Features
- Svelte: Add svelte v3 support ([#6698](https://github.com/storybooks/storybook/pull/6698))
- Angular: Disable production mode for debugging components ([#6215](https://github.com/storybooks/storybook/pull/6215))
- Angular: Allow optional component declaration without additional configuration ([#6666](https://github.com/storybooks/storybook/pull/6666))
- Core: Allow browsing to a kind and get the first story ([#6720](https://github.com/storybooks/storybook/pull/6720))
### Bug Fixes
- UI: Preserve dimensions on resizing for panel ([#6696](https://github.com/storybooks/storybook/pull/6696))
### Maintenance
- Add CRA React15 example to test back-compat ([#6475](https://github.com/storybooks/storybook/pull/6475))
- Remove teamcity CLI tests ([#6707](https://github.com/storybooks/storybook/pull/6707))
### Dependency Upgrades
- Upgrade to core-js v3 ([#6655](https://github.com/storybooks/storybook/pull/6655))
- Bump eslint-plugin-react from 7.12.4 to 7.13.0 ([#6728](https://github.com/storybooks/storybook/pull/6728))
- Bump @types/react-native from 0.57.50 to 0.57.51 ([#6732](https://github.com/storybooks/storybook/pull/6732))
- Bump @types/node from 11.13.7 to 12.0.0 ([#6730](https://github.com/storybooks/storybook/pull/6730))
- Bump jest-cli from 24.7.1 to 24.8.0 ([#6729](https://github.com/storybooks/storybook/pull/6729))
- Bump @babel/preset-env from 7.4.3 to 7.4.4 ([#6731](https://github.com/storybooks/storybook/pull/6731))
- Bump raw-loader from 1.0.0 to 2.0.0 ([#6685](https://github.com/storybooks/storybook/pull/6685))
- Bump react-color from 2.17.1 to 2.17.3 ([#6681](https://github.com/storybooks/storybook/pull/6681))
- Bump @babel/plugin-proposal-class-properties from 7.4.0 to 7.4.4 ([#6686](https://github.com/storybooks/storybook/pull/6686))
- Bump react-dev-utils from 8.0.0 to 9.0.0 ([#6682](https://github.com/storybooks/storybook/pull/6682))
- Bump codelyzer from 5.0.0 to 5.0.1 ([#6687](https://github.com/storybooks/storybook/pull/6687))
- Bump @types/react from 16.8.14 to 16.8.16 ([#6717](https://github.com/storybooks/storybook/pull/6717))
- Bump react-redux from 7.0.2 to 7.0.3 ([#6684](https://github.com/storybooks/storybook/pull/6684))
## 5.1.0-alpha.39 (May 2, 2019)
### Features
- React-native: Ondevice actions ([#6594](https://github.com/storybooks/storybook/pull/6594))
- React-native: Use emotion to style RN UI ([#6603](https://github.com/storybooks/storybook/pull/6603))
### Bug Fixes
- API: Mimic PureComponent behavior for Consumer children ([#6412](https://github.com/storybooks/storybook/pull/6412))
## 5.1.0-alpha.38 (May 2, 2019)
Failed publish
## 5.1.0-alpha.37 (May 1, 2019)
### Bug Fixes
- Core: Fix regression with deep linking ([#6688](https://github.com/storybooks/storybook/pull/6688))
- Addon-contexts: No cancel option in UI if the context have no param ([#6669](https://github.com/storybooks/storybook/pull/6669))
- CLI: Fix `sb init` for projects with frozen lock files ([#6629](https://github.com/storybooks/storybook/pull/6629))
### Maintenance
- CLI: Refactor how we install dev dependencies in cli ([#6695](https://github.com/storybooks/storybook/pull/6695))
## 5.0.11 (April 28, 2019)
### Bug Fixes
- Polymer: Fix re-rendering lit-html elements after non-lit-html element ([#5868](https://github.com/storybooks/storybook/pull/5868))
- Addon-knobs: Check color knob value before applying uppercase ([#6598](https://github.com/storybooks/storybook/pull/6598))
- Angular: Fix sourceMap property of angulars webpack config ([#6535](https://github.com/storybooks/storybook/pull/6535))
### Maintenance
- UI: Add missing props in stories ([#6353](https://github.com/storybooks/storybook/pull/6353))
## 5.1.0-alpha.36 (April 27, 2019)
### Features
- Addon-contexts: Preact support ([#6660](https://github.com/storybooks/storybook/pull/6660))
- Angular: Allow optional component declaration ([#6346](https://github.com/storybooks/storybook/pull/6346))
### Bug Fixes
- CLI: Fix `sb init` for projects with frozen lock files ([#6629](https://github.com/storybooks/storybook/pull/6629))
### Dependency Upgrades
- [Snyk] Fix for 1 vulnerable dependencies ([#6647](https://github.com/storybooks/storybook/pull/6647))
## 5.1.0-alpha.35 (April 27, 2019)
### Features
- Addon-notes: use @storybook/router <Link> to render links in notes ([#6398](https://github.com/storybooks/storybook/pull/6398))
- Angular: Support default `storybook` project configuration ([#6484](https://github.com/storybooks/storybook/pull/6484))
- Addon-contexts: Improve Vue integration ([#6632](https://github.com/storybooks/storybook/pull/6632))
- Addon-a11y: Design enhancements ([#6563](https://github.com/storybooks/storybook/pull/6563))
### Bug Fixes
- UI: `active` PropTypes on MobileLayout ([#6241](https://github.com/storybooks/storybook/pull/6241))
- Core: Fix css import when sideEffects is false ([#6650](https://github.com/storybooks/storybook/pull/6650))
- Core: Fix infinite loop with special characters in kind names ([#6607](https://github.com/storybooks/storybook/pull/6607))
- UI: Fix 'Escape' onKeyUp event doesn't work ([#6578](https://github.com/storybooks/storybook/pull/6578))
### Maintenance
- UI: Add missing props in stories ([#6353](https://github.com/storybooks/storybook/pull/6353))
- Build: tslint, and use eslint for everything ([#6621](https://github.com/storybooks/storybook/pull/6621))
- Build: deploy to local registry ([#6619](https://github.com/storybooks/storybook/pull/6619))
### Dependency Upgrades
- Bump ts-node from 8.0.3 to 8.1.0 ([#6585](https://github.com/storybooks/storybook/pull/6585))
- Bump semver from 5.7.0 to 6.0.0 ([#6580](https://github.com/storybooks/storybook/pull/6580))
- Bump react-color from 2.17.0 to 2.17.1 ([#6583](https://github.com/storybooks/storybook/pull/6583))
## 5.1.0-alpha.34 (April 24, 2019)
### Features
- Addon-contexts: Add URL query param feature ([#6601](https://github.com/storybooks/storybook/pull/6601))
- UI: Add classNames to sidebar nav elements ([#6571](https://github.com/storybooks/storybook/pull/6571))
### Bug Fixes
- Addon-knobs: Check color knob value before applying uppercase ([#6598](https://github.com/storybooks/storybook/pull/6598))
- React-native: Restore title in section header ([#6599](https://github.com/storybooks/storybook/pull/6599))
## 5.1.0-alpha.33 (April 23, 2019) ## 5.1.0-alpha.33 (April 23, 2019)
### Features ### Features
* React: Add support for create-react-app@3.0.0 ([#6560](https://github.com/storybooks/storybook/pull/6560)) - React: Add support for create-react-app@3.0.0 ([#6560](https://github.com/storybooks/storybook/pull/6560))
## 5.1.0-alpha.32 (April 22, 2019) ## 5.1.0-alpha.32 (April 22, 2019)
### Bug Fixes ### Bug Fixes
* Addon-contexts: bug-fixing, testing, typing ([#6572](https://github.com/storybooks/storybook/pull/6572)) - Addon-contexts: bug-fixing, testing, typing ([#6572](https://github.com/storybooks/storybook/pull/6572))
### Dependency Upgrades ### Dependency Upgrades
* CHANGE opn to open ([#6567](https://github.com/storybooks/storybook/pull/6567)) - CHANGE opn to open ([#6567](https://github.com/storybooks/storybook/pull/6567))
## 5.1.0-alpha.31 (April 19, 2019) ## 5.1.0-alpha.31 (April 19, 2019)
### Features ### Features
* Addon-backgrounds: Emit event on updating background ([#6561](https://github.com/storybooks/storybook/pull/6561)) - Addon-backgrounds: Emit event on updating background ([#6561](https://github.com/storybooks/storybook/pull/6561))
* Addon-contexts: Merge into monorepo ([#6559](https://github.com/storybooks/storybook/pull/6559)) - Addon-contexts: Merge into monorepo ([#6559](https://github.com/storybooks/storybook/pull/6559))
### Bug Fixes ### Bug Fixes
* Angular: Fix sourceMap property of angulars webpack config ([#6535](https://github.com/storybooks/storybook/pull/6535)) - Angular: Fix sourceMap property of angulars webpack config ([#6535](https://github.com/storybooks/storybook/pull/6535))
* Addon-jest: Fix result display ([#6539](https://github.com/storybooks/storybook/pull/6539)) - Addon-jest: Fix result display ([#6539](https://github.com/storybooks/storybook/pull/6539))
### Dependency Upgrades ### Dependency Upgrades
* Bump ember-source from 3.8.1 to 3.9.1 ([#6531](https://github.com/storybooks/storybook/pull/6531)) - Bump ember-source from 3.8.1 to 3.9.1 ([#6531](https://github.com/storybooks/storybook/pull/6531))
* Bump typescript from 3.4.2 to 3.4.3 ([#6528](https://github.com/storybooks/storybook/pull/6528)) - Bump typescript from 3.4.2 to 3.4.3 ([#6528](https://github.com/storybooks/storybook/pull/6528))
## 5.0.10 (April 18, 2019) ## 5.0.10 (April 18, 2019)

View File

@ -28,14 +28,16 @@ To test your project against the current latest version of storybook, you can cl
git clone https://github.com/storybooks/storybook.git git clone https://github.com/storybooks/storybook.git
cd storybook cd storybook
yarn install yarn install
yarn bootstrap --core yarn bootstrap
``` ```
The bootstrap command might ask which sections of the codebase you want to bootstrap. Unless you're going to work with ReactNative or the Documentation, you can keep the default. The bootstrap command might ask which sections of the codebase you want to bootstrap. Unless you're going to work with ReactNative or the Documentation, you can keep the default.
You can also pick directly from CLI: You can also pick directly from CLI:
yarn bootstrap --core ```sh
yarn bootstrap --core
```
#### 2a. Run unit tests #### 2a. Run unit tests
@ -67,7 +69,7 @@ Before the tests are run, the project must be bootstrapped with core. You can ac
This option executes tests from `<rootdir>/examples/official-storybook` This option executes tests from `<rootdir>/examples/official-storybook`
In order for the image snapshots to be correctly generated, you must have a static build of the storybook up-to-date : In order for the image snapshots to be correctly generated, you must have a static build of the storybook up-to-date :
```javascript ```sh
cd examples/official-storybook cd examples/official-storybook
yarn build-storybook yarn build-storybook
cd ../.. cd ../..
@ -80,68 +82,118 @@ Puppeteer is used to launch and grab screenshots of example pages, while jest is
If you made any changes to the `lib/cli` package, the easiest way to verify that it doesn't break anything is to run e2e tests: If you made any changes to the `lib/cli` package, the easiest way to verify that it doesn't break anything is to run e2e tests:
yarn test --cli ```sh
yarn test --cli
```
This will run a bash script located at `lib/cli/test/run_tests.sh`. It will copy the contents of `fixtures` into a temporary `run` directory, run `getstorybook` in each of the subdirectories, and check that storybook starts successfully using `yarn storybook --smoke-test`. This will run a bash script located at `lib/cli/test/run_tests.sh`. It will copy the contents of `fixtures` into a temporary `run` directory, run `getstorybook` in each of the subdirectories, and check that storybook starts successfully using `yarn storybook --smoke-test`.
After that, the `run` directory content will be compared with `snapshots`. You can update the snapshots by passing an `--update` flag: After that, the `run` directory content will be compared with `snapshots`. You can update the snapshots by passing an `--update` flag:
yarn test --cli --update ```sh
yarn test --cli --update
```
In that case, please check the git diff before committing to make sure it only contains the intended changes. In that case, please check the git diff before committing to make sure it only contains the intended changes.
#### 2c. Link `storybook` and any other required dependencies #### 2c. Run Linter
If you want to test your own existing project using the GitHub version of storybook, you need to `link` the packages you use in your project. We use eslint as a linter for all code (including typescript code).
All you have to run is:
```sh ```sh
cd app/react yarn lint
yarn link
cd <your-project>
yarn link @storybook/react
# repeat with whichever other parts of the monorepo you are using.
``` ```
It can be immensely helpful to get feedback in your editor, if you're using VsCode, you should install the `eslint` plugin and configure it with these settings:
```plaintext
"eslint.autoFixOnSave": true,
"eslint.packageManager": "yarn",
"eslint.options": {
"cache": true,
"cacheLocation": ".cache/eslint",
"extensions": [".js", ".jsx", ".mjs", ".json", ".ts", ".tsx"]
},
"eslint.validate": [
"javascript",
"javascriptreact",
{"language": "typescript", "autoFix": true },
{"language": "typescriptreact", "autoFix": true }
],
"eslint.alwaysShowStatus": true
```
This should enable auto-fix for all source files, and give linting warnings and errors within your editor.
### Reproductions ### Reproductions
#### In the monorepo
The best way to help figure out an issue you are having is to produce a minimal reproduction against the `master` branch. The best way to help figure out an issue you are having is to produce a minimal reproduction against the `master` branch.
A good way to do that is using the example `cra-kitchen-sink` app embedded in this repository: A good way to do that is using the example `cra-kitchen-sink` app embedded in this repository:
```sh ```sh
# Download and build this repository: # Download and build this repository:
git clone https://github.com/storybooks/storybook.git git clone https://github.com/storybooks/storybook.git
cd storybook cd storybook
yarn install yarn install
yarn bootstrap --core yarn bootstrap --core
# make changes to try and reproduce the problem, such as adding components + stories # make changes to try and reproduce the problem, such as adding components + stories
cd examples/cra-kitchen-sink cd examples/cra-kitchen-sink
yarn storybook yarn storybook
# see if you can see the problem, if so, commit it: # see if you can see the problem, if so, commit it:
git checkout "branch-describing-issue" git checkout "branch-describing-issue"
git add -A git add -A
git commit -m "reproduction for issue #123" git commit -m "reproduction for issue #123"
# fork the storybook repo to your account, then add the resulting remote # fork the storybook repo to your account, then add the resulting remote
git remote add <your-username> https://github.com/<your-username>/storybook.git git remote add <your-username> https://github.com/<your-username>/storybook.git
git push -u <your-username> master git push -u <your-username> master
``` ```
If you follow that process, you can then link to the GitHub repository in the issue. See <https://github.com/storybooks/storybook/issues/708#issuecomment-290589886> for an example. If you follow that process, you can then link to the GitHub repository in the issue. See <https://github.com/storybooks/storybook/issues/708#issuecomment-290589886> for an example.
**NOTE**: If your issue involves a webpack config, create-react-app will prevent you from modifying the _app's_ webpack config, however, you can still modify storybook's to mirror your app's version of the storybook. Alternatively, use `yarn eject` in the CRA app to get a modifiable webpack config. **NOTE**: If your issue involves a webpack config, create-react-app will prevent you from modifying the _app's_ webpack config, however, you can still modify storybook's to mirror your app's version of the storybook. Alternatively, use `yarn eject` in the CRA app to get a modifiable webpack config.
#### Outside the monorepo
Sometimes your storybook is deeply ingrained in your own setup and it's hard to create a minimal viable reproduction somewhere else.
Inside the storybook repo we have a script that allows you to test the packages inside this repo in your own seperate project.
You can use `npm link` on all packages, but npm linking is cumbersome and has subtle differences from what happens in a registry-based installation.
So the way our script works is that it:
- sets up a npm registry running on your own local machine
- changes your default registry to this local one
- builds all packages in the storybook repo
- publishes all packages as latest
Our script leaves the local registry running, for **as long as you keep it running** you can install storybook packages from this local registry.
- Navigate to your own project and then change `package.json` so the storybook packages match the version of the one you just published.
- Then just do the normal install procedure using `yarn` or `npm`
- Start using your storybook as normally.
If you've made a change to storybook's codebase and would want this change to be reflected in your app:
- Ensure the storybook packages are transpiled, by either having run `yarn dev` or `yarn bootstrap --core`.
- Go to the terminal where the local regitry is running and press `<Enter>`. This will kick off a new publish.
- Run the install procedure again in your local repo, (you may need to clean out node_modules first).
- Restart your storybook.
### Updating Tests ### Updating Tests
Before any contributes are submitted in a PR, make sure to add or update meaningful tests. A PR that has failing tests will be regarded as a “Work in Progress” and will not be merged until all tests pass. Before any contributes are submitted in a PR, make sure to add or update meaningful tests. A PR that has failing tests will be regarded as a “Work in Progress” and will not be merged until all tests pass.
When creating new unit test files, the tests should adhere to a particular folder structure and naming convention, as defined below. When creating new unit test files, the tests should adhere to a particular folder structure and naming convention, as defined below.
```sh ```sh
#Proper naming convention and structure for js tests files # Proper naming convention and structure for js tests files
+-- parentFolder +-- parentFolder
| +-- [filename].js | +-- [filename].js
| +-- [filename].test.js | +-- [filename].test.js
@ -149,14 +201,9 @@ When creating new unit test files, the tests should adhere to a particular folde
## Pull Requests (PRs) ## Pull Requests (PRs)
We welcome your contributions. There are many ways you can help us. This is few of those ways: We welcome all contributions. There are many ways you can help us. This is few of those ways:
- Fix typos and add more [documentation](https://github.com/storybooks/storybook/labels/needs%20docs). Before you submit a new PR, make sure you run `yarn test`. Do not submit a PR if tests are failing. If you need any help, the best way is to [join the discord server and ask in the maintenance channel](https://discord.gg/sMFvFsG).
- Try to fix some [bugs](https://github.com/storybooks/storybook/labels/bug).
- Work on [API](https://github.com/storybooks/storybook/labels/enhancement%3A%20api), [Addons](https://github.com/storybooks/storybook/labels/enhancement%3A%20addons), [UI](https://github.com/storybooks/storybook/labels/enhancement%3A%20ui) or [Webpack](https://github.com/storybooks/storybook/labels/enhancement%3A%20webpack) use enhancements and new [features](https://github.com/storybooks/storybook/labels/feature%20request).
- Add more [tests](https://codecov.io/gh/storybooks/storybook/tree/master/packages) (especially for the [UI](https://codecov.io/gh/storybooks/storybook/tree/master/packages/storybook-ui/src)).
Before you submit a new PR, make sure you run `yarn test`. Do not submit a PR if tests are failing. If you need any help, create an issue and ask.
### Reviewing PRs ### Reviewing PRs

View File

@ -16,6 +16,7 @@
- [Addon a11y uses parameters](#addon-a11y-uses-parameters-decorator-renamed) - [Addon a11y uses parameters](#addon-a11y-uses-parameters-decorator-renamed)
- [New keyboard shortcuts defaults](#new-keyboard-shortcuts-defaults) - [New keyboard shortcuts defaults](#new-keyboard-shortcuts-defaults)
- [New URL structure](#new-url-structure) - [New URL structure](#new-url-structure)
- [Vue integration](#vue-integration)
- [From version 4.0.x to 4.1.x](#from-version-40x-to-41x) - [From version 4.0.x to 4.1.x](#from-version-40x-to-41x)
- [Private addon config](#private-addon-config) - [Private addon config](#private-addon-config)
- [React 15.x](#react-15x) - [React 15.x](#react-15x)
@ -107,9 +108,9 @@ module.exports = ({ config }) => ({
...config.module, ...config.module,
rules: [ rules: [
/* your own rules "..." here and/or some subset of config.module.rules */ /* your own rules "..." here and/or some subset of config.module.rules */
] ],
} },
}) });
``` ```
Please refer to the [current custom webpack documentation](https://github.com/storybooks/storybook/blob/next/docs/src/pages/configurations/custom-webpack-config/index.md) for more information on custom webpack config and to [Issue #6081](https://github.com/storybooks/storybook/issues/6081) for more information about the change. Please refer to the [current custom webpack documentation](https://github.com/storybooks/storybook/blob/next/docs/src/pages/configurations/custom-webpack-config/index.md) for more information on custom webpack config and to [Issue #6081](https://github.com/storybooks/storybook/issues/6081) for more information about the change.
@ -395,6 +396,12 @@ You have to replace it with:
start-storybook --https start-storybook --https
``` ```
## Vue integration
The Vue integration was updated, so that every story returned from a story or decorator function is now being normalized with `Vue.extend` **and** is being wrapped by a functional component. Returning a string from a story or decorator function is still supported and is treated as a component with the returned string as the template.
Currently there is no recommended way of accessing the component options of a story inside a decorator.
## From version 4.0.x to 4.1.x ## From version 4.0.x to 4.1.x
There are are a few migrations you should be aware of in 4.1, including one unintentionally breaking change for advanced addon usage. There are are a few migrations you should be aware of in 4.1, including one unintentionally breaking change for advanced addon usage.

View File

@ -4,7 +4,7 @@ This storybook addon can be helpful to make your UI components more accessible.
[Framework Support](https://github.com/storybooks/storybook/blob/master/ADDONS_SUPPORT.md) [Framework Support](https://github.com/storybooks/storybook/blob/master/ADDONS_SUPPORT.md)
![](docs/screenshot.png) ![Screenshot](https://raw.githubusercontent.com/storybooks/storybook/HEAD/addons/a11y/docs/screenshot.png)
## Getting started ## Getting started

View File

@ -1,6 +1,6 @@
{ {
"name": "@storybook/addon-a11y", "name": "@storybook/addon-a11y",
"version": "5.1.0-alpha.33", "version": "5.1.0-rc.0",
"description": "a11y addon for storybook", "description": "a11y addon for storybook",
"keywords": [ "keywords": [
"a11y", "a11y",
@ -26,20 +26,21 @@
"prepare": "node ../../scripts/prepare.js" "prepare": "node ../../scripts/prepare.js"
}, },
"dependencies": { "dependencies": {
"@storybook/addons": "5.1.0-alpha.33", "@storybook/addons": "5.1.0-rc.0",
"@storybook/api": "5.1.0-alpha.33", "@storybook/api": "5.1.0-rc.0",
"@storybook/client-logger": "5.1.0-alpha.33", "@storybook/client-logger": "5.1.0-rc.0",
"@storybook/components": "5.1.0-alpha.33", "@storybook/components": "5.1.0-rc.0",
"@storybook/core-events": "5.1.0-alpha.33", "@storybook/core-events": "5.1.0-rc.0",
"@storybook/theming": "5.1.0-alpha.33", "@storybook/theming": "5.1.0-rc.0",
"axe-core": "^3.2.2", "axe-core": "^3.2.2",
"common-tags": "^1.8.0", "common-tags": "^1.8.0",
"core-js": "^2.6.5", "core-js": "^3.0.1",
"global": "^4.3.2", "global": "^4.3.2",
"hoist-non-react-statics": "^3.3.0", "hoist-non-react-statics": "^3.3.0",
"memoizerific": "^1.11.3", "memoizerific": "^1.11.3",
"react": "^16.8.4", "react": "^16.8.4",
"react-redux": "^7.0.2", "react-redux": "^7.0.2",
"react-sizeme": "^2.5.2",
"redux": "^4.0.1", "redux": "^4.0.1",
"util-deprecate": "^1.0.2" "util-deprecate": "^1.0.2"
}, },

View File

@ -5,7 +5,7 @@ import { ThemeProvider, themes, convert } from '@storybook/theming';
import { STORY_RENDERED } from '@storybook/core-events'; import { STORY_RENDERED } from '@storybook/core-events';
import { ScrollArea } from '@storybook/components'; import { ScrollArea } from '@storybook/components';
import { A11YPanel } from './A11YPanel.tsx'; import { A11YPanel } from './A11YPanel';
import { EVENTS } from '../constants'; import { EVENTS } from '../constants';
function createApi() { function createApi() {

View File

@ -6,12 +6,12 @@ import { STORY_RENDERED } from '@storybook/core-events';
import { ActionBar, Icons, ScrollArea } from '@storybook/components'; import { ActionBar, Icons, ScrollArea } from '@storybook/components';
import { AxeResults, Result } from 'axe-core'; import { AxeResults, Result } from 'axe-core';
import { API } from '@storybook/api';
import { Provider } from 'react-redux';
import { Report } from './Report'; import { Report } from './Report';
import { Tabs } from './Tabs'; import { Tabs } from './Tabs';
import { EVENTS } from '../constants'; import { EVENTS } from '../constants';
import { API } from '@storybook/api';
import { Provider } from 'react-redux';
import store, { clearElements } from '../redux-config'; import store, { clearElements } from '../redux-config';
export enum RuleType { export enum RuleType {
@ -48,7 +48,8 @@ const Incomplete = styled.span(({ theme }) => ({
const Loader = styled(({ className }) => ( const Loader = styled(({ className }) => (
<div className={className}> <div className={className}>
<Icon inline icon="sync" status="running" /> Please wait while a11y scan is running ... <Icon inline icon="sync" status="running" /> Please wait while the accessibility scan is running
...
</div> </div>
))({ ))({
display: 'flex', display: 'flex',
@ -175,10 +176,9 @@ export class A11YPanel extends Component<A11YPanelProps, A11YPanelState> {
label: <Violations>{violations.length} Violations</Violations>, label: <Violations>{violations.length} Violations</Violations>,
panel: ( panel: (
<Report <Report
passes={false}
items={violations} items={violations}
type={RuleType.VIOLATION} type={RuleType.VIOLATION}
empty="No a11y violations found." empty="No accessibility violations found."
/> />
), ),
items: violations, items: violations,
@ -188,10 +188,9 @@ export class A11YPanel extends Component<A11YPanelProps, A11YPanelState> {
label: <Passes>{passes.length} Passes</Passes>, label: <Passes>{passes.length} Passes</Passes>,
panel: ( panel: (
<Report <Report
passes
items={passes} items={passes}
type={RuleType.PASS} type={RuleType.PASS}
empty="No a11y check passed." empty="No accessibility checks passed."
/> />
), ),
items: passes, items: passes,
@ -201,10 +200,9 @@ export class A11YPanel extends Component<A11YPanelProps, A11YPanelState> {
label: <Incomplete>{incomplete.length} Incomplete</Incomplete>, label: <Incomplete>{incomplete.length} Incomplete</Incomplete>,
panel: ( panel: (
<Report <Report
passes={false}
items={incomplete} items={incomplete}
type={RuleType.INCOMPLETION} type={RuleType.INCOMPLETION}
empty="No a11y incomplete found." empty="No accessibility checks incomplete."
/> />
), ),
items: incomplete, items: incomplete,

View File

@ -34,7 +34,7 @@ const ColorIcon = styled.span(
}) })
); );
// tslint:disable-next-line:no-empty-interface // eslint-disable-next-line @typescript-eslint/no-empty-interface
interface ColorBlindnessProps {} interface ColorBlindnessProps {}
interface ColorBlindnessState { interface ColorBlindnessState {
@ -63,7 +63,8 @@ export class ColorBlindness extends Component<ColorBlindnessProps, ColorBlindnes
}; };
onVisibilityChange = (s: boolean) => { onVisibilityChange = (s: boolean) => {
if (this.state.expanded !== s) { const { expanded } = this.state;
if (expanded !== s) {
this.setState({ expanded: s }); this.setState({ expanded: s });
} }
}; };
@ -83,7 +84,7 @@ export class ColorBlindness extends Component<ColorBlindnessProps, ColorBlindnes
'mono', 'mono',
].map(i => ({ ].map(i => ({
id: i, id: i,
title: i, title: i.charAt(0).toUpperCase() + i.slice(1),
onClick: () => { onClick: () => {
this.setFilter(i); this.setFilter(i);
}, },

View File

@ -14,25 +14,25 @@ const Item = styled.li({
const ItemTitle = styled.span(({ theme }) => ({ const ItemTitle = styled.span(({ theme }) => ({
borderBottom: `1px solid ${theme.appBorderColor}`, borderBottom: `1px solid ${theme.appBorderColor}`,
width: '100%', width: '100%',
display: 'inline-block', display: 'flex',
paddingBottom: '6px', paddingBottom: '6px',
marginBottom: '6px', marginBottom: '6px',
justifyContent: 'space-between',
})); }));
const HighlightToggleElement = styled.span({ const HighlightToggleElement = styled.span({
fontWeight: 'normal', fontWeight: 'normal',
float: 'right', alignSelf: 'center',
paddingRight: '15px', paddingRight: '15px',
input: { margin: 0 }, input: { margin: 0 },
}); });
interface ElementProps { interface ElementProps {
element: NodeResult; element: NodeResult;
passes: boolean;
type: RuleType; type: RuleType;
} }
const Element: FunctionComponent<ElementProps> = ({ element, passes, type }) => { const Element: FunctionComponent<ElementProps> = ({ element, type }) => {
const { any, all, none } = element; const { any, all, none } = element;
const rules = [...any, ...all, ...none]; const rules = [...any, ...all, ...none];
const highlightToggleId = `${type}-${element.target[0]}`; const highlightToggleId = `${type}-${element.target[0]}`;
@ -51,21 +51,21 @@ const Element: FunctionComponent<ElementProps> = ({ element, passes, type }) =>
/> />
</HighlightToggleElement> </HighlightToggleElement>
</ItemTitle> </ItemTitle>
<Rules rules={rules} passes={passes} /> <Rules rules={rules} />
</Item> </Item>
); );
}; };
interface ElementsProps { interface ElementsProps {
elements: NodeResult[]; elements: NodeResult[];
passes: boolean;
type: RuleType; type: RuleType;
} }
export const Elements: FunctionComponent<ElementsProps> = ({ elements, passes, type }) => ( export const Elements: FunctionComponent<ElementsProps> = ({ elements, type }) => (
<ol> <ol>
{elements.map((element, index) => ( {elements.map((element, index) => (
<Element passes={passes} element={element} key={index} type={type} /> // eslint-disable-next-line react/no-array-index-key
<Element element={element} key={index} type={type} />
))} ))}
</ol> </ol>
); );

View File

@ -2,8 +2,8 @@ import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { ThemeProvider, themes, convert } from '@storybook/theming'; import { ThemeProvider, themes, convert } from '@storybook/theming';
import HighlightToggle from './HighlightToggle.tsx'; import HighlightToggle from './HighlightToggle';
import store from '../../redux-config.tsx'; import store from '../../redux-config';
function ThemedHighlightToggle(props) { function ThemedHighlightToggle(props) {
return ( return (

View File

@ -1,3 +1,4 @@
import { document } from 'global';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { styled, themes, convert } from '@storybook/theming'; import { styled, themes, convert } from '@storybook/theming';
@ -10,6 +11,7 @@ import { IFRAME } from '../../constants';
export class HighlightedElementData { export class HighlightedElementData {
originalOutline: string; originalOutline: string;
isHighlighted: boolean; isHighlighted: boolean;
} }
@ -50,6 +52,7 @@ function getElementBySelectorPath(elementPath: string): HTMLElement {
} }
function setElementOutlineStyle(targetElement: HTMLElement, outlineStyle: string): void { function setElementOutlineStyle(targetElement: HTMLElement, outlineStyle: string): void {
// eslint-disable-next-line no-param-reassign
targetElement.style.outline = outlineStyle; targetElement.style.outline = outlineStyle;
} }
@ -65,6 +68,7 @@ function areAllRequiredElementsHighlighted(
); );
}).length; }).length;
// eslint-disable-next-line no-nested-ternary
return highlightedCount === 0 return highlightedCount === 0
? CheckBoxStates.UNCHECKED ? CheckBoxStates.UNCHECKED
: highlightedCount === elementsToHighlight.length : highlightedCount === elementsToHighlight.length
@ -99,58 +103,31 @@ class HighlightToggle extends Component<ToggleProps> {
private checkBoxRef = React.createRef<HTMLInputElement>(); private checkBoxRef = React.createRef<HTMLInputElement>();
componentDidMount() { componentDidMount() {
this.props.elementsToHighlight.forEach(element => { const { elementsToHighlight, highlightedElementsMap } = this.props;
elementsToHighlight.forEach(element => {
const targetElement = getElementBySelectorPath(element.target[0]); const targetElement = getElementBySelectorPath(element.target[0]);
if (targetElement && !this.props.highlightedElementsMap.has(targetElement)) { if (targetElement && !highlightedElementsMap.has(targetElement)) {
this.saveElementDataToMap(targetElement, false, targetElement.style.outline); this.saveElementDataToMap(targetElement, false, targetElement.style.outline);
} }
}); });
} }
componentDidUpdate(prevProps: Readonly<ToggleProps>): void { componentDidUpdate(prevProps: Readonly<ToggleProps>): void {
const { indeterminate } = this.props;
if (this.checkBoxRef.current) { if (this.checkBoxRef.current) {
this.checkBoxRef.current.indeterminate = this.props.indeterminate; this.checkBoxRef.current.indeterminate = indeterminate;
} }
} }
highlightRuleLocation(targetElement: HTMLElement, addHighlight: boolean): void {
if (!targetElement) {
return;
}
if (addHighlight) {
setElementOutlineStyle(targetElement, `${colorsByType[this.props.type]} dotted 1px`);
return;
}
if (this.props.highlightedElementsMap.has(targetElement)) {
setElementOutlineStyle(
targetElement,
this.props.highlightedElementsMap.get(targetElement).originalOutline
);
}
}
saveElementDataToMap(
targetElement: HTMLElement,
isHighlighted: boolean,
originalOutline: string
): void {
const data: HighlightedElementData = new HighlightedElementData();
data.isHighlighted = isHighlighted;
data.originalOutline = originalOutline;
const payload = { element: targetElement, highlightedElementData: data };
this.props.addElement(payload);
}
onToggle = (): void => { onToggle = (): void => {
this.props.elementsToHighlight.forEach(element => { const { elementsToHighlight, highlightedElementsMap } = this.props;
elementsToHighlight.forEach(element => {
const targetElement = getElementBySelectorPath(element.target[0]); const targetElement = getElementBySelectorPath(element.target[0]);
if (!this.props.highlightedElementsMap.has(targetElement)) { if (!highlightedElementsMap.has(targetElement)) {
return; return;
} }
const originalOutline = this.props.highlightedElementsMap.get(targetElement).originalOutline; const { originalOutline } = highlightedElementsMap.get(targetElement);
const { isHighlighted } = this.props.highlightedElementsMap.get(targetElement); const { isHighlighted } = highlightedElementsMap.get(targetElement);
const { isToggledOn } = this.props; const { isToggledOn } = this.props;
if ((isToggledOn && isHighlighted) || (!isToggledOn && !isHighlighted)) { if ((isToggledOn && isHighlighted) || (!isToggledOn && !isHighlighted)) {
const addHighlight = !isToggledOn && !isHighlighted; const addHighlight = !isToggledOn && !isHighlighted;
@ -160,16 +137,49 @@ class HighlightToggle extends Component<ToggleProps> {
}); });
}; };
highlightRuleLocation(targetElement: HTMLElement, addHighlight: boolean): void {
const { highlightedElementsMap, type } = this.props;
if (!targetElement) {
return;
}
if (addHighlight) {
setElementOutlineStyle(targetElement, `${colorsByType[type]} dotted 1px`);
return;
}
if (highlightedElementsMap.has(targetElement)) {
setElementOutlineStyle(
targetElement,
highlightedElementsMap.get(targetElement).originalOutline
);
}
}
saveElementDataToMap(
targetElement: HTMLElement,
isHighlighted: boolean,
originalOutline: string
): void {
const { addElement: localAddElement } = this.props;
const data: HighlightedElementData = new HighlightedElementData();
data.isHighlighted = isHighlighted;
data.originalOutline = originalOutline;
const payload = { element: targetElement, highlightedElementData: data };
localAddElement(payload);
}
render() { render() {
const { toggleId, elementsToHighlight, isToggledOn } = this.props;
return ( return (
<Checkbox <Checkbox
ref={this.checkBoxRef} ref={this.checkBoxRef}
id={this.props.toggleId} id={toggleId}
type="checkbox" type="checkbox"
aria-label="Highlight result" aria-label="Highlight result"
disabled={!this.props.elementsToHighlight.length} disabled={!elementsToHighlight.length}
onChange={this.onToggle} onChange={this.onToggle}
checked={this.props.isToggledOn} checked={isToggledOn}
/> />
); );
} }

View File

@ -12,6 +12,7 @@ import HighlightToggle from './HighlightToggle';
const Wrapper = styled.div(({ theme }) => ({ const Wrapper = styled.div(({ theme }) => ({
display: 'flex', display: 'flex',
width: '100%',
borderBottom: `1px solid ${theme.appBorderColor}`, borderBottom: `1px solid ${theme.appBorderColor}`,
'&:hover': { '&:hover': {
background: theme.background.hoverable, background: theme.background.hoverable,
@ -49,14 +50,12 @@ const HighlightToggleElement = styled.span({
fontWeight: 'normal', fontWeight: 'normal',
float: 'right', float: 'right',
marginRight: '15px', marginRight: '15px',
marginTop: '10px', alignSelf: 'center',
input: { margin: 0 }, input: { margin: 0 },
}); });
interface ItemProps { interface ItemProps {
item: Result; item: Result;
passes: boolean;
type: RuleType; type: RuleType;
} }
@ -75,7 +74,7 @@ export class Item extends Component<ItemProps, ItemState> {
})); }));
render() { render() {
const { item, passes, type } = this.props; const { item, type } = this.props;
const { open } = this.state; const { open } = this.state;
const highlightToggleId = `${type}-${item.id}`; const highlightToggleId = `${type}-${item.id}`;
@ -104,7 +103,7 @@ export class Item extends Component<ItemProps, ItemState> {
{open ? ( {open ? (
<Fragment> <Fragment>
<Info item={item} key="info" /> <Info item={item} key="info" />
<Elements elements={item.nodes} passes={passes} type={type} key="elements" /> <Elements elements={item.nodes} type={type} key="elements" />
<Tags tags={item.tags} key="tags" /> <Tags tags={item.tags} key="tags" />
</Fragment> </Fragment>
) : null} ) : null}

View File

@ -1,8 +1,9 @@
import React, { FunctionComponent } from 'react'; import React, { FunctionComponent } from 'react';
import { styled } from '@storybook/theming'; import { styled } from '@storybook/theming';
import { Badge, Icons } from '@storybook/components';
import { Icons } from '@storybook/components';
import { CheckResult } from 'axe-core'; import { CheckResult } from 'axe-core';
import { SizeMe } from 'react-sizeme';
import { RuleType } from '../A11YPanel';
const impactColors = { const impactColors = {
minor: '#f1c40f', minor: '#f1c40f',
@ -15,18 +16,33 @@ const impactColors = {
const List = styled.div({ const List = styled.div({
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
padding: '4px', paddingBottom: '4px',
paddingRight: '4px',
paddingTop: '4px',
fontWeight: '400', fontWeight: '400',
} as any); } as any);
const Item = styled.div({ const Item = styled.div(({ elementWidth }: { elementWidth: number }) => {
display: 'flex', const maxWidthBeforeBreak = 407;
flexDirection: 'row', return {
marginBottom: '6px', flexDirection: elementWidth > maxWidthBeforeBreak ? 'row' : 'inherit',
marginBottom: elementWidth > maxWidthBeforeBreak ? '6px' : '12px',
display: elementWidth > maxWidthBeforeBreak ? 'flex' : 'block',
};
}); });
const StyledBadge = styled(Badge)(({ status }: { status: string }) => ({
padding: '2px 8px',
marginBottom: '3px',
minWidth: '65px',
maxWidth: 'fit-content',
width: '100%',
textAlign: 'center',
}));
const Message = styled.div({ const Message = styled.div({
paddingLeft: '6px', paddingLeft: '6px',
paddingRight: '23px',
}); });
const Status = styled.div(({ passes, impact }: { passes: boolean; impact: string }) => ({ const Status = styled.div(({ passes, impact }: { passes: boolean; impact: string }) => ({
@ -40,30 +56,64 @@ const Status = styled.div(({ passes, impact }: { passes: boolean; impact: string
}, },
})); }));
interface RuleProps { export enum ImpactValue {
rule: CheckResult; MINOR = 'minor',
passes: boolean; MODERATE = 'moderate',
SERIOUS = 'serious',
CRITICAL = 'critical',
} }
const Rule: FunctionComponent<RuleProps> = ({ rule, passes }) => ( interface RuleProps {
<Item> rule: CheckResult;
<Status passes={passes || undefined} impact={rule.impact}> }
{passes ? <Icons icon="check" /> : <Icons icon="cross" />}
</Status> const formatSeverityText = (severity: string) => {
return severity
.charAt(0)
.toUpperCase()
.concat(severity.slice(1));
};
const Rule: FunctionComponent<RuleProps> = ({ rule }) => {
let badgeType: any = null;
switch (rule.impact) {
case ImpactValue.CRITICAL:
badgeType = 'critical';
break;
case ImpactValue.SERIOUS:
badgeType = 'negative';
break;
case ImpactValue.MODERATE:
badgeType = 'warning';
break;
case ImpactValue.MINOR:
badgeType = 'neutral';
break;
default:
break;
}
return (
<SizeMe refreshMode="debounce">
{({ size }: { size: any }) => (
<Item elementWidth={size.width}>
<StyledBadge status={badgeType}>{formatSeverityText(rule.impact)}</StyledBadge>
<Message>{rule.message}</Message> <Message>{rule.message}</Message>
</Item> </Item>
); )}
</SizeMe>
);
};
interface RulesProps { interface RulesProps {
rules: CheckResult[]; rules: CheckResult[];
passes: boolean;
} }
export const Rules: FunctionComponent<RulesProps> = ({ rules, passes }) => { export const Rules: FunctionComponent<RulesProps> = ({ rules }) => {
return ( return (
<List> <List>
{rules.map((rule, index) => ( {rules.map((rule, index) => (
<Rule passes={passes} rule={rule} key={index} /> // eslint-disable-next-line react/no-array-index-key
<Rule rule={rule} key={index} />
))} ))}
</List> </List>
); );

View File

@ -21,6 +21,7 @@ exports[`HighlightToggle component should match snapshot 1`] = `
theme={ theme={
Object { Object {
"addonActionsTheme": Object { "addonActionsTheme": Object {
"ARROW_ANIMATION_DURATION": "0",
"ARROW_COLOR": "rgba(0,0,0,0.3)", "ARROW_COLOR": "rgba(0,0,0,0.3)",
"ARROW_FONT_SIZE": 8, "ARROW_FONT_SIZE": 8,
"ARROW_MARGIN_RIGHT": 4, "ARROW_MARGIN_RIGHT": 4,
@ -37,8 +38,10 @@ exports[`HighlightToggle component should match snapshot 1`] = `
"HTML_TAGNAME_TEXT_TRANSFORM": "lowercase", "HTML_TAGNAME_TEXT_TRANSFORM": "lowercase",
"HTML_TAG_COLOR": "rgb(168, 148, 166)", "HTML_TAG_COLOR": "rgb(168, 148, 166)",
"OBJECT_NAME_COLOR": "rgb(136, 19, 145)", "OBJECT_NAME_COLOR": "rgb(136, 19, 145)",
"OBJECT_PREVIEW_ARRAY_MAX_PROPERTIES": 10,
"OBJECT_PREVIEW_OBJECT_MAX_PROPERTIES": 5,
"OBJECT_VALUE_BOOLEAN_COLOR": "rgb(28, 0, 207)", "OBJECT_VALUE_BOOLEAN_COLOR": "rgb(28, 0, 207)",
"OBJECT_VALUE_FUNCTION_KEYWORD_COLOR": "rgb(170, 13, 145)", "OBJECT_VALUE_FUNCTION_PREFIX_COLOR": "rgb(13, 34, 170)",
"OBJECT_VALUE_NULL_COLOR": "rgb(128, 128, 128)", "OBJECT_VALUE_NULL_COLOR": "rgb(128, 128, 128)",
"OBJECT_VALUE_NUMBER_COLOR": "rgb(28, 0, 207)", "OBJECT_VALUE_NUMBER_COLOR": "rgb(28, 0, 207)",
"OBJECT_VALUE_REGEXP_COLOR": "rgb(196, 26, 22)", "OBJECT_VALUE_REGEXP_COLOR": "rgb(196, 26, 22)",
@ -125,6 +128,7 @@ exports[`HighlightToggle component should match snapshot 1`] = `
"app": "#F6F9FC", "app": "#F6F9FC",
"bar": "#FFFFFF", "bar": "#FFFFFF",
"content": "#FFFFFF", "content": "#FFFFFF",
"critical": "#FF4400",
"gridCellSize": 10, "gridCellSize": 10,
"hoverable": "rgba(0,0,0,.05)", "hoverable": "rgba(0,0,0,.05)",
"negative": "#FEDED2", "negative": "#FEDED2",
@ -256,6 +260,7 @@ exports[`HighlightToggle component should match snapshot 1`] = `
"color": Object { "color": Object {
"ancillary": "#22a699", "ancillary": "#22a699",
"border": "rgba(0,0,0,.1)", "border": "rgba(0,0,0,.1)",
"critical": "#FFFFFF",
"dark": "#666666", "dark": "#666666",
"darker": "#444444", "darker": "#444444",
"darkest": "#333333", "darkest": "#333333",

View File

@ -7,14 +7,13 @@ import { RuleType } from '../A11YPanel';
export interface ReportProps { export interface ReportProps {
items: Result[]; items: Result[];
empty: string; empty: string;
passes: boolean;
type: RuleType; type: RuleType;
} }
export const Report: FunctionComponent<ReportProps> = ({ items, empty, type, passes }) => ( export const Report: FunctionComponent<ReportProps> = ({ items, empty, type }) => (
<Fragment> <Fragment>
{items && items.length ? ( {items && items.length ? (
items.map(item => <Item passes={passes} item={item} key={`${type}:${item.id}`} type={type} />) items.map(item => <Item item={item} key={`${type}:${item.id}`} type={type} />)
) : ( ) : (
<Placeholder key="placeholder">{empty}</Placeholder> <Placeholder key="placeholder">{empty}</Placeholder>
)} )}

View File

@ -1,13 +1,13 @@
import React, { Component, SyntheticEvent } from 'react'; import React, { Component, SyntheticEvent } from 'react';
import { styled } from '@storybook/theming'; import { styled, themes } from '@storybook/theming';
import { NodeResult, Result } from 'axe-core';
import { SizeMe } from 'react-sizeme';
import store, { clearElements } from '../redux-config'; import store, { clearElements } from '../redux-config';
import HighlightToggle from './Report/HighlightToggle'; import HighlightToggle from './Report/HighlightToggle';
import { NodeResult, Result } from 'axe-core';
import { RuleType } from './A11YPanel'; import { RuleType } from './A11YPanel';
// TODO: reuse the Tabs component from @storybook/theming instead // TODO: reuse the Tabs component from @storybook/theming instead of re-building identical functionality
// of re-building identical functionality
const Container = styled.div({ const Container = styled.div({
width: '100%', width: '100%',
@ -18,26 +18,34 @@ const Container = styled.div({
const HighlightToggleLabel = styled.label(({ theme }) => ({ const HighlightToggleLabel = styled.label(({ theme }) => ({
cursor: 'pointer', cursor: 'pointer',
userSelect: 'none', userSelect: 'none',
marginBottom: '3px',
marginRight: '3px',
color: theme.color.dark, color: theme.color.dark,
})); }));
const GlobalToggleWrapper = styled.div(({ theme }) => ({ const GlobalToggle = styled.div(({ elementWidth }: { elementWidth: number }) => {
padding: '10px 15px 10px 0', const maxWidthBeforeBreak = 450;
return {
cursor: 'pointer', cursor: 'pointer',
fontSize: theme.typography.size.s2 - 1, fontSize: '14px',
height: 40, padding: elementWidth > maxWidthBeforeBreak ? '12px 15px 10px 0' : '12px 0px 3px 12px',
height: '40px',
border: 'none', border: 'none',
marginTop: elementWidth > maxWidthBeforeBreak ? '-40px' : '0px',
display: 'flex', float: elementWidth > maxWidthBeforeBreak ? 'right' : 'left',
display: elementWidth > maxWidthBeforeBreak ? 'flex' : 'block',
alignItems: 'center', alignItems: 'center',
width: elementWidth > maxWidthBeforeBreak ? 'auto' : '100%',
borderBottom: elementWidth > maxWidthBeforeBreak ? 'none' : '1px solid rgba(0,0,0,.1)',
input: { input: {
marginLeft: 10, marginLeft: '10',
marginRight: 0, marginRight: '0',
marginTop: 0, marginTop: '0',
marginBottom: 0, marginBottom: '0',
}, },
})); };
});
const Item = styled.button( const Item = styled.button(
({ theme }) => ({ ({ theme }) => ({
@ -78,12 +86,12 @@ const List = styled.div(({ theme }) => ({
})); }));
interface TabsProps { interface TabsProps {
tabs: Array<{ tabs: {
label: JSX.Element; label: JSX.Element;
panel: JSX.Element; panel: JSX.Element;
items: Result[]; items: Result[];
type: RuleType; type: RuleType;
}>; }[];
} }
interface TabsState { interface TabsState {
@ -113,11 +121,14 @@ export class Tabs extends Component<TabsProps, TabsState> {
const highlightToggleId = `${tabs[active].type}-global-checkbox`; const highlightToggleId = `${tabs[active].type}-global-checkbox`;
const highlightLabel = `Highlight results`; const highlightLabel = `Highlight results`;
return ( return (
<SizeMe refreshMode="debounce">
{({ size }: { size: any }) => (
<Container> <Container>
<List> <List>
<TabsWrapper> <TabsWrapper>
{tabs.map((tab, index) => ( {tabs.map((tab, index) => (
<Item <Item
/* eslint-disable-next-line react/no-array-index-key */
key={index} key={index}
data-index={index} data-index={index}
active={active === index} active={active === index}
@ -127,7 +138,9 @@ export class Tabs extends Component<TabsProps, TabsState> {
</Item> </Item>
))} ))}
</TabsWrapper> </TabsWrapper>
<GlobalToggleWrapper> </List>
{tabs[active].items.length > 0 ? (
<GlobalToggle elementWidth={size.width}>
<HighlightToggleLabel htmlFor={highlightToggleId}> <HighlightToggleLabel htmlFor={highlightToggleId}>
{highlightLabel} {highlightLabel}
</HighlightToggleLabel> </HighlightToggleLabel>
@ -137,10 +150,12 @@ export class Tabs extends Component<TabsProps, TabsState> {
elementsToHighlight={retrieveAllNodesFromResults(tabs[active].items)} elementsToHighlight={retrieveAllNodesFromResults(tabs[active].items)}
label={highlightLabel} label={highlightLabel}
/> />
</GlobalToggleWrapper> </GlobalToggle>
</List> ) : null}
{tabs[active].panel} {tabs[active].panel}
</Container> </Container>
)}
</SizeMe>
); );
} }
} }

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-object-literal-type-assertion */
import { document } from 'global'; import { document } from 'global';
import axe, { AxeResults, ElementContext, RunOptions, Spec } from 'axe-core'; import axe, { AxeResults, ElementContext, RunOptions, Spec } from 'axe-core';
import deprecate from 'util-deprecate'; import deprecate from 'util-deprecate';
@ -35,7 +36,6 @@ const run = (element: ElementContext, config: Spec, options: RunOptions) => {
.run( .run(
element || getElement(), element || getElement(),
options || options ||
// tslint:disable-next-line:no-object-literal-type-assertion
({ ({
restoreScroll: true, restoreScroll: true,
} as RunOptions) // cast to RunOptions is necessary because axe types are not up to date } as RunOptions) // cast to RunOptions is necessary because axe types are not up to date

View File

@ -28,8 +28,10 @@ function rootReducer(state = initialState, action: any) {
action.payload.highlightedElementData action.payload.highlightedElementData
), ),
}; };
} else if (action.type === CLEAR_ELEMENTS) { }
for (let key of Array.from(state.highlightedElementsMap.keys())) { if (action.type === CLEAR_ELEMENTS) {
// eslint-disable-next-line no-restricted-syntax
for (const key of Array.from(state.highlightedElementsMap.keys())) {
key.style.outline = state.highlightedElementsMap.get(key).originalOutline; key.style.outline = state.highlightedElementsMap.get(key).originalOutline;
state.highlightedElementsMap.delete(key); state.highlightedElementsMap.delete(key);
} }

View File

@ -1,10 +1,10 @@
import React, { Fragment, FunctionComponent } from 'react'; import React, { Fragment, FunctionComponent } from 'react';
import { styled } from '@storybook/theming'; import { styled } from '@storybook/theming';
import { addons, types } from '@storybook/addons';
import { ADDON_ID, PANEL_ID } from './constants'; import { ADDON_ID, PANEL_ID } from './constants';
import { ColorBlindness } from './components/ColorBlindness'; import { ColorBlindness } from './components/ColorBlindness';
import { A11YPanel } from './components/A11YPanel'; import { A11YPanel } from './components/A11YPanel';
import { addons, types } from '@storybook/addons';
const Hidden = styled.div(() => ({ const Hidden = styled.div(() => ({
'&, & svg': { '&, & svg': {

View File

@ -1 +1,2 @@
declare module 'global'; declare module 'global';
declare module 'react-sizeme';

View File

@ -4,7 +4,7 @@ Storybook Addon Actions can be used to display data received by event handlers i
[Framework Support](https://github.com/storybooks/storybook/blob/master/ADDONS_SUPPORT.md) [Framework Support](https://github.com/storybooks/storybook/blob/master/ADDONS_SUPPORT.md)
![Screenshot](docs/screenshot.png) ![Screenshot](https://raw.githubusercontent.com/storybooks/storybook/HEAD/addons/actions/docs/screenshot.png)
## Getting Started ## Getting Started

View File

@ -1,6 +1,6 @@
{ {
"name": "@storybook/addon-actions", "name": "@storybook/addon-actions",
"version": "5.1.0-alpha.33", "version": "5.1.0-rc.0",
"description": "Action Logger addon for storybook", "description": "Action Logger addon for storybook",
"keywords": [ "keywords": [
"storybook" "storybook"
@ -21,23 +21,23 @@
"prepare": "node ../../scripts/prepare.js" "prepare": "node ../../scripts/prepare.js"
}, },
"dependencies": { "dependencies": {
"@storybook/addons": "5.1.0-alpha.33", "@storybook/addons": "5.1.0-rc.0",
"@storybook/api": "5.1.0-alpha.33", "@storybook/api": "5.1.0-rc.0",
"@storybook/components": "5.1.0-alpha.33", "@storybook/components": "5.1.0-rc.0",
"@storybook/core-events": "5.1.0-alpha.33", "@storybook/core-events": "5.1.0-rc.0",
"@storybook/theming": "5.1.0-alpha.33", "@storybook/theming": "5.1.0-rc.0",
"core-js": "^2.6.5", "core-js": "^3.0.1",
"fast-deep-equal": "^2.0.1", "fast-deep-equal": "^2.0.1",
"global": "^4.3.2", "global": "^4.3.2",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"polished": "^3.0.0", "polished": "^3.3.1",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react": "^16.8.4", "react": "^16.8.4",
"react-inspector": "^2.3.1", "react-inspector": "^3.0.2",
"uuid": "^3.3.2" "uuid": "^3.3.2"
}, },
"devDependencies": { "devDependencies": {
"@types/lodash": "^4.14.123", "@types/lodash": "^4.14.129",
"@types/uuid": "^3.4.4" "@types/uuid": "^3.4.4"
}, },
"publishConfig": { "publishConfig": {

View File

@ -1,4 +1,5 @@
export * from './constants'; export * from './constants';
export * from './models';
export * from './preview'; export * from './preview';
if (module && module.hot && module.hot.decline) { if (module && module.hot && module.hot.decline) {

View File

@ -8,8 +8,7 @@ export function action(name: string, options: ActionOptions = {}): HandlerFuncti
...options, ...options,
}; };
// tslint:disable-next-line:no-shadowed-variable const handler = function actionHandler(...args: any[]) {
const handler = function action(...args: any[]) {
const channel = addons.getChannel(); const channel = addons.getChannel();
const id = uuid(); const id = uuid();
const minDepth = 5; // anything less is really just storybook internals const minDepth = 5; // anything less is really just storybook internals

View File

@ -30,7 +30,6 @@ const hasMatchInAncestry = (element: any, selector: any): boolean => {
const createHandlers = (actionsFn: (...arg: any[]) => object, ...args: any[]) => { const createHandlers = (actionsFn: (...arg: any[]) => object, ...args: any[]) => {
const actionsObject = actionsFn(...args); const actionsObject = actionsFn(...args);
return Object.entries(actionsObject).map(([key, action]) => { return Object.entries(actionsObject).map(([key, action]) => {
// eslint-disable-next-line no-unused-vars
const [_, eventName, selector] = key.match(delegateEventSplitter); const [_, eventName, selector] = key.match(delegateEventSplitter);
return { return {
eventName, eventName,

View File

@ -1,6 +1,6 @@
{ {
"name": "@storybook/addon-backgrounds", "name": "@storybook/addon-backgrounds",
"version": "5.1.0-alpha.33", "version": "5.1.0-rc.0",
"description": "A storybook addon to show different backgrounds for your preview", "description": "A storybook addon to show different backgrounds for your preview",
"keywords": [ "keywords": [
"addon", "addon",
@ -25,13 +25,13 @@
"prepare": "node ../../scripts/prepare.js" "prepare": "node ../../scripts/prepare.js"
}, },
"dependencies": { "dependencies": {
"@storybook/addons": "5.1.0-alpha.33", "@storybook/addons": "5.1.0-rc.0",
"@storybook/api": "5.1.0-alpha.33", "@storybook/api": "5.1.0-rc.0",
"@storybook/client-logger": "5.1.0-alpha.33", "@storybook/client-logger": "5.1.0-rc.0",
"@storybook/components": "5.1.0-alpha.33", "@storybook/components": "5.1.0-rc.0",
"@storybook/core-events": "5.1.0-alpha.33", "@storybook/core-events": "5.1.0-rc.0",
"@storybook/theming": "5.1.0-alpha.33", "@storybook/theming": "5.1.0-rc.0",
"core-js": "^2.6.5", "core-js": "^3.0.1",
"memoizerific": "^1.11.3", "memoizerific": "^1.11.3",
"react": "^16.8.4", "react": "^16.8.4",
"util-deprecate": "^1.0.2" "util-deprecate": "^1.0.2"

View File

@ -103,12 +103,14 @@ export class BackgroundSelector extends Component<{ api: API }, State> {
}; };
change = ({ selected, name }: { selected: string; name: string }) => { change = ({ selected, name }: { selected: string; name: string }) => {
this.props.api.emit(EVENTS.UPDATE, { selected, name }); const { api } = this.props;
api.emit(EVENTS.UPDATE, { selected, name });
this.setState({ selected, expanded: false }); this.setState({ selected, expanded: false });
}; };
onVisibilityChange = (s: boolean) => { onVisibilityChange = (s: boolean) => {
if (this.state.expanded !== s) { const { expanded } = this.state;
if (expanded !== s) {
this.setState({ expanded: s }); this.setState({ expanded: s });
} }
}; };

View File

@ -1,4 +1,3 @@
// tslint:disable-next-line:no-implicit-dependencies
export interface ICollection { export interface ICollection {
[p: string]: any; [p: string]: any;
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@storybook/addon-centered", "name": "@storybook/addon-centered",
"version": "5.1.0-alpha.33", "version": "5.1.0-rc.0",
"description": "Storybook decorator to center components", "description": "Storybook decorator to center components",
"keywords": [ "keywords": [
"addon", "addon",
@ -18,15 +18,23 @@
"license": "MIT", "license": "MIT",
"author": "Muhammed Thanish <mnmtanish@gmail.com>", "author": "Muhammed Thanish <mnmtanish@gmail.com>",
"main": "dist/index.js", "main": "dist/index.js",
"jsnext:main": "src/index.js", "types": "dist/index.d.ts",
"scripts": { "scripts": {
"prepare": "node ../../scripts/prepare.js" "prepare": "node ../../scripts/prepare.js"
}, },
"dependencies": { "dependencies": {
"core-js": "^2.6.5", "core-js": "^3.0.1",
"global": "^4.3.2", "global": "^4.3.2",
"util-deprecate": "^1.0.2" "util-deprecate": "^1.0.2"
}, },
"devDependencies": {
"@types/mithril": "^1.1.16"
},
"optionalDependencies": {
"mithril": "*",
"preact": "*",
"react": "*"
},
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
} }

View File

@ -1,11 +1,11 @@
import styles from './styles'; import styles from './styles';
function getComponentSelector(component) { function getComponentSelector(component: any) {
// eslint-disable-next-line no-underscore-dangle // eslint-disable-next-line no-underscore-dangle
return component.__annotations__[0].selector; return component.__annotations__[0].selector;
} }
function getTemplate(metadata) { function getTemplate(metadata: any) {
let tpl = ''; let tpl = '';
if (metadata.component) { if (metadata.component) {
const selector = getComponentSelector(metadata.component); const selector = getComponentSelector(metadata.component);
@ -24,7 +24,7 @@ function getTemplate(metadata) {
</div>`; </div>`;
} }
function getModuleMetadata(metadata) { function getModuleMetadata(metadata: any) {
const { moduleMetadata, component } = metadata; const { moduleMetadata, component } = metadata;
if (component && !moduleMetadata) { if (component && !moduleMetadata) {
@ -43,7 +43,7 @@ function getModuleMetadata(metadata) {
return moduleMetadata; return moduleMetadata;
} }
export default function(metadataFn) { export default function(metadataFn: any) {
const metadata = metadataFn(); const metadata = metadataFn();
return { return {

View File

@ -5,12 +5,6 @@
</div> </div>
<script> <script>
export default { export let style = '';
data() { export let innerStyle = '';
return {
style: '',
innerStyle: ''
};
}
};
</script> </script>

View File

@ -1,7 +1,7 @@
import { document } from 'global'; import { document } from 'global';
import styles from './styles'; import styles from './styles';
export default function(storyFn) { export default function(storyFn: () => { template: any; context: any }) {
const { template, context } = storyFn(); const { template, context } = storyFn();
const element = document.createElement('div'); const element = document.createElement('div');
@ -13,7 +13,7 @@ export default function(storyFn) {
element.appendChild(innerElement); element.appendChild(innerElement);
// the inner element should append the parent // the inner element should append the parent
innerElement.appendTo = function appendTo(el) { innerElement.appendTo = function appendTo(el: any) {
el.appendChild(element); el.appendChild(element);
}; };

View File

@ -10,11 +10,11 @@ import { document } from 'global';
* @returns {string} * @returns {string}
* @see https://stackoverflow.com/questions/38533544/jsx-css-to-inline-styles * @see https://stackoverflow.com/questions/38533544/jsx-css-to-inline-styles
*/ */
export default function jsonToCss(jsonStyles) { export default function jsonToCss(jsonStyles: Partial<CSSStyleDeclaration>) {
const frag = document.createElement('div'); const frag = document.createElement('div') as HTMLDivElement;
Object.keys(jsonStyles).forEach(key => { Object.keys(jsonStyles).forEach(key => {
frag.style[key] = jsonStyles[key]; (frag.style as any)[key] = (jsonStyles as any)[key];
}); });
return frag.getAttribute('style'); return frag.getAttribute('style');

View File

@ -4,14 +4,14 @@ import styles from './styles';
const INNER_ID = 'sb-addon-centered-inner'; const INNER_ID = 'sb-addon-centered-inner';
const WRAPPER_ID = 'sb-addon-centered-wrapper'; const WRAPPER_ID = 'sb-addon-centered-wrapper';
function getOrCreate(id, style) { function getOrCreate(id: string, style: Partial<CSSStyleDeclaration>): HTMLDivElement {
const elementOnDom = document.getElementById(id); const elementOnDom = document.getElementById(id);
if (elementOnDom) { if (elementOnDom) {
return elementOnDom; return elementOnDom;
} }
const element = document.createElement('div'); const element = document.createElement('div') as HTMLDivElement;
element.setAttribute('id', id); element.setAttribute('id', id);
Object.assign(element.style, style); Object.assign(element.style, style);
@ -26,7 +26,7 @@ function getWrapperDiv() {
return getOrCreate(WRAPPER_ID, styles.style); return getOrCreate(WRAPPER_ID, styles.style);
} }
export default function(storyFn) { export default function(storyFn: () => any) {
const inner = getInnerDiv(); const inner = getInnerDiv();
const wrapper = getWrapperDiv(); const wrapper = getWrapperDiv();
wrapper.appendChild(inner); wrapper.appendChild(inner);

View File

@ -1,10 +1,8 @@
/** @jsx m */ /** @jsx m */
import m, { ComponentTypes } from 'mithril';
// eslint-disable-next-line import/no-extraneous-dependencies
import m from 'mithril';
import styles from './styles'; import styles from './styles';
export default function(storyFn) { export default function(storyFn: () => ComponentTypes) {
return { return {
view: () => ( view: () => (
<div style={styles.style}> <div style={styles.style}>

View File

@ -1,9 +1,8 @@
/** @jsx h */ /** @jsx h */
// eslint-disable-next-line import/no-extraneous-dependencies import { Component, h } from 'preact';
import { h } from 'preact';
import styles from './styles'; import styles from './styles';
export default function(storyFn) { export default function(storyFn: () => Component) {
return ( return (
<div style={styles.style}> <div style={styles.style}>
<div style={styles.innerStyle}>{storyFn()}</div> <div style={styles.innerStyle}>{storyFn()}</div>

View File

@ -1,8 +1,7 @@
// eslint-disable-next-line import/no-extraneous-dependencies import React, { ReactNode } from 'react';
import React from 'react';
import styles from './styles'; import styles from './styles';
export default function(storyFn) { export default function(storyFn: () => ReactNode) {
return ( return (
<div style={styles.style}> <div style={styles.style}>
<div style={styles.innerStyle}>{storyFn()}</div> <div style={styles.innerStyle}>{storyFn()}</div>

View File

@ -1,10 +1,10 @@
const styles = { const styles = {
style: { style: {
position: 'fixed', position: 'fixed',
top: 0, top: '0',
left: 0, left: '0',
bottom: 0, bottom: '0',
right: 0, right: '0',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
overflow: 'auto', overflow: 'auto',
@ -14,6 +14,6 @@ const styles = {
maxHeight: '100%', // Hack for centering correctly in IE11 maxHeight: '100%', // Hack for centering correctly in IE11
overflow: 'auto', overflow: 'auto',
}, },
}; } as const;
export default styles; export default styles;

View File

@ -18,10 +18,16 @@ const centeredStyles = {
* *
* @see https://svelte.technology/guide#svelte-component * @see https://svelte.technology/guide#svelte-component
*/ */
export default function(storyFn) { export default function(storyFn: () => any) {
const { Component: OriginalComponent, data, on } = storyFn(); const { Component: OriginalComponent, props, on } = storyFn();
return { Component: OriginalComponent, data, on, Wrapper: Centered, WrapperData: centeredStyles }; return {
Component: OriginalComponent,
props,
on,
Wrapper: Centered,
WrapperData: centeredStyles,
};
} }
if (module && module.hot && module.hot.decline) { if (module && module.hot && module.hot.decline) {

2
addons/centered/src/typings.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
declare module 'global';
declare module '*.svelte';

View File

@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"types": ["webpack-env"]
},
"include": [
"src/**/*"
],
"exclude": [
"src/__tests__/**/*"
]
}

View File

@ -31,11 +31,13 @@ once then apply it everywhere**.
use it to bridge with your favorite routing, state-management solutions, or even your own use it to bridge with your favorite routing, state-management solutions, or even your own
[React Context](https://reactjs.org/docs/context.html) provider. [React Context](https://reactjs.org/docs/context.html) provider.
4. Offer chainable and granular configurations. It is even possible to fine-tune at per story level. 4. Offer chainable and granular configurations. It is even possible to fine-tune at per story level.
5. Visual regression friendly. You can use this addon to driving the same story under different contexts to smoke
testing important visual states.
## 🧰 Requirements ## 🧰 Requirements
Make sure the version of your Storybook is above v5. Currently, this addon supports the following frameworks: Make sure the version of your Storybook is above v5. For the full list the current supported framework, see
**React**, and **Vue**. Other frameworks might get support in the near future (PRs are welcome!). [Addon / Framework Support Table](../../ADDONS_SUPPORT.md).
## 🎬 Getting started ## 🎬 Getting started
@ -56,8 +58,8 @@ To load your contextual setups for your stories globally, adding the following l
see it near your `addon.js` file): see it near your `addon.js` file):
```js ```js
import { addDecorator } from '@storybook/react'; // or '@storybook/vue' import { addDecorator } from '@storybook/[framework]';
import { withContexts } from '@storybook/addon-contexts/react'; // or '@storybook/addon-contexts/vue' import { withContexts } from '@storybook/addon-contexts/[framework]';
import { contexts } from './configs/contexts'; // we will define the contextual setups later in API section import { contexts } from './configs/contexts'; // we will define the contextual setups later in API section
addDecorator(withContexts(contexts)); addDecorator(withContexts(contexts));
@ -66,8 +68,8 @@ addDecorator(withContexts(contexts));
Alternatively, just like other addons, you can use this addon only for a given set of stories: Alternatively, just like other addons, you can use this addon only for a given set of stories:
```js ```js
import { storiesOf } from '@storybook/react'; // or '@storybook/vue' import { storiesOf } from '@storybook/[framework]';
import { withContexts } from '@storybook/addon-contexts/react'; // or '@storybook/addon-contexts/vue' import { withContexts } from '@storybook/addon-contexts/[framework]';
import { contexts } from './configs/contexts'; import { contexts } from './configs/contexts';
const story = storiesOf('Component With Contexts', module).addDecorator(withContexts(contexts)); // use this addon with a default contextual environment setups const story = storiesOf('Component With Contexts', module).addDecorator(withContexts(contexts)); // use this addon with a default contextual environment setups
@ -227,6 +229,9 @@ be shown at first in the toolbar menu in your Storybook.
4. The addon will persist the selected params (the addon state) between stories at run-time (similar to other 4. The addon will persist the selected params (the addon state) between stories at run-time (similar to other
addons). If the active param were gone after story switching, it fallback to the default then the first. As a addons). If the active param were gone after story switching, it fallback to the default then the first. As a
rule of thumbs, whenever collisions made possible, always the first wins. rule of thumbs, whenever collisions made possible, always the first wins.
5. Query parameters are supported for pre-selecting contexts param, which comes handy for visual regression testing.
You can do this by appending `&contexts=[name of contexts]=[name of param]` in the URL under iframe mode. Use `,`
to separate multiple contexts (e.g. `&contexts=Theme=Forests,Language=Fr`).
## 📖 License ## 📖 License

View File

@ -1,19 +1,20 @@
{ {
"name": "@storybook/addon-contexts", "name": "@storybook/addon-contexts",
"version": "5.1.0-alpha.33", "version": "5.1.0-rc.0",
"description": "Storybook Addon Contexts", "description": "Storybook Addon Contexts",
"keywords": [ "keywords": [
"storybook", "storybook",
"preact",
"react", "react",
"vue" "vue"
], ],
"author": "Leo Y. Li", "author": "Leo Y. Li",
"license": "MIT", "license": "MIT",
"main": "dist/index.js", "main": "dist/register.js",
"types": "dist/index.d.ts",
"files": [ "files": [
"dist/**/*", "dist/**/*",
"register.js", "register.js",
"preact.js",
"react.js", "react.js",
"vue.js" "vue.js"
], ],
@ -23,15 +24,21 @@
"directory": "addons/contexts" "directory": "addons/contexts"
}, },
"scripts": { "scripts": {
"prepare": "node ../../scripts/prepare.js" "prepare": "node ../../scripts/prepare.js",
"dev:check-types": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@storybook/addons": "5.1.0-alpha.33", "@storybook/addons": "5.1.0-rc.0",
"@storybook/channels": "5.1.0-alpha.33", "@storybook/api": "5.1.0-rc.0",
"@storybook/components": "5.1.0-alpha.33", "@storybook/components": "5.1.0-rc.0",
"@storybook/core-events": "5.1.0-alpha.33" "@storybook/core-events": "5.1.0-rc.0"
},
"peerDependencies": {
"global": "*",
"qs": "*"
}, },
"optionalDependencies": { "optionalDependencies": {
"preact": "*",
"react": "*", "react": "*",
"vue": "*" "vue": "*"
}, },

View File

@ -0,0 +1,4 @@
import { withContexts } from './dist/preview/frameworks/preact';
export { withContexts };
export default withContexts;

View File

@ -1,7 +1,38 @@
import { withContexts } from './preview/frameworks/react'; import { makeDecorator, StoryWrapper } from '@storybook/addons';
export { withContexts }; import { ContextsPreviewAPI } from './preview/ContextsPreviewAPI';
export default withContexts; import { ID, PARAM } from './shared/constants';
import { AddonSetting, AnyFunctionReturns, ContextNode, PropsMap } from './shared/types.d';
console.error( /**
`[addon-contexts] Deprecation warning: "import { withContexts } from 'addon-contexts'" has been deprecated. Please import from 'addon-contexts/react' instead.` * This file serves a idiomatic facade of a Storybook decorator.
); *
* Wrapper function get called whenever the Storybook rerender the view. This reflow logic is
* framework agnostic; on the other hand, the framework specific bindings are the implementation
* details hidden behind the passed `render` function.
*
* Here, we need a dedicated singleton as a state manager for preview (the addon API, in vanilla)
* who is also knowing how to communicate with the Storybook manager (in React) via the Storybook
* event system.
*
* @param {Render} render - framework specific bindings
*/
export type Render<T> = (...args: [ContextNode[], PropsMap, AnyFunctionReturns<T>]) => T;
type CreateAddonDecorator = <T>(render: Render<T>) => (contexts: AddonSetting[]) => unknown;
export const createAddonDecorator: CreateAddonDecorator = render => {
const wrapper: StoryWrapper = (getStory, context, settings: any) => {
const { getContextNodes, getSelectionState, getPropsMap } = ContextsPreviewAPI();
const nodes = getContextNodes(settings);
const state = getSelectionState();
const props = getPropsMap(nodes, state);
return render(nodes, props, () => getStory(context));
};
return makeDecorator({
name: ID,
parameterName: PARAM,
skipIfNoParametersOrOptions: true,
allowDeprecatedUsage: false,
wrapper,
});
};

View File

@ -1,31 +0,0 @@
import React, { useEffect, useState, useCallback } from 'react';
import { useChannel, Channel } from './libs/useChannel';
import { ToolBar } from './ToolBar';
import { REBOOT_MANAGER, UPDATE_MANAGER, UPDATE_PREVIEW } from '../constants';
import { SelectionState, FCNoChildren } from '../types';
/**
* Control addon states and addon-story interactions
*/
type AddonManager = FCNoChildren<{
channel: Channel;
}>;
export const AddonManager: AddonManager = ({ channel }) => {
const [nodes, setNodes] = useState([]);
const [state, setState] = useState<SelectionState>(undefined);
const setSelected = useCallback(
(nodeId, name) => setState((obj = {}) => ({ ...obj, [nodeId]: name })),
[]
);
// from preview
useChannel(UPDATE_MANAGER, newNodes => setNodes(newNodes), []);
useChannel(UPDATE_MANAGER, (_, newState) => newState && setState(newState), []);
// to preview
useEffect(() => channel.emit(REBOOT_MANAGER), []);
useEffect(() => state && channel.emit(UPDATE_PREVIEW, state), [state]);
return <ToolBar nodes={nodes} state={state || {}} setSelected={setSelected} />;
};

View File

@ -0,0 +1,32 @@
import React, { useEffect, useState, useCallback } from 'react';
import { useChannel } from './libs/useChannel';
import { ToolBar } from './components/ToolBar';
import { deserialize, serialize } from '../shared/serializers';
import { PARAM, REBOOT_MANAGER, UPDATE_MANAGER, UPDATE_PREVIEW } from '../shared/constants';
import { FCNoChildren, ManagerAPI } from '../shared/types.d';
/**
* A smart component for handling manager-preview interactions.
*/
type ContextsManager = FCNoChildren<{
api: ManagerAPI;
}>;
export const ContextsManager: ContextsManager = ({ api }) => {
const [nodes, setNodes] = useState([]);
const [state, setState] = useState(deserialize(api.getQueryParam(PARAM)));
const setSelected = useCallback(
(nodeId, name) => setState(obj => ({ ...obj, [nodeId]: name })),
[]
);
// from preview
useChannel(UPDATE_MANAGER, newNodes => setNodes(newNodes), []);
// to preview
useEffect(() => api.emit(REBOOT_MANAGER), []);
useEffect(() => api.emit(UPDATE_PREVIEW, state), [state]);
useEffect(() => api.setQueryParams({ [PARAM]: serialize(state) }), [state]);
return <ToolBar nodes={nodes} state={state || {}} setSelected={setSelected} />;
};

View File

@ -1,7 +1,7 @@
import React, { ComponentProps } from 'react'; import React, { ComponentProps } from 'react';
import { Separator } from '@storybook/components'; import { Separator } from '@storybook/components';
import { ToolbarControl } from './ToolbarControl'; import { ToolbarControl } from './ToolbarControl';
import { ContextNode, FCNoChildren, SelectionState } from '../types'; import { ContextNode, FCNoChildren, SelectionState } from '../../shared/types.d';
type ToolBar = FCNoChildren<{ type ToolBar = FCNoChildren<{
nodes: ContextNode[]; nodes: ContextNode[];
@ -13,16 +13,14 @@ export const ToolBar: ToolBar = React.memo(({ nodes, state, setSelected }) =>
nodes.length ? ( nodes.length ? (
<> <>
<Separator /> <Separator />
{nodes.map(({ components, ...forwardProps }) => {nodes.map(({ components, ...forwardProps }) => (
forwardProps.params.length > 1 ? (
<ToolbarControl <ToolbarControl
{...forwardProps} {...forwardProps}
setSelected={setSelected} setSelected={setSelected}
selected={state[forwardProps.nodeId]} selected={state[forwardProps.nodeId]}
key={forwardProps.nodeId} key={forwardProps.nodeId}
/> />
) : null ))}
)}
</> </>
) : null ) : null
); );

View File

@ -1,10 +1,10 @@
import React, { ComponentProps } from 'react'; import React, { ComponentProps } from 'react';
import { Icons, IconButton, WithTooltip } from '@storybook/components'; import { Icons, IconButton, WithTooltip } from '@storybook/components';
import { ToolBarMenuOptions } from './ToolBarMenuOptions'; import { ToolBarMenuOptions } from './ToolBarMenuOptions';
import { ContextNode, FCNoChildren } from '../types'; import { ContextNode, FCNoChildren } from '../../shared/types.d';
type ToolBarMenu = FCNoChildren<{ type ToolBarMenu = FCNoChildren<{
icon: ContextNode['icon']; icon: ComponentProps<typeof Icons>['icon'];
title: ContextNode['title']; title: ContextNode['title'];
active: boolean; active: boolean;
expanded: boolean; expanded: boolean;
@ -19,8 +19,7 @@ export const ToolBarMenu: ToolBarMenu = ({
expanded, expanded,
setExpanded, setExpanded,
optionsProps, optionsProps,
}) => }) => (
icon ? (
<WithTooltip <WithTooltip
closeOnClick closeOnClick
trigger="click" trigger="click"
@ -33,4 +32,4 @@ export const ToolBarMenu: ToolBarMenu = ({
<Icons icon={icon} /> <Icons icon={icon} />
</IconButton> </IconButton>
</WithTooltip> </WithTooltip>
) : null; );

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { TooltipLinkList } from '@storybook/components'; import { TooltipLinkList } from '@storybook/components';
import { OPT_OUT } from '../constants'; import { OPT_OUT } from '../../shared/constants';
import { FCNoChildren } from '../types'; import { FCNoChildren } from '../../shared/types.d';
type ToolBarMenuOptions = FCNoChildren<{ type ToolBarMenuOptions = FCNoChildren<{
activeName: string; activeName: string;

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { ToolBarMenu } from './ToolBarMenu'; import { ToolBarMenu } from './ToolBarMenu';
import { OPT_OUT } from '../constants'; import { OPT_OUT } from '../../shared/constants';
import { ContextNode, FCNoChildren, Omit } from '../types'; import { ContextNode, FCNoChildren, Omit } from '../../shared/types.d';
type ToolbarControl = FCNoChildren< type ToolbarControl = FCNoChildren<
Omit< Omit<
@ -25,15 +25,14 @@ export const ToolbarControl: ToolbarControl = ({
const [expanded, setExpanded] = React.useState(false); const [expanded, setExpanded] = React.useState(false);
const paramNames = params.map(({ name }) => name); const paramNames = params.map(({ name }) => name);
const activeName = const activeName =
// validate the selected name // validate the integrity of the selected name
(paramNames.concat(OPT_OUT).includes(selected) && selected) || ([...paramNames, options.cancelable && OPT_OUT].includes(selected) && selected) ||
// fallback to default // fallback to default
(params.find(param => !!param.default) || { name: null }).name || (params.find(param => !!param.default) || { name: null }).name ||
// fallback to the first // fallback to the first
params[0].name; params[0].name;
const list = options.cancelable === false ? paramNames : [OPT_OUT, ...paramNames]; const list = options.cancelable ? [OPT_OUT, ...paramNames] : paramNames;
const props = { const props = {
icon,
title, title,
active: activeName !== OPT_OUT, active: activeName !== OPT_OUT,
expanded, expanded,
@ -48,5 +47,5 @@ export const ToolbarControl: ToolbarControl = ({
}, },
}; };
return options.disable || list.length < 2 ? null : <ToolBarMenu {...props} />; return icon && list.length && !options.disable ? <ToolBarMenu icon={icon} {...props} /> : null;
}; };

View File

@ -1,7 +1,6 @@
export { Channel } from '@storybook/channels';
import addons from '@storybook/addons'; import addons from '@storybook/addons';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { AnyFunctionReturns } from '../../types'; import { AnyFunctionReturns } from '../../shared/types.d';
/** /**
* The React hook version of Storybook Channel API. * The React hook version of Storybook Channel API.

View File

@ -0,0 +1,81 @@
import addons from '@storybook/addons';
import { window } from 'global';
import { parse } from 'qs';
import { getContextNodes, getPropsMap, getRendererFrom, singleton } from './libs';
import { deserialize } from '../shared/serializers';
import {
PARAM,
REBOOT_MANAGER,
UPDATE_PREVIEW,
UPDATE_MANAGER,
FORCE_RE_RENDER,
SET_CURRENT_STORY,
} from '../shared/constants';
import { ContextNode, PropsMap, SelectionState } from '../shared/types.d';
/**
* A singleton for handling preview-manager and one-time-only side-effects.
*/
export const ContextsPreviewAPI = singleton(() => {
const channel = addons.getChannel();
let contextsNodesMemo: ContextNode[] | null = null;
let selectionState: SelectionState = {};
/**
* URL query param can be used to predetermine the contexts a story should render,
* which is useful for performing image snapshot testing or URL sharing.
*/
if (window && window.location) {
selectionState = deserialize(parse(window.location.search)[PARAM]) || {};
}
/**
* (Vue specific)
* Vue will inject getter/setter watchers on the first rendering of the addon,
* which is why we have to keep an internal reference and use `Object.assign` to notify the watcher.
*/
const reactivePropsMap = {};
const updateReactiveSystem = (propsMap: PropsMap) => Object.assign(reactivePropsMap, propsMap);
/**
* Preview-manager communications.
*/
// from manager
channel.on(UPDATE_PREVIEW, state => {
if (state) {
selectionState = state;
channel.emit(FORCE_RE_RENDER);
}
});
channel.on(REBOOT_MANAGER, () => {
channel.emit(UPDATE_MANAGER, contextsNodesMemo);
});
channel.on(SET_CURRENT_STORY, () => {
// trash the memorization since the story-level setting may change (diffing it is much expensive)
contextsNodesMemo = null;
});
// to manager
const getContextNodesWithSideEffects: typeof getContextNodes = (...arg) => {
if (contextsNodesMemo === null) {
contextsNodesMemo = getContextNodes(...arg);
channel.emit(UPDATE_MANAGER, contextsNodesMemo);
}
return contextsNodesMemo;
};
/**
* @Public
* Exposed interfaces
*/
return {
// methods get called on Storybook event lifecycle
getContextNodes: getContextNodesWithSideEffects,
getSelectionState: () => selectionState,
getPropsMap,
// methods for processing framework specific bindings
getRendererFrom,
updateReactiveSystem,
};
});

View File

@ -1,52 +0,0 @@
import addons from '@storybook/addons';
import { FORCE_RE_RENDER, SET_CURRENT_STORY } from '@storybook/core-events';
import { REBOOT_MANAGER, UPDATE_PREVIEW, UPDATE_MANAGER } from '../constants';
import { getContextNodes, getPropsMap, getRendererFrom, singleton } from './libs';
import { ContextNode, PropsMap } from '../types';
/**
* @Public
* A singleton for handling wrapper-manager side-effects
*/
export const addonContextsAPI = singleton(() => {
const channel = addons.getChannel();
let memorizedNodes: null | ContextNode[] = null;
let selectionState = {};
// from manager
channel.on(SET_CURRENT_STORY, () => (memorizedNodes = null));
channel.on(REBOOT_MANAGER, () => channel.emit(UPDATE_MANAGER, memorizedNodes, selectionState));
channel.on(UPDATE_PREVIEW, state => (selectionState = Object.freeze(state)));
channel.on(UPDATE_PREVIEW, () => channel.emit(FORCE_RE_RENDER));
// to manager
const getContextNodesWithSideEffects: typeof getContextNodes = (...arg) => {
// we want to notify the manager only when the story changed since `parameter` can be changed
if (memorizedNodes === null) {
memorizedNodes = getContextNodes(...arg);
channel.emit(UPDATE_MANAGER, memorizedNodes);
}
return memorizedNodes;
};
/**
* (Vue specific)
* Vue will inject getter/setters on the first rendering of the addon,
* which is the reason why we have to keep an internal reference and use `Object.assign` to update it.
*/
let reactivePropsMap = {};
const updateReactiveSystem = (propsMap: PropsMap) =>
/* tslint:disable:prefer-object-spread */
Object.assign(reactivePropsMap, propsMap);
return {
// methods get called on Storybook event lifecycle
getContextNodes: getContextNodesWithSideEffects,
getSelectionState: () => selectionState,
getPropsMap,
// methods for processing framework specific bindings
getRendererFrom,
updateReactiveSystem,
};
});

View File

@ -0,0 +1,14 @@
import Preact from 'preact';
import { createAddonDecorator, Render } from '../../index';
import { ContextsPreviewAPI } from '../ContextsPreviewAPI';
/**
* This is the framework specific bindings for Preact.
* '@storybook/preact' expects the returning object from a decorator to be a 'Preact vNode'.
*/
export const renderPreact: Render<Preact.VNode> = (contextNodes, propsMap, getStoryVNode) => {
const { getRendererFrom } = ContextsPreviewAPI();
return getRendererFrom(Preact.h)(contextNodes, propsMap, getStoryVNode);
};
export const withContexts = createAddonDecorator(renderPreact);

View File

@ -1,13 +1,13 @@
import React from 'react'; import React from 'react';
import { createAddonDecorator, Render } from '../index'; import { createAddonDecorator, Render } from '../../index';
import { addonContextsAPI } from '../api'; import { ContextsPreviewAPI } from '../ContextsPreviewAPI';
/** /**
* This is the framework specific bindings for React. * This is the framework specific bindings for React.
* '@storybook/react' expects the returning object from a decorator to be a 'React Element' (vNode). * '@storybook/react' expects the returning object from a decorator to be a 'React Element' (vNode).
*/ */
export const renderReact: Render<React.ReactElement> = (contextNodes, propsMap, getStoryVNode) => { export const renderReact: Render<React.ReactElement> = (contextNodes, propsMap, getStoryVNode) => {
const { getRendererFrom } = addonContextsAPI(); const { getRendererFrom } = ContextsPreviewAPI();
return getRendererFrom(React.createElement)(contextNodes, propsMap, getStoryVNode); return getRendererFrom(React.createElement)(contextNodes, propsMap, getStoryVNode);
}; };

View File

@ -1,22 +1,27 @@
import Vue from 'vue'; import Vue from 'vue';
import { createAddonDecorator, Render } from '../index'; import { createAddonDecorator, Render } from '../../index';
import { addonContextsAPI } from '../api'; import { ContextsPreviewAPI } from '../ContextsPreviewAPI';
import { ID } from '../../constants'; import { ID } from '../../shared/constants';
/** /**
* This is the framework specific bindings for Vue. * This is the framework specific bindings for Vue.
* '@storybook/vue' expects the returning object from a decorator to be a 'VueComponent'. * '@storybook/vue' expects the returning object from a decorator to be a 'VueComponent'.
*/ */
export const renderVue: Render<Vue.Component> = (contextNodes, propsMap, getStoryVNode) => { export const renderVue: Render<Vue.Component> = (contextNodes, propsMap, getStoryComponent) => {
const { getRendererFrom, updateReactiveSystem } = addonContextsAPI(); const { getRendererFrom, updateReactiveSystem } = ContextsPreviewAPI();
const reactiveProps = updateReactiveSystem(propsMap); const reactiveProps = updateReactiveSystem(propsMap);
return Vue.extend({ return Vue.extend({
name: ID, name: ID,
data: () => reactiveProps, data: () => reactiveProps,
render: createElement => render: createElement =>
getRendererFrom((component, props, children) => getRendererFrom((Component, props, children) => {
createElement(component, { props }, [children]) const { key, ref, style, classNames, ...rest } = props || Object();
)(contextNodes, reactiveProps, () => createElement(getStoryVNode())), const contextData =
Component instanceof Object
? { key, ref, style, class: classNames, props: rest } // component as a Vue object
: { key, ref, style, class: classNames, attrs: rest }; // component as a HTML tag string
return createElement(Component, contextData, [children]);
})(contextNodes, reactiveProps, () => createElement(getStoryComponent())),
}); });
}; };

View File

@ -1,38 +0,0 @@
import { makeDecorator, StoryWrapper } from '@storybook/addons';
import { addonContextsAPI } from './api';
import { ID, PARAM } from '../constants';
import { AddonSetting, AnyFunctionReturns, ContextNode, PropsMap } from '../types';
/**
* This file serves a idiomatic facade of a Storybook decorator.
*
* Wrapper function get called whenever the Storybook rerender the view. This reflow logic is
* framework agnostic; on the other hand, the framework specific bindings are the implementation
* details hidden behind the passed `render` function.
*
* Here, we need a dedicated singleton as a state manager for preview (the addon API, in vanilla)
* who is also knowing how to communicate with the Storybook manager (in React) via the Storybook
* event system.
*
* @param {Render} render - framework specific bindings
*/
export type Render<T> = (...args: [ContextNode[], PropsMap, AnyFunctionReturns<T>]) => T;
type CreateAddonDecorator = <T>(render: Render<T>) => (contexts: AddonSetting[]) => T;
export const createAddonDecorator: CreateAddonDecorator = render => {
const wrapper: StoryWrapper = (getStory, context, settings: any) => {
const { getContextNodes, getSelectionState, getPropsMap } = addonContextsAPI();
const nodes = getContextNodes(settings);
const state = getSelectionState();
const props = getPropsMap(nodes, state);
return render(nodes, props, () => getStory(context));
};
return makeDecorator({
name: ID,
parameterName: PARAM,
skipIfNoParametersOrOptions: true,
allowDeprecatedUsage: false,
wrapper,
});
};

View File

@ -12,7 +12,7 @@ describe('Test on functional helpers: memorize', () => {
const resultC = someFnMemo(1); const resultC = someFnMemo(1);
// assertion // assertion
expect(someFn).toBeCalledTimes(2); expect(someFn).toHaveBeenCalledTimes(2);
expect(resultA).toEqual(someFn(1)); expect(resultA).toEqual(someFn(1));
expect(resultA).not.toEqual(resultB); expect(resultA).not.toEqual(resultB);
expect(resultA).toBe(resultC); expect(resultA).toBe(resultC);
@ -30,7 +30,7 @@ describe('Test on functional helpers: memorize', () => {
const resultC = someFnMemo(1, 3); const resultC = someFnMemo(1, 3);
// assertion // assertion
expect(someFn).toBeCalledTimes(2); expect(someFn).toHaveBeenCalledTimes(2);
expect(resultA).toEqual(someFn(1, 2)); expect(resultA).toEqual(someFn(1, 2));
expect(resultA).toBe(resultB); expect(resultA).toBe(resultB);
expect(resultA).not.toEqual(resultC); expect(resultA).not.toEqual(resultC);
@ -49,7 +49,7 @@ describe('Test on functional helpers: singleton', () => {
const resultC = someFnSingleton(7, 8, 9); const resultC = someFnSingleton(7, 8, 9);
// assertion // assertion
expect(someFn).toBeCalledTimes(1); expect(someFn).toHaveBeenCalledTimes(1);
expect(resultA).toEqual(someFn(1, 2, 3)); expect(resultA).toEqual(someFn(1, 2, 3));
expect(resultA).toBe(resultB); expect(resultA).toBe(resultB);
expect(resultA).toBe(resultC); expect(resultA).toBe(resultC);

View File

@ -3,12 +3,12 @@
* the default is to memorize its the first argument; * the default is to memorize its the first argument;
* @return the memorized version of a function. * @return the memorized version of a function.
*/ */
type Memorize = <T, U extends any[]>( type memorize = <T, U extends any[]>(
fn: (...args: U) => T, fn: (...args: U) => T,
resolver?: (...args: U) => unknown resolver?: (...args: U) => unknown
) => (...args: U) => T; ) => (...args: U) => T;
export const memorize: Memorize = (fn, resolver) => { export const memorize: memorize = (fn, resolver) => {
const memo = new Map(); const memo = new Map();
return (...arg) => { return (...arg) => {
const key = resolver ? resolver(...arg) : arg[0]; const key = resolver ? resolver(...arg) : arg[0];
@ -21,6 +21,6 @@ export const memorize: Memorize = (fn, resolver) => {
* the returned value is cached for resolving the subsequent calls. * the returned value is cached for resolving the subsequent calls.
* @return the singleton version of a function. * @return the singleton version of a function.
*/ */
type Singleton = <T, U extends any[]>(fn: (...args: U) => T) => (...args: U) => T; type singleton = <T, U extends any[]>(fn: (...args: U) => T) => (...args: U) => T;
export const singleton: Singleton = fn => memorize(fn, () => 'singleton'); export const singleton: singleton = fn => memorize(fn, () => 'singleton');

View File

@ -1,25 +1,25 @@
import { AddonSetting, ContextNode, WrapperSettings } from '../../types'; /* eslint-disable no-underscore-dangle */
import { AddonSetting, ContextNode, WrapperSettings } from '../../shared/types.d';
/** /**
* @private * @private
* Merge the top-level (global options) and the story-level (parameters) from a pair of setting; * Merge the top-level (global options) and the story-level (parameters) from a pair of setting;
* @return the normalized definition for a contextual environment (i.e. a contextNode). * @return the normalized definition for a contextual environment (i.e. a contextNode).
*/ */
type GetMergedSettings = ( type _getMergedSettings = (
topLevel: Partial<AddonSetting>, topLevel: Partial<AddonSetting>,
storyLevel: Partial<AddonSetting> storyLevel: Partial<AddonSetting>
) => ContextNode; ) => ContextNode;
export const _getMergedSettings: GetMergedSettings = (topLevel, storyLevel) => ({ export const _getMergedSettings: _getMergedSettings = (topLevel, storyLevel) => ({
nodeId: topLevel.title || storyLevel.title || '', // strip out special characters reserved for serializing
nodeId: (topLevel.title || storyLevel.title || '').replace(/[,+]/g, ''),
icon: topLevel.icon || storyLevel.icon || '', icon: topLevel.icon || storyLevel.icon || '',
title: topLevel.title || storyLevel.title || '', title: topLevel.title || storyLevel.title || '',
components: topLevel.components || storyLevel.components || [], components: topLevel.components || storyLevel.components || [],
params: params:
topLevel.params || storyLevel.params topLevel.params || storyLevel.params
? Array() ? [...(topLevel.params || []), ...(storyLevel.params || [])].filter(Boolean)
.concat(topLevel.params, storyLevel.params)
.filter(Boolean)
: [{ name: '', props: {} }], : [{ name: '', props: {} }],
options: { options: {
deep: false, deep: false,
@ -35,11 +35,10 @@ export const _getMergedSettings: GetMergedSettings = (topLevel, storyLevel) => (
* Pair up settings for merging normalizations to produce the contextual definitions (i.e. contextNodes); * Pair up settings for merging normalizations to produce the contextual definitions (i.e. contextNodes);
* it guarantee the adding order can be respected but not duplicated. * it guarantee the adding order can be respected but not duplicated.
*/ */
type GetContextNodes = (settings: WrapperSettings) => ContextNode[]; type getContextNodes = (settings: WrapperSettings) => ContextNode[];
export const getContextNodes: GetContextNodes = ({ options, parameters }) => { export const getContextNodes: getContextNodes = ({ options, parameters }) => {
const titles = Array() const titles = [...(options || []), ...(parameters || [])]
.concat(options, parameters)
.filter(Boolean) .filter(Boolean)
.map(({ title }) => title); .map(({ title }) => title);

Some files were not shown because too many files have changed in this diff Show More