Merge remote-tracking branch 'origin/next' into v5-style-refinements

This commit is contained in:
Tom Coleman 2019-02-13 11:32:36 +11:00
commit 70b7e43020
170 changed files with 2726 additions and 2820 deletions

View File

@ -49,7 +49,6 @@ module.exports = {
'./addons/storysource/src/loader',
'./app/**/src/server/**',
'./app/**/src/bin/**',
'./dangerfile.js',
],
presets: [
[

View File

@ -1,7 +1,11 @@
import { fail, danger } from 'danger';
import { flatten, intersection, isEmpty } from 'lodash';
import { execSync } from 'child_process';
const pkg = require('./package.json'); // eslint-disable-line import/newline-after-import
execSync('npm install lodash');
const { flatten, intersection, isEmpty } = require('lodash');
const pkg = require('../../package.json'); // eslint-disable-line import/newline-after-import
const prLogConfig = pkg['pr-log'];
const Versions = {
@ -20,18 +24,11 @@ const checkRequiredLabels = labels => {
branchVersion === Versions.PATCH ? 'feature request' : [],
]);
const requiredLabels = flatten([
prLogConfig.skipLabels || [],
(prLogConfig.validLabels || []).map(keyVal => keyVal[0]),
]);
const requiredLabels = flatten([prLogConfig.skipLabels || [], (prLogConfig.validLabels || []).map(keyVal => keyVal[0])]);
const blockingLabels = intersection(forbiddenLabels, labels);
if (!isEmpty(blockingLabels)) {
fail(
`PR is marked with ${blockingLabels.map(label => `"${label}"`).join(', ')} label${
blockingLabels.length > 1 ? 's' : ''
}.`
);
fail(`PR is marked with ${blockingLabels.map(label => `"${label}"`).join(', ')} label${blockingLabels.length > 1 ? 's' : ''}.`);
}
const foundLabels = intersection(requiredLabels, labels);

21
.github/main.workflow vendored
View File

@ -1,12 +1,15 @@
workflow "New workflow" {
on = "push"
resolves = ["Hello World"]
workflow "Dangerfile JS Pull" {
on = "pull_request"
resolves = "Danger JS"
}
action "Hello World" {
uses = "./ci/action-a"
env = {
MY_NAME = "Mona"
}
args = "\"Hello world, I'm $MY_NAME!\""
workflow "Dangerfile JS Label" {
on = "label"
resolves = "Danger JS"
}
action "Danger JS" {
uses = "danger/danger-js@master"
secrets = ["GITHUB_TOKEN"]
args = "--dangerfile .ci/danger/dangerfile.ts"
}

View File

@ -21,7 +21,6 @@ object Project : Project({
buildType(OpenSourceProjects_Storybook_Bootstrap)
buildType(OpenSourceProjects_Storybook_CliTestLatestCra)
buildType(OpenSourceProjects_Storybook_Examples)
buildType(OpenSourceProjects_Storybook_Danger)
buildType(OpenSourceProjects_Storybook_NativeSmokeTests)
buildType(OpenSourceProjects_Storybook_Docs)
buildType(OpenSourceProjects_Storybook_Build_2)

View File

@ -1,79 +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
import jetbrains.buildServer.configs.kotlin.v2017_2.triggers.vcs
import jetbrains.buildServer.configs.kotlin.v2017_2.triggers.retryBuild
import jetbrains.buildServer.configs.kotlin.v2017_2.triggers.VcsTrigger
object OpenSourceProjects_Storybook_Danger : BuildType({
uuid = "759f0116-2f7d-4c03-8220-56e4ab03be3a"
id = "OpenSourceProjects_Storybook_Danger"
name = "Danger"
params {
password("env.DANGER_GITHUB_API_TOKEN", "credentialsJSON:9ac87388-d267-4def-a10e-3e596369f644")
param("env.PULL_REQUEST_URL", "https://github.com/storybooks/storybook/%teamcity.build.branch%")
}
vcs {
root(OpenSourceProjects_Storybook.vcsRoots.OpenSourceProjects_Storybook_HttpsGithubComStorybooksStorybookRefsHeadsMaster1)
buildDefaultBranch = false
}
steps {
script {
name = "Danger"
scriptContent = """
#!/bin/sh
set -e -x
yarn
yarn danger ci
""".trimIndent()
dockerImage = "node:%docker.node.version%"
}
}
triggers {
vcs {
quietPeriodMode = VcsTrigger.QuietPeriodMode.USE_DEFAULT
branchFilter = """
+:*
-:master
""".trimIndent()
}
retryBuild {
delaySeconds = 3600
}
}
features {
commitStatusPublisher {
publisher = github {
githubUrl = "https://api.github.com"
authType = personalToken {
token = "credentialsJSON:5ffe2d7e-531e-4f6f-b1fc-a41bfea26eaa"
}
}
param("github_oauth_user", "Hypnosphi")
}
feature {
type = "pullRequests"
param("filterAuthorRole", "EVERYBODY")
param("authenticationType", "token")
param("secure:accessToken", "credentialsJSON:5ffe2d7e-531e-4f6f-b1fc-a41bfea26eaa")
}
}
requirements {
doesNotContain("env.OS", "Windows")
}
cleanup {
artifacts(days = 1)
}
})

View File

@ -1,3 +1,56 @@
## 5.0.0-beta.2 (February 9, 2019)
### Features
* Storyshots: puppeteer launch config for CI ([#5487](https://github.com/storybooks/storybook/pull/5487))
### Bug Fixes
* Addon-notes: fix loading behavior ([#5514](https://github.com/storybooks/storybook/pull/5514))
* CLI: Workaround -h shorthand conflict ([#5464](https://github.com/storybooks/storybook/pull/5464))
* Addons: remove tool addon styling ([#5452](https://github.com/storybooks/storybook/pull/5452))
### Maintenance
* UI: V5 style refinements ([#5444](https://github.com/storybooks/storybook/pull/5444))
* Core: Allow ignoring preview bundle building in core ([#5523](https://github.com/storybooks/storybook/pull/5523))
* Core: Extract client-api pkg from core ([#5521](https://github.com/storybooks/storybook/pull/5521))
* UI: Add react element hoisting && ADD display names via babel-plugin ([#5454](https://github.com/storybooks/storybook/pull/5454))
* Build: TEMP disable the image snapshots ([#5469](https://github.com/storybooks/storybook/pull/5469))
### Dependency Upgrades
* Bump vue and vue-template-compiler ([#5516](https://github.com/storybooks/storybook/pull/5516))
* Bump react from 16.7.0 to 16.8.1 ([#5517](https://github.com/storybooks/storybook/pull/5517))
* Bump react-is from 16.7.0 to 16.8.1 ([#5504](https://github.com/storybooks/storybook/pull/5504))
* Bump eslint-plugin-jest from 22.2.1 to 22.2.2 ([#5505](https://github.com/storybooks/storybook/pull/5505))
* Bump babel-plugin-emotion from 10.0.6 to 10.0.7 ([#5502](https://github.com/storybooks/storybook/pull/5502))
* Bump ember-source from 3.7.2 to 3.7.3 ([#5501](https://github.com/storybooks/storybook/pull/5501))
* Bump lint-staged from 8.1.1 to 8.1.3 ([#5503](https://github.com/storybooks/storybook/pull/5503))
* Bump enzyme-adapter-react-16 from 1.9.0 to 1.9.1 ([#5507](https://github.com/storybooks/storybook/pull/5507))
* dependencies: update lit-html to 1.0.0 ([#5490](https://github.com/storybooks/storybook/pull/5490))
* Bump esm from 3.2.0 to 3.2.1 ([#5492](https://github.com/storybooks/storybook/pull/5492))
* Bump webpack-cli from 3.2.1 to 3.2.3 ([#5494](https://github.com/storybooks/storybook/pull/5494))
* Bump @babel/plugin-proposal-object-rest-spread from 7.3.1 to 7.3.2 ([#5496](https://github.com/storybooks/storybook/pull/5496))
* Bump fuse.js from 3.3.0 to 3.3.1 ([#5497](https://github.com/storybooks/storybook/pull/5497))
* Bump jest-emotion from 10.0.6 to 10.0.7 ([#5495](https://github.com/storybooks/storybook/pull/5495))
* Bump prettier from 1.16.1 to 1.16.4 ([#5482](https://github.com/storybooks/storybook/pull/5482))
* Bump @types/webpack-env from 1.13.6 to 1.13.7 ([#5477](https://github.com/storybooks/storybook/pull/5477))
* Bump typescript from 3.2.4 to 3.3.1 ([#5479](https://github.com/storybooks/storybook/pull/5479))
* Bump eslint-plugin-import from 2.15.0 to 2.16.0 ([#5480](https://github.com/storybooks/storybook/pull/5480))
* Bump storybook-chromatic from 1.2.5 to 1.2.6 ([#5481](https://github.com/storybooks/storybook/pull/5481))
* Bump enzyme-adapter-react-16 from 1.8.0 to 1.9.0 ([#5478](https://github.com/storybooks/storybook/pull/5478))
* Bump markdown-to-jsx from 6.9.0 to 6.9.1 ([#5465](https://github.com/storybooks/storybook/pull/5465))
* Bump @angular/compiler from 7.2.2 to 7.2.3 ([#5466](https://github.com/storybooks/storybook/pull/5466))
* Bump babel-plugin-react-docgen from 2.0.0 to 2.0.2 ([#5468](https://github.com/storybooks/storybook/pull/5468))
* Bump danger from 7.0.4 to 7.0.7 ([#5467](https://github.com/storybooks/storybook/pull/5467))
* Bump puppeteer from 1.11.0 to 1.12.0 ([#5450](https://github.com/storybooks/storybook/pull/5450))
* Bump @angular/platform-browser-dynamic from 7.2.2 to 7.2.3 ([#5434](https://github.com/storybooks/storybook/pull/5434))
* Bump esm from 3.1.4 to 3.2.0 ([#5447](https://github.com/storybooks/storybook/pull/5447))
* Bump eslint-plugin-jest from 22.1.3 to 22.2.1 ([#5448](https://github.com/storybooks/storybook/pull/5448))
* Bump danger from 7.0.2 to 7.0.4 ([#5449](https://github.com/storybooks/storybook/pull/5449))
* Bump case-sensitive-paths-webpack-plugin from 2.1.2 to 2.2.0 ([#5451](https://github.com/storybooks/storybook/pull/5451))
## 5.0.0-beta.1 (February 1, 2019)
### Bug Fixes

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-a11y",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "a11y addon for storybook",
"keywords": [
"a11y",
@ -25,11 +25,11 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/client-logger": "5.0.0-beta.1",
"@storybook/components": "5.0.0-beta.1",
"@storybook/core-events": "5.0.0-beta.1",
"@storybook/theming": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"@storybook/client-logger": "5.0.0-beta.2",
"@storybook/components": "5.0.0-beta.2",
"@storybook/core-events": "5.0.0-beta.2",
"@storybook/theming": "5.0.0-beta.2",
"axe-core": "^3.1.2",
"common-tags": "^1.8.0",
"core-js": "^2.6.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-actions",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Action Logger addon for storybook",
"keywords": [
"storybook"
@ -15,15 +15,15 @@
},
"license": "MIT",
"main": "dist/index.js",
"jsnext:main": "src/index.js",
"types": "dist/index.d.ts",
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/components": "5.0.0-beta.1",
"@storybook/core-events": "5.0.0-beta.1",
"@storybook/theming": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"@storybook/components": "5.0.0-beta.2",
"@storybook/core-events": "5.0.0-beta.2",
"@storybook/theming": "5.0.0-beta.2",
"core-js": "^2.6.2",
"fast-deep-equal": "^2.0.1",
"global": "^4.3.2",
@ -37,5 +37,9 @@
},
"publishConfig": {
"access": "public"
},
"devDependencies": {
"@types/lodash": "^4.14.120",
"@types/uuid": "^3.4.4"
}
}

View File

@ -1,4 +1,3 @@
import PropTypes from 'prop-types';
import React from 'react';
import Inspector from 'react-inspector';
@ -6,11 +5,18 @@ import { withTheme } from '@storybook/theming';
import { ActionBar } from '@storybook/components';
import { Actions, Action, Wrapper, InspectorContainer, Counter } from './style';
import { ActionDisplay } from '../../models';
const ActionLogger = withTheme(({ actions, onClear, theme }) => (
interface ActionLoggerProps {
actions: ActionDisplay[];
onClear: () => void;
theme: any;
}
export const ActionLogger = withTheme(({ actions, onClear, theme }: ActionLoggerProps) => (
<Wrapper>
<Actions>
{actions.map(action => (
{actions.map((action: ActionDisplay) => (
<Action key={action.id}>
{action.count > 1 && <Counter>{action.count}</Counter>}
<InspectorContainer>
@ -29,18 +35,3 @@ const ActionLogger = withTheme(({ actions, onClear, theme }) => (
<ActionBar actionItems={[{ title: 'Clear', onClick: onClear }]} />
</Wrapper>
));
ActionLogger.propTypes = {
onClear: PropTypes.func.isRequired,
actions: PropTypes.arrayOf(
PropTypes.shape({
count: PropTypes.node,
data: PropTypes.shape({
name: PropTypes.node.isRequired,
args: PropTypes.any,
}),
})
).isRequired,
};
export default ActionLogger;

View File

@ -1,14 +1,32 @@
import React from 'react';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import deepEqual from 'fast-deep-equal';
import { STORY_RENDERED } from '@storybook/core-events';
import ActionLoggerComponent from '../../components/ActionLogger';
import { ActionLogger as ActionLoggerComponent } from '../../components/ActionLogger';
import { EVENT_ID } from '../..';
import { ActionDisplay } from '../../models';
export default class ActionLogger extends React.Component {
state = { actions: [] };
interface ActionLoggerProps {
active: boolean;
api: {
on(event: string, callback: (data: any) => void): void;
off(event: string, callback: (data: any) => void): void;
};
}
interface ActionLoggerState {
actions: ActionDisplay[];
}
export default class ActionLogger extends Component<ActionLoggerProps, ActionLoggerState> {
private mounted: boolean;
constructor(props: ActionLoggerProps) {
super(props);
this.state = { actions: [] };
}
componentDidMount() {
this.mounted = true;
@ -33,13 +51,13 @@ export default class ActionLogger extends React.Component {
}
};
addAction = action => {
addAction = (action: ActionDisplay) => {
let { actions = [] } = this.state;
actions = [...actions];
const previous = actions.length && actions[0];
if (previous && deepEqual(previous.data, action.data, { strict: true })) {
if (previous && deepEqual(previous.data, action.data)) {
previous.count++; // eslint-disable-line
} else {
action.count = 1; // eslint-disable-line
@ -62,17 +80,3 @@ export default class ActionLogger extends React.Component {
return active ? <ActionLoggerComponent {...props} /> : null;
}
}
ActionLogger.propTypes = {
active: PropTypes.bool.isRequired,
channel: PropTypes.shape({
emit: PropTypes.func,
on: PropTypes.func,
removeListener: PropTypes.func,
}).isRequired,
api: PropTypes.shape({
on: PropTypes.func,
getQueryParam: PropTypes.func,
setQueryParams: PropTypes.func,
}).isRequired,
};

View File

@ -1,27 +0,0 @@
import {
action,
actions,
decorate,
configureActions,
decorateAction,
withActions,
} from './preview';
// addons, panels and events get unique names using a prefix
import { ADDON_ID, PANEL_ID, EVENT_ID } from './constants';
export {
action,
actions,
decorate,
configureActions,
decorateAction,
withActions,
ADDON_ID,
PANEL_ID,
EVENT_ID,
};
if (module && module.hot && module.hot.decline) {
module.hot.decline();
}

View File

@ -0,0 +1,6 @@
export * from './constants';
export * from './preview';
if (module && module.hot && module.hot.decline) {
module.hot.decline();
}

View File

@ -5,13 +5,9 @@ import { ADDON_ID, PANEL_ID } from '.';
export function register() {
addons.register(ADDON_ID, api => {
const channel = addons.getChannel();
addons.addPanel(PANEL_ID, {
title: 'Actions',
// eslint-disable-next-line react/prop-types
render: ({ active, key }) => (
<ActionLogger key={key} channel={channel} api={api} active={active} />
),
render: ({ active }) => <ActionLogger api={api} active={active} />,
});
});
}

View File

@ -0,0 +1,11 @@
import { ActionOptions } from './ActionOptions';
export interface ActionDisplay {
id: string;
data: {
name: string;
args: any[];
};
count: number;
options: ActionOptions;
}

View File

@ -0,0 +1,5 @@
export interface ActionOptions {
depth?: number;
clearOnStoryChange?: boolean;
limit?: number;
}

View File

@ -0,0 +1,5 @@
import { HandlerFunction } from './HandlerFunction';
export interface ActionsMap {
[key: string]: HandlerFunction;
}

View File

@ -0,0 +1 @@
export type DecoratorFunction = (args: any[]) => any[];

View File

@ -0,0 +1 @@
export type HandlerFunction = (...args: any[]) => void;

View File

@ -0,0 +1,5 @@
export * from './ActionDisplay';
export * from './ActionOptions';
export * from './ActionsMap';
export * from './DecoratorFunction';
export * from './HandlerFunction';

View File

@ -1,22 +0,0 @@
import uuid from 'uuid/v1';
import addons from '@storybook/addons';
import { EVENT_ID } from '../constants';
export default function action(name, options = {}) {
const actionOptions = {
...options,
};
// eslint-disable-next-line no-shadow
const handler = function action(...args) {
const channel = addons.getChannel();
const id = uuid();
channel.emit(EVENT_ID, {
id,
data: { name, args },
options: actionOptions,
});
};
return handler;
}

View File

@ -0,0 +1,25 @@
import uuid from 'uuid/v1';
import { addons } from '@storybook/addons';
import { EVENT_ID } from '../constants';
import { ActionDisplay, ActionOptions, HandlerFunction } from '../models';
export function action(name: string, options: ActionOptions = {}): HandlerFunction {
const actionOptions = {
...options,
};
// tslint:disable-next-line:no-shadowed-variable
const handler = function action(...args: any[]) {
const channel = addons.getChannel();
const id = uuid();
const actionDisplayToEmit: ActionDisplay = {
id,
count: 0,
data: { name, args },
options: actionOptions,
};
channel.emit(EVENT_ID, actionDisplayToEmit);
};
return handler;
}

View File

@ -1,7 +1,8 @@
import action from './action';
import { action } from './action';
import { ActionOptions, ActionsMap } from '../models';
export default function actions(...args) {
let options = {};
export function actions(...args: any[]): ActionsMap {
let options: ActionOptions = {};
const names = args;
// last argument can be options
if (names.length !== 1 && typeof args[args.length - 1] !== 'string') {
@ -16,7 +17,7 @@ export default function actions(...args) {
});
}
const actionsObject = {};
const actionsObject: ActionsMap = {};
Object.keys(namesObject).forEach(name => {
actionsObject[name] = action(namesObject[name], options);
});

View File

@ -1,9 +0,0 @@
export const config = {
depth: 10,
clearOnStoryChange: true,
limit: 50,
};
export function configureActions(options = {}) {
Object.assign(config, options);
}

View File

@ -0,0 +1,11 @@
import { ActionOptions } from '../models';
export const config: ActionOptions = {
depth: 10,
clearOnStoryChange: true,
limit: 50,
};
export const configureActions = (options: ActionOptions = {}): void => {
Object.assign(config, options);
};

View File

@ -1,26 +1,27 @@
import action from './action';
import actions from './actions';
import { action } from './action';
import { actions } from './actions';
import { createDecorator } from './withActions';
import { ActionOptions, DecoratorFunction, HandlerFunction } from '../models';
function applyDecorators(decorators, actionCallback) {
return (..._args) => {
const applyDecorators = (decorators: DecoratorFunction[], actionCallback: HandlerFunction) => {
return (..._args: any[]) => {
const decorated = decorators.reduce((args, fn) => fn(args), _args);
actionCallback(...decorated);
};
}
};
export function decorateAction(decorators) {
return (name, options) => {
export const decorateAction = (decorators: DecoratorFunction[]): ((name: string, options?: ActionOptions) => HandlerFunction) => {
return (name: string, options?: ActionOptions) => {
const callAction = action(name, options);
return applyDecorators(decorators, callAction);
};
}
};
export function decorate(decorators) {
export const decorate = (decorators: DecoratorFunction[]) => {
const decorated = decorateAction(decorators);
const decoratedActions = (...args) => {
const decoratedActions = (...args: any[]) => {
const rawActions = actions(...args);
const actionsObject = {};
const actionsObject = {} as any;
Object.keys(rawActions).forEach(name => {
actionsObject[name] = applyDecorators(decorators, rawActions[name]);
});
@ -31,4 +32,4 @@ export function decorate(decorators) {
actions: decoratedActions,
withActions: createDecorator(decoratedActions),
};
}
};

View File

@ -1,5 +0,0 @@
export { default as action } from './action';
export { default as actions } from './actions';
export { configureActions } from './configureActions';
export { decorateAction, decorate } from './decorateAction';
export { default as withActions } from './withActions';

View File

@ -0,0 +1,5 @@
export * from './action';
export * from './actions';
export * from './configureActions';
export * from './decorateAction';
export * from './withActions';

View File

@ -1,13 +1,13 @@
// Based on http://backbonejs.org/docs/backbone.html#section-164
import { document, Element } from 'global';
import isEqual from 'lodash/isEqual';
import addons from '@storybook/addons';
import { isEqual } from 'lodash';
import { addons } from '@storybook/addons';
import Events from '@storybook/core-events';
import actions from './actions';
import { actions } from './actions';
let lastSubscription;
let lastArgs;
let lastSubscription: () => () => void;
let lastArgs: any[];
const delegateEventSplitter = /^(\S+)\s*(.*)$/;
@ -16,7 +16,7 @@ const matchesMethod = isIE ? 'msMatchesSelector' : 'matches';
const root = document && document.getElementById('root');
const hasMatchInAncestry = (element, selector) => {
const hasMatchInAncestry = (element: any, selector: any): boolean => {
if (element[matchesMethod](selector)) {
return true;
}
@ -27,14 +27,14 @@ const hasMatchInAncestry = (element, selector) => {
return hasMatchInAncestry(parent, selector);
};
const createHandlers = (actionsFn, ...args) => {
const createHandlers = (actionsFn: (...arg: any[]) => object, ...args: any[]) => {
const actionsObject = actionsFn(...args);
return Object.entries(actionsObject).map(([key, action]) => {
// eslint-disable-next-line no-unused-vars
const [_, eventName, selector] = key.match(delegateEventSplitter);
return {
eventName,
handler: e => {
handler: (e: { target: any }) => {
if (!selector || hasMatchInAncestry(e.target, selector)) {
action(e);
}
@ -43,24 +43,24 @@ const createHandlers = (actionsFn, ...args) => {
});
};
const actionsSubscription = (...args) => {
const actionsSubscription = (...args: any[]) => {
if (!isEqual(args, lastArgs)) {
lastArgs = args;
// @ts-ignore
const handlers = createHandlers(...args);
lastSubscription = () => {
handlers.forEach(({ eventName, handler }) => root.addEventListener(eventName, handler));
return () =>
handlers.forEach(({ eventName, handler }) => root.removeEventListener(eventName, handler));
return () => handlers.forEach(({ eventName, handler }) => root.removeEventListener(eventName, handler));
};
}
return lastSubscription;
};
export const createDecorator = actionsFn => (...args) => story => {
export const createDecorator = (actionsFn: any) => (...args: any[]) => (story: () => any) => {
if (root != null) {
addons.getChannel().emit(Events.REGISTER_SUBSCRIPTION, actionsSubscription(actionsFn, ...args));
}
return story();
};
export default createDecorator(actions);
export const withActions = createDecorator(actions);

4
addons/actions/src/typings.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
// TODO: following packages need definition files or a TS migration
declare module '@storybook/components';
declare module 'global';
declare module 'react-inspector';

View File

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

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-backgrounds",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "A storybook addon to show different backgrounds for your preview",
"keywords": [
"addon",
@ -19,24 +19,25 @@
"license": "MIT",
"author": "jbaxleyiii",
"main": "dist/index.js",
"jsnext:main": "src/index.js",
"types": "dist/index.d.ts",
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/client-logger": "5.0.0-beta.1",
"@storybook/components": "5.0.0-beta.1",
"@storybook/core-events": "5.0.0-beta.1",
"@storybook/theming": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"@storybook/client-logger": "5.0.0-beta.2",
"@storybook/components": "5.0.0-beta.2",
"@storybook/core-events": "5.0.0-beta.2",
"@storybook/theming": "5.0.0-beta.2",
"core-js": "^2.6.2",
"eventemitter3": "^3.1.0",
"global": "^4.3.2",
"memoizerific": "^1.11.3",
"prop-types": "^15.6.2",
"react": "^16.8.1",
"util-deprecate": "^1.0.2"
},
"devDependencies": {
"@types/util-deprecate": "^1.0.0"
},
"publishConfig": {
"access": "public"
}

View File

@ -1,149 +0,0 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import memoize from 'memoizerific';
import { Global, styled } from '@storybook/theming';
import { SET_STORIES } from '@storybook/core-events';
import { Icons, IconButton, WithTooltip, TooltipLinkList } from '@storybook/components';
import { PARAM_KEY } from './constants';
export const ColorIcon = styled.span(({ background }) => ({
borderRadius: '1rem',
display: 'block',
height: '1rem',
width: '1rem',
background,
}));
const iframeId = 'storybook-preview-background';
const createItem = memoize(1000)((id, name, value, hasSwatch, change) =>
hasSwatch
? {
id: id || name,
title: name,
onClick: () => {
change({ selected: value, expanded: false });
},
value,
right: <ColorIcon background={value} />,
}
: {
id: id || name,
title: name,
onClick: () => {
change({ selected: value, expanded: false });
},
value,
}
);
const getSelected = (list, state) => {
if (!list.length) {
return 'transparent';
}
if (state === 'transparent') {
return state;
}
if (list.find(i => i.value === state)) {
return state;
}
if (list.find(i => i.default)) {
return list.find(i => i.default).value;
}
return 'transparent';
};
const getState = memoize(10)((props, state, change) => {
const data = props.api.getCurrentStoryData();
const list = (data && data.parameters && data.parameters[PARAM_KEY]) || [];
const selected = getSelected(list, state.selected);
const initial =
selected !== 'transparent'
? [createItem('reset', 'Clear background', 'transparent', false, change)]
: [];
const items = list.length
? initial.concat(list.map(({ id, name, value }) => createItem(id, name, value, true, change)))
: list;
return {
items,
selected,
};
});
export default class BackgroundTool extends Component {
static propTypes = {
api: PropTypes.shape({
getQueryParam: PropTypes.func,
setQueryParams: PropTypes.func,
}).isRequired,
};
constructor(props) {
super(props);
this.state = {
items: [],
selected: undefined,
expanded: false,
};
this.listener = () => {
this.setState({ selected: null });
};
}
componentDidMount() {
const { api } = this.props;
api.on(SET_STORIES, this.listener);
}
componentWillUnmount() {
const { api } = this.props;
api.off(SET_STORIES, this.listener);
}
change = (...args) => this.setState(...args);
render() {
const { expanded } = this.state;
const { items, selected } = getState(this.props, this.state, this.change);
return items.length ? (
<Fragment>
{selected ? (
<Global
styles={{
[`#${iframeId}`]: {
background: selected,
},
}}
/>
) : null}
<WithTooltip
placement="top"
trigger="click"
tooltipShown={expanded}
onVisibilityChange={s => this.setState({ expanded: s })}
tooltip={<TooltipLinkList links={items} />}
closeOnClick
>
<IconButton key="background" title="Backgrounds">
<Icons icon="photo" />
</IconButton>
</WithTooltip>
</Fragment>
) : null;
}
}

View File

@ -1,5 +0,0 @@
import { styled } from '@storybook/theming';
export const ColorIcon = styled.span(({ background }) => ({
background,
}));

View File

@ -0,0 +1,9 @@
import { styled } from '@storybook/theming';
export const ColorIcon = styled.span(({ background }: { background: string }) => ({
borderRadius: '1rem',
display: 'block',
height: '1rem',
width: '1rem',
background,
}));

View File

@ -0,0 +1 @@
export * from './ColorIcon';

View File

@ -1,7 +1,7 @@
export const ADDON_ID = 'storybook/background';
export const PARAM_KEY = 'backgrounds';
export default {
export const EVENTS = {
SET: `${ADDON_ID}:set`,
UNSET: `${ADDON_ID}:unset`,
};

View File

@ -0,0 +1,165 @@
import React, { Component, Fragment } from 'react';
import memoize from 'memoizerific';
import { Global } from '@storybook/theming';
import { SET_STORIES } from '@storybook/core-events';
import { Icons, IconButton, WithTooltip, TooltipLinkList } from '@storybook/components';
import { PARAM_KEY } from '../constants';
import { ColorIcon } from '../components';
import { BackgroundConfig, BackgroundSelectorItem } from '../models';
const iframeId = 'storybook-preview-background';
const createBackgroundSelectorItem = memoize(1000)(
(
id: string,
name: string,
value: string,
hasSwatch: boolean,
change: (arg: { selected: string; expanded: boolean }) => void
): BackgroundSelectorItem => ({
id: id || name,
title: name,
onClick: () => {
change({ selected: value, expanded: false });
},
value,
right: hasSwatch ? <ColorIcon background={value} /> : undefined,
})
);
const getSelectedBackgroundColor = (
list: BackgroundConfig[],
currentSelectedValue: string
): string => {
if (!list.length) {
return 'transparent';
}
if (currentSelectedValue === 'transparent') {
return currentSelectedValue;
}
if (list.find(i => i.value === currentSelectedValue)) {
return currentSelectedValue;
}
if (list.find(i => i.default)) {
return list.find(i => i.default).value;
}
return 'transparent';
};
const getDisplayableState = memoize(10)(
(props: BackgroundToolProps, state: BackgroundToolState, change) => {
const data = props.api.getCurrentStoryData();
const list: BackgroundConfig[] = (data && data.parameters && data.parameters[PARAM_KEY]) || [];
const selectedBackgroundColor = getSelectedBackgroundColor(list, state.selected);
let availableBackgroundSelectorItems: BackgroundSelectorItem[] = [];
if (selectedBackgroundColor !== 'transparent') {
availableBackgroundSelectorItems.push(
createBackgroundSelectorItem('reset', 'Clear background', 'transparent', false, change)
);
}
if (list.length) {
availableBackgroundSelectorItems = [
...availableBackgroundSelectorItems,
...list.map(({ name, value }) =>
createBackgroundSelectorItem(null, name, value, true, change)
),
];
}
return {
items: availableBackgroundSelectorItems,
selectedBackgroundColor,
};
}
);
interface BackgroundToolProps {
api: {
on(event: string, callback: (data: any) => void): void;
off(event: string, callback: (data: any) => void): void;
getCurrentStoryData(): any;
};
}
interface BackgroundToolState {
items: BackgroundSelectorItem[];
selected: string;
expanded: boolean;
}
export class BackgroundSelector extends Component<BackgroundToolProps, BackgroundToolState> {
private listener = () => {
this.setState({ selected: null });
};
constructor(props: BackgroundToolProps) {
super(props);
this.state = {
items: [],
selected: null,
expanded: false,
};
}
componentDidMount() {
const { api } = this.props;
api.on(SET_STORIES, this.listener);
}
componentWillUnmount() {
const { api } = this.props;
api.off(SET_STORIES, this.listener);
}
change = (args: { selected: string; expanded: boolean }) => this.setState(args);
render() {
const { expanded } = this.state;
const { items, selectedBackgroundColor } = getDisplayableState(
this.props,
this.state,
this.change
);
return items.length ? (
<Fragment>
{selectedBackgroundColor ? (
<Global
styles={{
[`#${iframeId}`]: {
background: selectedBackgroundColor,
},
}}
/>
) : null}
<WithTooltip
placement="top"
trigger="click"
tooltipShown={expanded}
onVisibilityChange={(newVisibility: boolean) =>
this.setState({ expanded: newVisibility })
}
tooltip={<TooltipLinkList links={items} />}
closeOnClick
>
<IconButton key="background" title="Backgrounds">
<Icons icon="photo" />
</IconButton>
</WithTooltip>
</Fragment>
) : null;
}
}

View File

@ -0,0 +1 @@
export * from './BackgroundSelector';

View File

@ -1,14 +1,15 @@
import addons, { makeDecorator } from '@storybook/addons';
import CoreEvents from '@storybook/core-events';
import { addons, makeDecorator } from '@storybook/addons';
import deprecate from 'util-deprecate';
import Events from './constants';
import { REGISTER_SUBSCRIPTION } from '@storybook/core-events';
import { EVENTS } from './constants';
import { BackgroundConfig } from './models';
let prevBackgrounds;
let prevBackgrounds: BackgroundConfig[];
const subscription = () => () => {
prevBackgrounds = null;
addons.getChannel().emit(Events.UNSET);
addons.getChannel().emit(EVENTS.UNSET);
};
export const withBackgrounds = makeDecorator({
@ -25,10 +26,10 @@ export const withBackgrounds = makeDecorator({
}
if (prevBackgrounds !== backgrounds) {
addons.getChannel().emit(Events.SET, backgrounds);
addons.getChannel().emit(EVENTS.SET, backgrounds);
prevBackgrounds = backgrounds;
}
addons.getChannel().emit(CoreEvents.REGISTER_SUBSCRIPTION, subscription);
addons.getChannel().emit(REGISTER_SUBSCRIPTION, subscription);
return getStory(context);
},

View File

@ -0,0 +1,5 @@
export interface BackgroundConfig {
name: string;
value: string;
default?: boolean;
}

View File

@ -0,0 +1,7 @@
export interface BackgroundSelectorItem {
id: string;
title: string;
onClick: () => void;
value: string;
right?: any;
}

View File

@ -0,0 +1,2 @@
export * from './BackgroundConfig';
export * from './BackgroundSelectorItem';

View File

@ -1,13 +1,14 @@
import React from 'react';
import addons, { types } from '@storybook/addons';
import { addons, types } from '@storybook/addons';
import { ADDON_ID } from './constants';
import Tool from './Tool';
import { BackgroundSelector } from './containers';
addons.register(ADDON_ID, api => {
addons.add(ADDON_ID, {
title: 'Backgrounds',
type: types.TOOL,
match: ({ viewMode }) => viewMode === 'story',
render: () => <Tool api={api} />,
render: () => <BackgroundSelector api={api} />,
});
});

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

@ -0,0 +1,2 @@
// TODO: following packages need definition files or a TS migration
declare module '@storybook/components';

View File

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

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-centered",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Storybook decorator to center components",
"keywords": [
"addon",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-cssresources",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "A storybook addon to switch between css resources at runtime for your story",
"keywords": [
"addon",
@ -25,10 +25,10 @@
},
"dependencies": {
"@emotion/styled": "10.0.6",
"@storybook/addons": "5.0.0-beta.1",
"@storybook/channels": "5.0.0-beta.1",
"@storybook/components": "5.0.0-beta.1",
"@storybook/core-events": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"@storybook/channels": "5.0.0-beta.2",
"@storybook/components": "5.0.0-beta.2",
"@storybook/core-events": "5.0.0-beta.2",
"core-js": "^2.6.2",
"global": "^4.3.2",
"prop-types": "^15.6.2",

View File

@ -67,7 +67,7 @@ export const withCssResources = makeDecorator({
skipIfNoParametersOrOptions: true,
allowDeprecatedUsage: false,
wrapper: (getStory: any, context: any, { options, parameters }: any) => {
wrapper: (getStory, context, { options, parameters }) => {
const storyOptions = parameters || options;
addons.getChannel().on(EVENTS.SET, setResources);

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-events",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Add events to your Storybook stories.",
"keywords": [
"addon",
@ -23,9 +23,9 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/core-events": "5.0.0-beta.1",
"@storybook/theming": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"@storybook/core-events": "5.0.0-beta.2",
"@storybook/theming": "5.0.0-beta.2",
"core-js": "^2.6.2",
"format-json": "^1.0.3",
"prop-types": "^15.6.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-google-analytics",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Storybook addon for google analytics",
"keywords": [
"addon",
@ -19,8 +19,8 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/core-events": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"@storybook/core-events": "5.0.0-beta.2",
"core-js": "^2.6.2",
"global": "^4.3.2",
"react-ga": "^2.5.3"

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-graphql",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Storybook addon to display the GraphiQL IDE",
"keywords": [
"addon",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-info",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "A Storybook addon to show additional information for your stories.",
"keywords": [
"addon",
@ -21,10 +21,10 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/client-logger": "5.0.0-beta.1",
"@storybook/components": "5.0.0-beta.1",
"@storybook/theming": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"@storybook/client-logger": "5.0.0-beta.2",
"@storybook/components": "5.0.0-beta.2",
"@storybook/theming": "5.0.0-beta.2",
"core-js": "^2.6.2",
"global": "^4.3.2",
"marksy": "^6.1.0",

View File

@ -0,0 +1,86 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PropTable multiLineText should exclude excluded propTypes 1`] = `
<small>
No propTypes defined!
</small>
`;
exports[`PropTable multiLineText should have 2 br tags for 3 lines of text 1`] = `
Array [
<span>
foo
</span>,
<span>
<br />
bar
</span>,
<span>
<br />
baz
</span>,
]
`;
exports[`PropTable multiLineText should include all propTypes by default 1`] = `
<Table>
<Thead>
<Tr>
<Th>
property
</Th>
<Th>
propType
</Th>
<Th>
required
</Th>
<Th>
default
</Th>
<Th>
description
</Th>
</Tr>
</Thead>
<Tbody>
<Tr
key="foo"
>
<Td
isMonospace={true}
>
foo
</Td>
<Td
isMonospace={true}
>
<PrettyPropType
depth={1}
propType={
Object {
"name": "string",
}
}
/>
</Td>
<Td
isMonospace={false}
>
-
</Td>
<Td
isMonospace={false}
>
-
</Td>
<Td
isMonospace={false}
/>
</Tr>
</Tbody>
</Table>
`;

View File

@ -0,0 +1,12 @@
import PropTypes from 'prop-types';
import React from 'react';
import '../style.css';
const Table = ({ children }) => <table className="info-table">{children}</table>;
Table.propTypes = {
children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)])
.isRequired,
};
export default Table;

View File

@ -0,0 +1,25 @@
import React from 'react';
import { shallow } from 'enzyme';
import Table from './Table';
describe('PropTable/Table', () => {
it('renders a table html node with one child element', () => {
const wrapper = shallow(
<Table>
<div>foo bar</div>
</Table>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a table html node with multiple children elements', () => {
const wrapper = shallow(
<Table>
<div>foo bar</div>
<div>baz</div>
</Table>
);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -0,0 +1,11 @@
import PropTypes from 'prop-types';
import React from 'react';
const Tbody = ({ children }) => <tbody>{children}</tbody>;
Tbody.propTypes = {
children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)])
.isRequired,
};
export default Tbody;

View File

@ -0,0 +1,25 @@
import React from 'react';
import { shallow } from 'enzyme';
import Tbody from './Tbody';
describe('PropTable/Tbody', () => {
it('renders a tbody html node with children', () => {
const wrapper = shallow(
<Tbody>
<div>foo bar</div>
</Tbody>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a tbody html node with multiple children elements', () => {
const wrapper = shallow(
<Tbody>
<div>foo bar</div>
<div>baz</div>
</Tbody>
);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -0,0 +1,23 @@
import PropTypes from 'prop-types';
import React from 'react';
import '../style.css';
const Td = ({ isMonospace, children }) => (
<td className={isMonospace ? 'info-table-monospace' : null}>{children}</td>
);
Td.propTypes = {
children: PropTypes.oneOfType([
PropTypes.node,
PropTypes.element,
PropTypes.arrayOf(PropTypes.node),
PropTypes.arrayOf(PropTypes.element),
]).isRequired,
isMonospace: PropTypes.bool,
};
Td.defaultProps = {
isMonospace: false,
};
export default Td;

View File

@ -0,0 +1,54 @@
import React from 'react';
import { shallow } from 'enzyme';
import Td from './Td';
describe('PropTable/Td', () => {
it('renders a td html node child element', () => {
const wrapper = shallow(
<Td>
<div>foo bar</div>
</Td>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a monospace td html node child element', () => {
const wrapper = shallow(
<Td isMonospace>
<div>foo bar</div>
</Td>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a td html node with multiple children elements', () => {
const wrapper = shallow(
<Td>
<div>foo bar</div>
<div>baz</div>
</Td>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a monospace td html node with multiple children elements', () => {
const wrapper = shallow(
<Td isMonospace>
<div>foo bar</div>
<div>baz</div>
</Td>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a td html node with one child node', () => {
const wrapper = shallow(<Td>foo bar</Td>);
expect(wrapper).toMatchSnapshot();
});
it('renders a monospace td html node with one child node', () => {
const wrapper = shallow(<Td isMonospace>foo bar</Td>);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -0,0 +1,15 @@
import PropTypes from 'prop-types';
import React from 'react';
const Th = ({ children }) => <th>{children}</th>;
Th.propTypes = {
children: PropTypes.oneOfType([
PropTypes.node,
PropTypes.element,
PropTypes.arrayOf(PropTypes.node),
PropTypes.arrayOf(PropTypes.element),
]).isRequired,
};
export default Th;

View File

@ -0,0 +1,26 @@
import React from 'react';
import { shallow } from 'enzyme';
import Th from './Th';
describe('PropTable/Th', () => {
it('renders a th html node with react element children', () => {
const wrapper = shallow(
<Th>
<div>foo bar</div>
<div>baz</div>
</Th>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a th html node with html node children', () => {
const wrapper = shallow(<Th>foo bar baz</Th>);
expect(wrapper).toMatchSnapshot();
});
it('renders a th html node with one child node', () => {
const wrapper = shallow(<Th>foo bar</Th>);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -0,0 +1,11 @@
import PropTypes from 'prop-types';
import React from 'react';
const Thead = ({ children }) => <thead>{children}</thead>;
Thead.propTypes = {
children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)])
.isRequired,
};
export default Thead;

View File

@ -0,0 +1,25 @@
import React from 'react';
import { shallow } from 'enzyme';
import Thead from './Thead';
describe('PropTable/Thead', () => {
it('renders a thead html node with children', () => {
const wrapper = shallow(
<Thead>
<div>foo bar</div>
</Thead>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a thead html node with multiple children elements', () => {
const wrapper = shallow(
<Thead>
<div>foo bar</div>
<div>baz</div>
</Thead>
);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -0,0 +1,10 @@
import PropTypes from 'prop-types';
import React from 'react';
const Tr = ({ children }) => <tr>{children}</tr>;
Tr.propTypes = {
children: PropTypes.node.isRequired,
};
export default Tr;

View File

@ -0,0 +1,27 @@
import React from 'react';
import { shallow } from 'enzyme';
import Tr from './Tr';
import Td from './Td';
describe('PropTable/Tr', () => {
it('renders a tr html node with react element children', () => {
const wrapper = shallow(
<Tr>
<Td>foo bar</Td>
<Td>baz</Td>
</Tr>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a tr html node with html node children', () => {
const wrapper = shallow(<Tr>foo bar baz</Tr>);
expect(wrapper).toMatchSnapshot();
});
it('renders a tr html node with one child node', () => {
const wrapper = shallow(<Tr>foo bar</Tr>);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -0,0 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PropTable/Table renders a table html node with multiple children elements 1`] = `
<table
className="info-table"
>
<div>
foo bar
</div>
<div>
baz
</div>
</table>
`;
exports[`PropTable/Table renders a table html node with one child element 1`] = `
<table
className="info-table"
>
<div>
foo bar
</div>
</table>
`;

View File

@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PropTable/Tbody renders a tbody html node with children 1`] = `
<tbody>
<div>
foo bar
</div>
</tbody>
`;
exports[`PropTable/Tbody renders a tbody html node with multiple children elements 1`] = `
<tbody>
<div>
foo bar
</div>
<div>
baz
</div>
</tbody>
`;

View File

@ -0,0 +1,63 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PropTable/Td renders a monospace td html node child element 1`] = `
<td
className="info-table-monospace"
>
<div>
foo bar
</div>
</td>
`;
exports[`PropTable/Td renders a monospace td html node with multiple children elements 1`] = `
<td
className="info-table-monospace"
>
<div>
foo bar
</div>
<div>
baz
</div>
</td>
`;
exports[`PropTable/Td renders a monospace td html node with one child node 1`] = `
<td
className="info-table-monospace"
>
foo bar
</td>
`;
exports[`PropTable/Td renders a td html node child element 1`] = `
<td
className={null}
>
<div>
foo bar
</div>
</td>
`;
exports[`PropTable/Td renders a td html node with multiple children elements 1`] = `
<td
className={null}
>
<div>
foo bar
</div>
<div>
baz
</div>
</td>
`;
exports[`PropTable/Td renders a td html node with one child node 1`] = `
<td
className={null}
>
foo bar
</td>
`;

View File

@ -0,0 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PropTable/Th renders a th html node with html node children 1`] = `
<th>
foo bar baz
</th>
`;
exports[`PropTable/Th renders a th html node with one child node 1`] = `
<th>
foo bar
</th>
`;
exports[`PropTable/Th renders a th html node with react element children 1`] = `
<th>
<div>
foo bar
</div>
<div>
baz
</div>
</th>
`;

View File

@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PropTable/Thead renders a thead html node with children 1`] = `
<thead>
<div>
foo bar
</div>
</thead>
`;
exports[`PropTable/Thead renders a thead html node with multiple children elements 1`] = `
<thead>
<div>
foo bar
</div>
<div>
baz
</div>
</thead>
`;

View File

@ -0,0 +1,28 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PropTable/Tr renders a tr html node with html node children 1`] = `
<tr>
foo bar baz
</tr>
`;
exports[`PropTable/Tr renders a tr html node with one child node 1`] = `
<tr>
foo bar
</tr>
`;
exports[`PropTable/Tr renders a tr html node with react element children 1`] = `
<tr>
<Td
isMonospace={false}
>
foo bar
</Td>
<Td
isMonospace={false}
>
baz
</Td>
</tr>
`;

View File

@ -0,0 +1,121 @@
import PropTypes from 'prop-types';
import React from 'react';
import PrettyPropType from '../types/PrettyPropType';
import PropVal from '../PropVal';
import Table from './components/Table';
import Tbody from './components/Tbody';
import Td from './components/Td';
import Th from './components/Th';
import Thead from './components/Thead';
import Tr from './components/Tr';
export const multiLineText = input => {
if (!input) {
return input;
}
const text = String(input);
const arrayOfText = text.split(/\r?\n|\r/g);
const isSingleLine = arrayOfText.length < 2;
return isSingleLine
? text
: arrayOfText.map((lineOfText, i) => (
// eslint-disable-next-line react/no-array-index-key
<span key={`${lineOfText}.${i}`}>
{i > 0 && <br />} {lineOfText}
</span>
));
};
const determineIncludedPropTypes = (propDefinitions, excludedPropTypes) => {
if (excludedPropTypes.length === 0) {
return propDefinitions;
}
return propDefinitions.filter(
propDefinition => !excludedPropTypes.includes(propDefinition.property)
);
};
export default function PropTable(props) {
const {
type,
maxPropObjectKeys,
maxPropArrayLength,
maxPropStringLength,
propDefinitions,
excludedPropTypes,
} = props;
if (!type) {
return null;
}
const includedPropDefinitions = determineIncludedPropTypes(propDefinitions, excludedPropTypes);
if (!includedPropDefinitions.length) {
return <small>No propTypes defined!</small>;
}
const propValProps = {
maxPropObjectKeys,
maxPropArrayLength,
maxPropStringLength,
};
return (
<Table>
<Thead>
<Tr>
<Th>property</Th>
<Th>propType</Th>
<Th>required</Th>
<Th>default</Th>
<Th>description</Th>
</Tr>
</Thead>
<Tbody>
{includedPropDefinitions.map(row => (
<Tr key={row.property}>
<Td isMonospace>{row.property}</Td>
<Td isMonospace>
<PrettyPropType propType={row.propType} />
</Td>
<Td>{row.required ? 'yes' : '-'}</Td>
<Td>
{row.defaultValue === undefined ? (
'-'
) : (
<PropVal val={row.defaultValue} {...propValProps} valueStyles={{}} />
)}
</Td>
<Td>{multiLineText(row.description)}</Td>
</Tr>
))}
</Tbody>
</Table>
);
}
PropTable.displayName = 'PropTable';
PropTable.defaultProps = {
type: null,
propDefinitions: [],
excludedPropTypes: [],
};
PropTable.propTypes = {
type: PropTypes.func,
maxPropObjectKeys: PropTypes.number.isRequired,
maxPropArrayLength: PropTypes.number.isRequired,
maxPropStringLength: PropTypes.number.isRequired,
excludedPropTypes: PropTypes.arrayOf(PropTypes.string),
propDefinitions: PropTypes.arrayOf(
PropTypes.shape({
property: PropTypes.string.isRequired,
propType: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
required: PropTypes.bool,
description: PropTypes.string,
defaultValue: PropTypes.any,
})
),
};

View File

@ -0,0 +1,78 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { shallow } from 'enzyme';
import PropTable, { multiLineText } from './index';
describe('PropTable', () => {
describe('multiLineText', () => {
const singleLine = 'Foo bar baz';
const unixMultiLineText = 'foo \n bar \n baz';
const windowsMultiLineText = 'foo \r bar \r baz';
const duplicatedMultiLine = 'foo\nfoo\nfoo';
const propDefinitions = [
{
defaultValue: undefined,
description: '',
propType: { name: 'string' },
property: 'foo',
required: false,
},
];
const FooComponent = () => <div />;
const propTableProps = {
type: FooComponent,
maxPropArrayLength: 5,
maxPropObjectKeys: 5,
maxPropStringLength: 5,
propDefinitions,
};
it('should include all propTypes by default', () => {
const wrapper = shallow(<PropTable {...propTableProps} />);
expect(wrapper).toMatchSnapshot();
});
it('should exclude excluded propTypes', () => {
const props = { ...propTableProps, excludedPropTypes: ['foo'] };
const wrapper = shallow(<PropTable {...props} />);
expect(wrapper).toMatchSnapshot();
});
it('should return a blank string for a null input', () => {
expect(multiLineText(null)).toBe(null);
});
it('should return a blank string for an undefined input', () => {
expect(multiLineText(undefined)).toBe(undefined);
});
it('should cast a number to a string', () => {
expect(multiLineText(1)).toBe('1');
});
it('should return its input for a single line of text', () => {
expect(multiLineText(singleLine)).toBe(singleLine);
});
it('should return an array for unix multiline text', () => {
expect(multiLineText(unixMultiLineText)).toHaveLength(3);
});
it('should return an array for windows multiline text', () => {
expect(multiLineText(windowsMultiLineText)).toHaveLength(3);
});
it('should return an array with unique keys for duplicated multiline text', () => {
const wrappers = multiLineText(duplicatedMultiLine).map(tag => shallow(tag));
const keys = wrappers.map(wrapper => wrapper.key());
const deDup = new Set(keys);
expect(keys).toHaveLength(deDup.size);
});
it('should have 2 br tags for 3 lines of text', () => {
const tree = renderer.create(multiLineText(unixMultiLineText)).toJSON();
expect(tree).toMatchSnapshot();
});
});
});

View File

@ -0,0 +1,15 @@
.info-table, .info-table td, .info-table th {
border-collapse: collapse;
border: 1px solid #cccccc;
color: #444444;
margin-top: 0.25rem;
padding-right: 0.5rem;
padding: 0.25rem;
text-align: left;
vertical-align: top;
}
.info-table-monospace {
font-family: Menlo, Monaco, "Courier New", monospace;
font-size: 0.88em;
}

View File

@ -4,7 +4,7 @@ import deprecate from 'util-deprecate';
import { makeDecorator } from '@storybook/addons';
import { logger } from '@storybook/client-logger';
import Story from './components/Story';
import PropTable from './components/PropTable';
import PropTable from './components/PropTable/index';
import makeTableComponent from './components/makeTableComponent';
import { H1, H2, H3, H4, H5, H6, Code, P, UL, A, LI } from './components/markdown';

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-jest",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "React storybook addon that show component jest report",
"keywords": [
"addon",
@ -27,10 +27,10 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/components": "5.0.0-beta.1",
"@storybook/core-events": "5.0.0-beta.1",
"@storybook/theming": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"@storybook/components": "5.0.0-beta.2",
"@storybook/core-events": "5.0.0-beta.2",
"@storybook/theming": "5.0.0-beta.2",
"core-js": "^2.6.2",
"global": "^4.3.2",
"prop-types": "^15.6.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-knobs",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Storybook Addon Prop Editor Component",
"keywords": [
"addon",
@ -21,10 +21,10 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/components": "5.0.0-beta.1",
"@storybook/core-events": "5.0.0-beta.1",
"@storybook/theming": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"@storybook/components": "5.0.0-beta.2",
"@storybook/core-events": "5.0.0-beta.2",
"@storybook/theming": "5.0.0-beta.2",
"copy-to-clipboard": "^3.0.8",
"core-js": "^2.6.2",
"escape-html": "^1.0.3",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-links",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Story Links addon for storybook",
"keywords": [
"addon",
@ -21,8 +21,8 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/core-events": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"@storybook/core-events": "5.0.0-beta.2",
"common-tags": "^1.8.0",
"core-js": "^2.6.2",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-notes",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Write notes for your Storybook stories.",
"keywords": [
"addon",
@ -22,11 +22,11 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/client-logger": "5.0.0-beta.1",
"@storybook/components": "5.0.0-beta.1",
"@storybook/core-events": "5.0.0-beta.1",
"@storybook/theming": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"@storybook/client-logger": "5.0.0-beta.2",
"@storybook/components": "5.0.0-beta.2",
"@storybook/core-events": "5.0.0-beta.2",
"@storybook/theming": "5.0.0-beta.2",
"markdown-to-jsx": "^6.9.1",
"prop-types": "^15.6.2",
"util-deprecate": "^1.0.2"

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-ondevice-backgrounds",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "A storybook addon to show different backgrounds for your preview",
"keywords": [
"addon",
@ -23,7 +23,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"core-js": "^2.6.2",
"prop-types": "^15.6.2"
},

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-ondevice-knobs",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Display storybook story knobs on your deviced.",
"keywords": [
"addon",
@ -20,7 +20,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"core-js": "^2.6.2",
"deep-equal": "^1.0.1",
"prop-types": "^15.6.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-ondevice-notes",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Write notes for your Storybook stories.",
"keywords": [
"addon",
@ -18,7 +18,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"core-js": "^2.6.2",
"prop-types": "^15.6.2",
"react-native-simple-markdown": "^1.1.0"

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-options",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Options addon for storybook",
"keywords": [
"addon",
@ -20,7 +20,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"core-js": "^2.6.2",
"util-deprecate": "^1.0.2"
},

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-storyshots",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "StoryShots is a Jest Snapshot Testing Addon for Storybook.",
"keywords": [
"addon",
@ -24,7 +24,7 @@
"storybook": "start-storybook -p 6006"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"core-js": "^2.6.2",
"glob": "^7.1.3",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-storyshots-puppeteer",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Image snappshots addition to StoryShots base on puppeteer",
"keywords": [
"addon",
@ -21,8 +21,8 @@
"prepare": "node ../../../scripts/prepare.js"
},
"dependencies": {
"@storybook/core": "5.0.0-beta.1",
"@storybook/node-logger": "5.0.0-beta.1",
"@storybook/core": "5.0.0-beta.2",
"@storybook/node-logger": "5.0.0-beta.2",
"core-js": "^2.6.2",
"jest-image-snapshot": "^2.6.0",
"puppeteer": "^1.12.0",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-storysource",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Stories addon for storybook",
"keywords": [
"addon",
@ -21,9 +21,9 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/components": "5.0.0-beta.1",
"@storybook/theming": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"@storybook/components": "5.0.0-beta.2",
"@storybook/theming": "5.0.0-beta.2",
"core-js": "^2.6.2",
"estraverse": "^4.2.0",
"loader-utils": "^1.2.1",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-viewport",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Storybook addon to change the viewport size to mobile",
"keywords": [
"addon",
@ -20,11 +20,11 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/client-logger": "5.0.0-beta.1",
"@storybook/components": "5.0.0-beta.1",
"@storybook/core-events": "5.0.0-beta.1",
"@storybook/theming": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"@storybook/client-logger": "5.0.0-beta.2",
"@storybook/components": "5.0.0-beta.2",
"@storybook/core-events": "5.0.0-beta.2",
"@storybook/theming": "5.0.0-beta.2",
"core-js": "^2.6.2",
"global": "^4.3.2",
"memoizerific": "^1.11.3",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/angular",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Storybook for Angular: Develop Angular Components in isolation with Hot Reloading.",
"keywords": [
"storybook"
@ -25,8 +25,8 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/core": "5.0.0-beta.1",
"@storybook/node-logger": "5.0.0-beta.1",
"@storybook/core": "5.0.0-beta.2",
"@storybook/node-logger": "5.0.0-beta.2",
"angular2-template-loader": "^0.6.2",
"core-js": "^2.6.2",
"fork-ts-checker-webpack-plugin": "^0.5.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/ember",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Storybook for Ember: Develop Ember Component in isolation with Hot Reloading.",
"homepage": "https://github.com/storybooks/storybook/tree/master/app/ember",
"bugs": {
@ -23,7 +23,7 @@
},
"dependencies": {
"@ember/test-helpers": "^1.3.1",
"@storybook/core": "5.0.0-beta.1",
"@storybook/core": "5.0.0-beta.2",
"common-tags": "^1.8.0",
"core-js": "^2.6.2",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/html",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Storybook for HTML: View HTML snippets in isolation with Hot Reloading.",
"keywords": [
"storybook"
@ -24,7 +24,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/core": "5.0.0-beta.1",
"@storybook/core": "5.0.0-beta.2",
"common-tags": "^1.8.0",
"core-js": "^2.6.2",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/marko",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Storybook for Marko: Develop Marko Component in isolation with Hot Reloading.",
"keywords": [
"storybook"
@ -25,7 +25,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/core": "5.0.0-beta.1",
"@storybook/core": "5.0.0-beta.2",
"common-tags": "^1.8.0",
"core-js": "^2.6.2",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/mithril",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Storybook for Mithril: Develop Mithril Component in isolation.",
"keywords": [
"storybook"
@ -26,7 +26,7 @@
},
"dependencies": {
"@babel/plugin-transform-react-jsx": "^7.3.0",
"@storybook/core": "5.0.0-beta.1",
"@storybook/core": "5.0.0-beta.2",
"common-tags": "^1.8.0",
"core-js": "^2.6.2",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/polymer",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Storybook for Polymer: Develop Polymer components in isolation with Hot Reloading.",
"keywords": [
"storybook"
@ -24,7 +24,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/core": "5.0.0-beta.1",
"@storybook/core": "5.0.0-beta.2",
"@webcomponents/webcomponentsjs": "^1.2.0",
"common-tags": "^1.8.0",
"core-js": "^2.6.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/preact",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Storybook for Preact: Develop Preact Component in isolation.",
"keywords": [
"storybook"
@ -26,7 +26,7 @@
},
"dependencies": {
"@babel/plugin-transform-react-jsx": "^7.3.0",
"@storybook/core": "5.0.0-beta.1",
"@storybook/core": "5.0.0-beta.2",
"common-tags": "^1.8.0",
"core-js": "^2.6.2",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/react-native",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "A better way to develop React Native Components for your app",
"keywords": [
"react",
@ -25,12 +25,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-beta.1",
"@storybook/channel-websocket": "5.0.0-beta.1",
"@storybook/channels": "5.0.0-beta.1",
"@storybook/core": "5.0.0-beta.1",
"@storybook/core-events": "5.0.0-beta.1",
"@storybook/ui": "5.0.0-beta.1",
"@storybook/addons": "5.0.0-beta.2",
"@storybook/channel-websocket": "5.0.0-beta.2",
"@storybook/channels": "5.0.0-beta.2",
"@storybook/core": "5.0.0-beta.2",
"@storybook/core-events": "5.0.0-beta.2",
"@storybook/ui": "5.0.0-beta.2",
"babel-loader": "^8.0.4",
"babel-plugin-macros": "^2.4.5",
"babel-plugin-syntax-async-functions": "^6.13.0",
@ -61,7 +61,7 @@
"url-parse": "^1.4.3",
"uuid": "^3.3.2",
"webpack": "^4.29.0",
"webpack-dev-middleware": "^3.5.1",
"webpack-dev-middleware": "^3.5.2",
"webpack-hot-middleware": "^2.24.3",
"ws": "^6.1.3"
},

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/react",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Storybook for React: Develop React Component in isolation with Hot Reloading.",
"keywords": [
"storybook"
@ -28,9 +28,9 @@
"@babel/plugin-transform-react-constant-elements": "^7.2.0",
"@babel/preset-flow": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"@storybook/core": "5.0.0-beta.1",
"@storybook/node-logger": "5.0.0-beta.1",
"@storybook/theming": "5.0.0-beta.1",
"@storybook/core": "5.0.0-beta.2",
"@storybook/node-logger": "5.0.0-beta.2",
"@storybook/theming": "5.0.0-beta.2",
"@svgr/webpack": "^4.0.3",
"babel-plugin-named-asset-import": "^0.3.0",
"babel-plugin-react-docgen": "^2.0.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/riot",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"description": "Storybook for riot.js: View riot snippets in isolation with Hot Reloading.",
"keywords": [
"storybook"
@ -24,7 +24,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/core": "5.0.0-beta.1",
"@storybook/core": "5.0.0-beta.2",
"common-tags": "^1.8.0",
"core-js": "^2.6.2",
"global": "^4.3.2",

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