From dfbf5d9cb5a2135d11ab49c390969105c4d8da9e Mon Sep 17 00:00:00 2001 From: Emilio Martinez Date: Mon, 24 Jun 2019 12:30:42 -0700 Subject: [PATCH 1/7] Addon-knobs: straight rename js => ts This commit simply transitions the files from js to ts to make the commit history clearer afterwards. Expected not to type-check. --- addons/knobs/package.json | 2 +- addons/knobs/src/{KnobManager.js => KnobManager.ts} | 0 addons/knobs/src/{KnobStore.js => KnobStore.ts} | 0 addons/knobs/src/components/{Panel.js => Panel.tsx} | 0 .../src/components/{PropForm.js => PropForm.tsx} | 0 .../src/components/types/{Array.js => Array.tsx} | 0 .../components/types/{Boolean.js => Boolean.tsx} | 0 .../src/components/types/{Button.js => Button.tsx} | 0 .../types/{Checkboxes.js => Checkboxes.tsx} | 0 .../src/components/types/{Color.js => Color.tsx} | 0 .../src/components/types/{Date.js => Date.tsx} | 0 .../src/components/types/{Files.js => Files.tsx} | 0 .../src/components/types/{Number.js => Number.tsx} | 0 .../src/components/types/{Object.js => Object.tsx} | 0 .../components/types/{Options.js => Options.tsx} | 0 .../src/components/types/{Radio.js => Radio.tsx} | 0 .../src/components/types/{Select.js => Select.tsx} | 0 .../src/components/types/{Text.js => Text.tsx} | 0 .../src/components/types/{index.js => index.ts} | 0 addons/knobs/src/{converters.js => converters.ts} | 0 addons/knobs/src/{index.js => index.ts} | 0 addons/knobs/src/{register.js => register.tsx} | 0 .../src/{registerKnobs.js => registerKnobs.ts} | 0 addons/knobs/src/{shared.js => shared.ts} | 0 addons/knobs/tsconfig.json | 13 +++++++++++++ 25 files changed, 14 insertions(+), 1 deletion(-) rename addons/knobs/src/{KnobManager.js => KnobManager.ts} (100%) rename addons/knobs/src/{KnobStore.js => KnobStore.ts} (100%) rename addons/knobs/src/components/{Panel.js => Panel.tsx} (100%) rename addons/knobs/src/components/{PropForm.js => PropForm.tsx} (100%) rename addons/knobs/src/components/types/{Array.js => Array.tsx} (100%) rename addons/knobs/src/components/types/{Boolean.js => Boolean.tsx} (100%) rename addons/knobs/src/components/types/{Button.js => Button.tsx} (100%) rename addons/knobs/src/components/types/{Checkboxes.js => Checkboxes.tsx} (100%) rename addons/knobs/src/components/types/{Color.js => Color.tsx} (100%) rename addons/knobs/src/components/types/{Date.js => Date.tsx} (100%) rename addons/knobs/src/components/types/{Files.js => Files.tsx} (100%) rename addons/knobs/src/components/types/{Number.js => Number.tsx} (100%) rename addons/knobs/src/components/types/{Object.js => Object.tsx} (100%) rename addons/knobs/src/components/types/{Options.js => Options.tsx} (100%) rename addons/knobs/src/components/types/{Radio.js => Radio.tsx} (100%) rename addons/knobs/src/components/types/{Select.js => Select.tsx} (100%) rename addons/knobs/src/components/types/{Text.js => Text.tsx} (100%) rename addons/knobs/src/components/types/{index.js => index.ts} (100%) rename addons/knobs/src/{converters.js => converters.ts} (100%) rename addons/knobs/src/{index.js => index.ts} (100%) rename addons/knobs/src/{register.js => register.tsx} (100%) rename addons/knobs/src/{registerKnobs.js => registerKnobs.ts} (100%) rename addons/knobs/src/{shared.js => shared.ts} (100%) create mode 100644 addons/knobs/tsconfig.json diff --git a/addons/knobs/package.json b/addons/knobs/package.json index 0a8f478bc7fb..c743cb42ffee 100644 --- a/addons/knobs/package.json +++ b/addons/knobs/package.json @@ -17,7 +17,7 @@ }, "license": "MIT", "main": "dist/index.js", - "jsnext:main": "src/index.js", + "types": "dist/index.d.ts", "scripts": { "prepare": "node ../../scripts/prepare.js" }, diff --git a/addons/knobs/src/KnobManager.js b/addons/knobs/src/KnobManager.ts similarity index 100% rename from addons/knobs/src/KnobManager.js rename to addons/knobs/src/KnobManager.ts diff --git a/addons/knobs/src/KnobStore.js b/addons/knobs/src/KnobStore.ts similarity index 100% rename from addons/knobs/src/KnobStore.js rename to addons/knobs/src/KnobStore.ts diff --git a/addons/knobs/src/components/Panel.js b/addons/knobs/src/components/Panel.tsx similarity index 100% rename from addons/knobs/src/components/Panel.js rename to addons/knobs/src/components/Panel.tsx diff --git a/addons/knobs/src/components/PropForm.js b/addons/knobs/src/components/PropForm.tsx similarity index 100% rename from addons/knobs/src/components/PropForm.js rename to addons/knobs/src/components/PropForm.tsx diff --git a/addons/knobs/src/components/types/Array.js b/addons/knobs/src/components/types/Array.tsx similarity index 100% rename from addons/knobs/src/components/types/Array.js rename to addons/knobs/src/components/types/Array.tsx diff --git a/addons/knobs/src/components/types/Boolean.js b/addons/knobs/src/components/types/Boolean.tsx similarity index 100% rename from addons/knobs/src/components/types/Boolean.js rename to addons/knobs/src/components/types/Boolean.tsx diff --git a/addons/knobs/src/components/types/Button.js b/addons/knobs/src/components/types/Button.tsx similarity index 100% rename from addons/knobs/src/components/types/Button.js rename to addons/knobs/src/components/types/Button.tsx diff --git a/addons/knobs/src/components/types/Checkboxes.js b/addons/knobs/src/components/types/Checkboxes.tsx similarity index 100% rename from addons/knobs/src/components/types/Checkboxes.js rename to addons/knobs/src/components/types/Checkboxes.tsx diff --git a/addons/knobs/src/components/types/Color.js b/addons/knobs/src/components/types/Color.tsx similarity index 100% rename from addons/knobs/src/components/types/Color.js rename to addons/knobs/src/components/types/Color.tsx diff --git a/addons/knobs/src/components/types/Date.js b/addons/knobs/src/components/types/Date.tsx similarity index 100% rename from addons/knobs/src/components/types/Date.js rename to addons/knobs/src/components/types/Date.tsx diff --git a/addons/knobs/src/components/types/Files.js b/addons/knobs/src/components/types/Files.tsx similarity index 100% rename from addons/knobs/src/components/types/Files.js rename to addons/knobs/src/components/types/Files.tsx diff --git a/addons/knobs/src/components/types/Number.js b/addons/knobs/src/components/types/Number.tsx similarity index 100% rename from addons/knobs/src/components/types/Number.js rename to addons/knobs/src/components/types/Number.tsx diff --git a/addons/knobs/src/components/types/Object.js b/addons/knobs/src/components/types/Object.tsx similarity index 100% rename from addons/knobs/src/components/types/Object.js rename to addons/knobs/src/components/types/Object.tsx diff --git a/addons/knobs/src/components/types/Options.js b/addons/knobs/src/components/types/Options.tsx similarity index 100% rename from addons/knobs/src/components/types/Options.js rename to addons/knobs/src/components/types/Options.tsx diff --git a/addons/knobs/src/components/types/Radio.js b/addons/knobs/src/components/types/Radio.tsx similarity index 100% rename from addons/knobs/src/components/types/Radio.js rename to addons/knobs/src/components/types/Radio.tsx diff --git a/addons/knobs/src/components/types/Select.js b/addons/knobs/src/components/types/Select.tsx similarity index 100% rename from addons/knobs/src/components/types/Select.js rename to addons/knobs/src/components/types/Select.tsx diff --git a/addons/knobs/src/components/types/Text.js b/addons/knobs/src/components/types/Text.tsx similarity index 100% rename from addons/knobs/src/components/types/Text.js rename to addons/knobs/src/components/types/Text.tsx diff --git a/addons/knobs/src/components/types/index.js b/addons/knobs/src/components/types/index.ts similarity index 100% rename from addons/knobs/src/components/types/index.js rename to addons/knobs/src/components/types/index.ts diff --git a/addons/knobs/src/converters.js b/addons/knobs/src/converters.ts similarity index 100% rename from addons/knobs/src/converters.js rename to addons/knobs/src/converters.ts diff --git a/addons/knobs/src/index.js b/addons/knobs/src/index.ts similarity index 100% rename from addons/knobs/src/index.js rename to addons/knobs/src/index.ts diff --git a/addons/knobs/src/register.js b/addons/knobs/src/register.tsx similarity index 100% rename from addons/knobs/src/register.js rename to addons/knobs/src/register.tsx diff --git a/addons/knobs/src/registerKnobs.js b/addons/knobs/src/registerKnobs.ts similarity index 100% rename from addons/knobs/src/registerKnobs.js rename to addons/knobs/src/registerKnobs.ts diff --git a/addons/knobs/src/shared.js b/addons/knobs/src/shared.ts similarity index 100% rename from addons/knobs/src/shared.js rename to addons/knobs/src/shared.ts diff --git a/addons/knobs/tsconfig.json b/addons/knobs/tsconfig.json new file mode 100644 index 000000000000..8876bb6737a1 --- /dev/null +++ b/addons/knobs/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "./src", + "types": ["webpack-env"] + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "src/__tests__/**/*" + ] +} From 754ec02628e531ee48067d7454f99a1876ed5568 Mon Sep 17 00:00:00 2001 From: Emilio Martinez Date: Mon, 24 Jun 2019 18:12:10 -0700 Subject: [PATCH 2/7] Addon-knobs: migrate components --- addons/knobs/src/components/types/Array.tsx | 73 ++++++++------- addons/knobs/src/components/types/Boolean.tsx | 30 +++++-- addons/knobs/src/components/types/Button.tsx | 30 +++++-- .../knobs/src/components/types/Checkboxes.tsx | 83 +++++++++++------ addons/knobs/src/components/types/Color.tsx | 90 +++++++++++++------ addons/knobs/src/components/types/Date.tsx | 75 ++++++++++------ addons/knobs/src/components/types/Files.tsx | 41 ++++++--- addons/knobs/src/components/types/Number.tsx | 90 +++++++++++-------- addons/knobs/src/components/types/Object.tsx | 57 ++++++++---- addons/knobs/src/components/types/Options.tsx | 85 +++++++++++++++--- addons/knobs/src/components/types/Radio.tsx | 71 ++++++++++----- addons/knobs/src/components/types/Select.tsx | 34 +++++-- addons/knobs/src/components/types/Text.tsx | 54 ++++++----- 13 files changed, 555 insertions(+), 258 deletions(-) diff --git a/addons/knobs/src/components/types/Array.tsx b/addons/knobs/src/components/types/Array.tsx index 6f198878bd9d..a0a1fb81e02d 100644 --- a/addons/knobs/src/components/types/Array.tsx +++ b/addons/knobs/src/components/types/Array.tsx @@ -1,25 +1,61 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { Component, WeakValidationMap } from 'react'; import { Form } from '@storybook/components'; -function formatArray(value, separator) { +type ArrayTypeKnobValue = string[]; + +interface ArrayTypeProps { + knob: { + name: string; + value: ArrayTypeKnobValue; + separator: string; + }; + onChange: (value: ArrayTypeKnobValue) => ArrayTypeKnobValue; +} + +function formatArray(value: string, separator: string) { if (value === '') { return []; } return value.split(separator); } -class ArrayType extends React.Component { - shouldComponentUpdate(nextProps) { +export default class ArrayType extends Component { + static defaultProps: Partial = { + knob: {} as any, + onChange: (value: ArrayTypeKnobValue) => value, + }; + + static propTypes: WeakValidationMap = { + // TODO: remove `any` once DefinitelyTyped/DefinitelyTyped#31280 has been resolved + knob: PropTypes.shape({ + name: PropTypes.string, + value: PropTypes.array, + separator: PropTypes.string, + }) as any, + onChange: PropTypes.func, + }; + + static serialize = (value: ArrayTypeKnobValue) => value; + + static deserialize = (value: ArrayTypeKnobValue) => { + if (Array.isArray(value)) return value; + + return Object.keys(value) + .sort() + .reduce((array, key) => [...array, value[key]], []); + }; + + shouldComponentUpdate(nextProps: Readonly) { const { knob } = this.props; return nextProps.knob.value !== knob.value; } - handleChange = e => { + handleChange = (e: Event) => { const { knob, onChange } = this.props; - const { value } = e.target; + const { value } = e.target as HTMLTextAreaElement; const newVal = formatArray(value, knob.separator); onChange(newVal); @@ -40,28 +76,3 @@ class ArrayType extends React.Component { ); } } - -ArrayType.defaultProps = { - knob: {}, - onChange: value => value, -}; - -ArrayType.propTypes = { - knob: PropTypes.shape({ - name: PropTypes.string, - value: PropTypes.array, - separator: PropTypes.string, - }), - onChange: PropTypes.func, -}; - -ArrayType.serialize = value => value; -ArrayType.deserialize = value => { - if (Array.isArray(value)) return value; - - return Object.keys(value) - .sort() - .reduce((array, key) => [...array, value[key]], []); -}; - -export default ArrayType; diff --git a/addons/knobs/src/components/types/Boolean.tsx b/addons/knobs/src/components/types/Boolean.tsx index 8264f47fee28..818aa5ef1084 100644 --- a/addons/knobs/src/components/types/Boolean.tsx +++ b/addons/knobs/src/components/types/Boolean.tsx @@ -1,8 +1,19 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { FunctionComponent } from 'react'; import { styled } from '@storybook/theming'; +type BooleanTypeKnobValue = boolean; + +interface BooleanTypeProps { + knob: { + name: string; + value: BooleanTypeKnobValue; + separator: string; + }; + onChange: (value: BooleanTypeKnobValue) => BooleanTypeKnobValue; +} + const Input = styled.input({ display: 'table-cell', boxSizing: 'border-box', @@ -14,7 +25,13 @@ const Input = styled.input({ color: '#555', }); -const BooleanType = ({ knob, onChange }) => ( +const serialize = (value: BooleanTypeKnobValue): string | null => (value ? String(value) : null); +const deserialize = (value: string | null) => value === 'true'; + +const BooleanType: FunctionComponent & { + serialize: typeof serialize; + deserialize: typeof deserialize; +} = ({ knob, onChange }) => ( ( ); BooleanType.defaultProps = { - knob: {}, + knob: {} as any, onChange: value => value, }; BooleanType.propTypes = { + // TODO: remove `any` once DefinitelyTyped/DefinitelyTyped#31280 has been resolved knob: PropTypes.shape({ name: PropTypes.string, value: PropTypes.bool, - }), + }) as any, onChange: PropTypes.func, }; -BooleanType.serialize = value => (value ? String(value) : null); -BooleanType.deserialize = value => value === 'true'; +BooleanType.serialize = serialize; +BooleanType.deserialize = deserialize; export default BooleanType; diff --git a/addons/knobs/src/components/types/Button.tsx b/addons/knobs/src/components/types/Button.tsx index 39b0a07f5780..d5cd6d13d8cb 100644 --- a/addons/knobs/src/components/types/Button.tsx +++ b/addons/knobs/src/components/types/Button.tsx @@ -1,22 +1,42 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { FunctionComponent, Validator } from 'react'; import { Form } from '@storybook/components'; -const ButtonType = ({ knob, onClick }) => ( +interface ButtonTypeKnobProp { + name: string; +} + +interface ButtonTypeProps { + knob: ButtonTypeKnobProp; + onClick: (knob: ButtonTypeKnobProp) => any; +} + +const serialize = (): undefined => undefined; +const deserialize = (): undefined => undefined; + +const ButtonType: FunctionComponent & { + serialize: typeof serialize; + deserialize: typeof deserialize; +} = ({ knob, onClick }) => ( onClick(knob)}> {knob.name} ); +ButtonType.defaultProps = { + knob: {} as any, +}; + ButtonType.propTypes = { + // TODO: remove `any` once DefinitelyTyped/DefinitelyTyped#31280 has been resolved knob: PropTypes.shape({ name: PropTypes.string, - }).isRequired, + }).isRequired as Validator, onClick: PropTypes.func.isRequired, }; -ButtonType.serialize = () => undefined; -ButtonType.deserialize = () => undefined; +ButtonType.serialize = serialize; +ButtonType.deserialize = deserialize; export default ButtonType; diff --git a/addons/knobs/src/components/types/Checkboxes.tsx b/addons/knobs/src/components/types/Checkboxes.tsx index a15cf1d64126..1765c014d2df 100644 --- a/addons/knobs/src/components/types/Checkboxes.tsx +++ b/addons/knobs/src/components/types/Checkboxes.tsx @@ -1,8 +1,33 @@ -import React, { Component } from 'react'; +import React, { Component, ChangeEvent, WeakValidationMap } from 'react'; import PropTypes from 'prop-types'; import { styled } from '@storybook/theming'; -const CheckboxesWrapper = styled.div(({ isInline }) => +type CheckboxesTypeKnobValue = string[]; + +interface CheckboxesWrapperProps { + isInline: boolean; +} + +interface CheckboxesTypeKnobProp { + name: string; + value: CheckboxesTypeKnobValue; + defaultValue: CheckboxesTypeKnobValue; + options: { + [key: string]: string; + }; +} + +interface CheckboxesTypeProps { + knob: CheckboxesTypeKnobProp; + isInline: boolean; + onChange: (value: CheckboxesTypeKnobValue) => CheckboxesTypeKnobValue; +} + +interface CheckboxesTypeState { + values: CheckboxesTypeKnobValue; +} + +const CheckboxesWrapper = styled.div(({ isInline }: CheckboxesWrapperProps) => isInline ? { display: 'flex', @@ -27,8 +52,29 @@ const CheckboxLabel = styled.label({ display: 'inline-block', }); -class CheckboxesType extends Component { - constructor(props) { +export default class CheckboxesType extends Component { + static defaultProps: CheckboxesTypeProps = { + knob: {} as any, + onChange: value => value, + isInline: false, + }; + + static propTypes: WeakValidationMap = { + // TODO: remove `any` once DefinitelyTyped/DefinitelyTyped#31280 has been resolved + knob: PropTypes.shape({ + name: PropTypes.string, + value: PropTypes.array, + options: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), + }) as any, + onChange: PropTypes.func, + isInline: PropTypes.bool, + }; + + static serialize = (value: CheckboxesTypeKnobValue) => value; + + static deserialize = (value: CheckboxesTypeKnobValue) => value; + + constructor(props: CheckboxesTypeProps) { super(props); const { knob } = props; @@ -37,9 +83,9 @@ class CheckboxesType extends Component { }; } - handleChange = e => { + handleChange = (e: ChangeEvent) => { const { onChange } = this.props; - const currentValue = e.target.value; + const currentValue = (e.target as HTMLInputElement).value; const { values } = this.state; if (values.includes(currentValue)) { @@ -53,10 +99,10 @@ class CheckboxesType extends Component { onChange(values); }; - renderCheckboxList = ({ options }) => + renderCheckboxList = ({ options }: CheckboxesTypeKnobProp) => Object.keys(options).map(key => this.renderCheckbox(key, options[key])); - renderCheckbox = (label, value) => { + renderCheckbox = (label: string, value: string) => { const { knob } = this.props; const { name } = knob; const id = `${name}-${value}`; @@ -87,24 +133,3 @@ class CheckboxesType extends Component { ); } } - -CheckboxesType.defaultProps = { - knob: {}, - onChange: value => value, - isInline: false, -}; - -CheckboxesType.propTypes = { - knob: PropTypes.shape({ - name: PropTypes.string, - value: PropTypes.array, - options: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), - }), - onChange: PropTypes.func, - isInline: PropTypes.bool, -}; - -CheckboxesType.serialize = value => value; -CheckboxesType.deserialize = value => value; - -export default CheckboxesType; diff --git a/addons/knobs/src/components/types/Color.tsx b/addons/knobs/src/components/types/Color.tsx index 1b440d615616..43c3c2a20f5e 100644 --- a/addons/knobs/src/components/types/Color.tsx +++ b/addons/knobs/src/components/types/Color.tsx @@ -1,12 +1,45 @@ +// @ts-ignore import { document } from 'global'; import PropTypes from 'prop-types'; -import React from 'react'; +import React, { Component, WeakValidationMap } from 'react'; +// @ts-ignore import { SketchPicker } from 'react-color'; import { styled } from '@storybook/theming'; - import { Form } from '@storybook/components'; +type ColorTypeKnobValue = string; + +interface ColorTypeProps { + knob: { + name: string; + value: ColorTypeKnobValue; + }; + onChange: (value: ColorTypeKnobValue) => ColorTypeKnobValue; +} + +interface ColorTypeState { + displayColorPicker: boolean; +} + +interface ColorButtonProps { + name: string; + type: string; + size: string; + active: boolean; + onClick: () => any; +} + +// TODO: These types should come from @types/react-color once installed +interface ColorResult { + rgb: { + a?: number; + b: number; + g: number; + r: number; + }; +} + const { Button } = Form; const Swatch = styled.div(({ theme }) => ({ @@ -20,25 +53,45 @@ const Swatch = styled.div(({ theme }) => ({ borderRadius: '1rem', })); -const ColorButton = styled(Button)(({ active }) => ({ +const ColorButton = styled(Button)(({ active }: ColorButtonProps) => ({ zIndex: active ? 3 : 'unset', })); const Popover = styled.div({ position: 'absolute', - zIndex: '2', + zIndex: 2, }); -class ColorType extends React.Component { - state = { +export default class ColorType extends Component { + static propTypes: WeakValidationMap = { + // TODO: remove `any` once DefinitelyTyped/DefinitelyTyped#31280 has been resolved + knob: PropTypes.shape({ + name: PropTypes.string, + value: PropTypes.string, + }) as any, + onChange: PropTypes.func, + }; + + static defaultProps: ColorTypeProps = { + knob: {} as any, + onChange: value => value, + }; + + static serialize = (value: ColorTypeKnobValue) => value; + + static deserialize = (value: ColorTypeKnobValue) => value; + + state: ColorTypeState = { displayColorPicker: false, }; + popover: HTMLDivElement; + componentDidMount() { document.addEventListener('mousedown', this.handleWindowMouseDown); } - shouldComponentUpdate(nextProps, nextState) { + shouldComponentUpdate(nextProps: ColorTypeProps, nextState: ColorTypeState) { const { knob } = this.props; const { displayColorPicker } = this.state; @@ -51,9 +104,9 @@ class ColorType extends React.Component { document.removeEventListener('mousedown', this.handleWindowMouseDown); } - handleWindowMouseDown = e => { + handleWindowMouseDown = (e: MouseEvent) => { const { displayColorPicker } = this.state; - if (!displayColorPicker || this.popover.contains(e.target)) { + if (!displayColorPicker || this.popover.contains(e.target as HTMLElement)) { return; } @@ -70,7 +123,7 @@ class ColorType extends React.Component { }); }; - handleChange = color => { + handleChange = (color: ColorResult) => { const { onChange } = this.props; onChange(`rgba(${color.rgb.r},${color.rgb.g},${color.rgb.b},${color.rgb.a})`); @@ -105,20 +158,3 @@ class ColorType extends React.Component { ); } } - -ColorType.propTypes = { - knob: PropTypes.shape({ - name: PropTypes.string, - value: PropTypes.string, - }), - onChange: PropTypes.func, -}; -ColorType.defaultProps = { - knob: {}, - onChange: value => value, -}; - -ColorType.serialize = value => value; -ColorType.deserialize = value => value; - -export default ColorType; diff --git a/addons/knobs/src/components/types/Date.tsx b/addons/knobs/src/components/types/Date.tsx index f645fcca9e68..e1fb6cccb665 100644 --- a/addons/knobs/src/components/types/Date.tsx +++ b/addons/knobs/src/components/types/Date.tsx @@ -1,8 +1,22 @@ -import React, { Component } from 'react'; +import React, { Component, ChangeEvent, WeakValidationMap } from 'react'; import PropTypes from 'prop-types'; import { styled } from '@storybook/theming'; import { Form } from '@storybook/components'; +type DateTypeKnobValue = number; + +interface DateTypeProps { + knob: { + name: string; + value: DateTypeKnobValue; + }; + onChange: (value: DateTypeKnobValue) => DateTypeKnobValue; +} + +interface DateTypeState { + valid: boolean | undefined; +} + const FlexSpaced = styled.div({ flex: 1, display: 'flex', @@ -15,29 +29,54 @@ const FlexSpaced = styled.div({ }); const FlexInput = styled(Form.Input)({ flex: 1 }); -const formatDate = date => { +const formatDate = (date: Date) => { const year = `000${date.getFullYear()}`.slice(-4); const month = `0${date.getMonth() + 1}`.slice(-2); const day = `0${date.getDate()}`.slice(-2); return `${year}-${month}-${day}`; }; -const formatTime = date => { + +const formatTime = (date: Date) => { const hours = `0${date.getHours()}`.slice(-2); const minutes = `0${date.getMinutes()}`.slice(-2); return `${hours}:${minutes}`; }; -class DateType extends Component { +export default class DateType extends Component { + static defaultProps: DateTypeProps = { + knob: {} as any, + onChange: value => value, + }; + + static propTypes: WeakValidationMap = { + // TODO: remove `any` once DefinitelyTyped/DefinitelyTyped#31280 has been resolved + knob: PropTypes.shape({ + name: PropTypes.string, + value: PropTypes.number, + }) as any, + onChange: PropTypes.func, + }; + + static serialize = (value: DateTypeKnobValue) => + new Date(value).getTime() || new Date().getTime(); + + static deserialize = (value: DateTypeKnobValue) => + new Date(value).getTime() || new Date().getTime(); + static getDerivedStateFromProps() { return { valid: true }; } - state = { + state: DateTypeState = { valid: undefined, }; + dateInput: HTMLInputElement; + + timeInput: HTMLInputElement; + componentDidUpdate() { const { knob } = this.props; const { valid } = this.state; @@ -49,7 +88,7 @@ class DateType extends Component { } } - onDateChange = e => { + onDateChange = (e: ChangeEvent) => { const { knob, onChange } = this.props; const { state } = this; @@ -70,7 +109,7 @@ class DateType extends Component { } }; - onTimeChange = e => { + onTimeChange = (e: ChangeEvent) => { const { knob, onChange } = this.props; const { state } = this; @@ -100,7 +139,7 @@ class DateType extends Component { { + ref={(el: HTMLInputElement) => { this.dateInput = el; }} id={`${name}date`} @@ -111,7 +150,7 @@ class DateType extends Component { type="time" id={`${name}time`} name={`${name}time`} - ref={el => { + ref={(el: HTMLInputElement) => { this.timeInput = el; }} onChange={this.onTimeChange} @@ -121,21 +160,3 @@ class DateType extends Component { ) : null; } } - -DateType.defaultProps = { - knob: {}, - onChange: value => value, -}; - -DateType.propTypes = { - knob: PropTypes.shape({ - name: PropTypes.string, - value: PropTypes.number, - }), - onChange: PropTypes.func, -}; - -DateType.serialize = value => new Date(value).getTime() || new Date().getTime(); -DateType.deserialize = value => new Date(value).getTime() || new Date().getTime(); - -export default DateType; diff --git a/addons/knobs/src/components/types/Files.tsx b/addons/knobs/src/components/types/Files.tsx index 797498d2b935..628e7fb0f00a 100644 --- a/addons/knobs/src/components/types/Files.tsx +++ b/addons/knobs/src/components/types/Files.tsx @@ -1,46 +1,67 @@ +// @ts-ignore import { FileReader } from 'global'; import PropTypes from 'prop-types'; -import React from 'react'; +import React, { ChangeEvent, FunctionComponent } from 'react'; import { styled } from '@storybook/theming'; import { Form } from '@storybook/components'; +type DateTypeKnobValue = string[]; + +interface FilesTypeProps { + knob: { + name: string; + accept: string; + value: DateTypeKnobValue; + }; + onChange: (value: DateTypeKnobValue) => DateTypeKnobValue; +} + const FileInput = styled(Form.Input)({ paddingTop: 4, }); -function fileReaderPromise(file) { - return new Promise(resolve => { +function fileReaderPromise(file: File) { + return new Promise(resolve => { const fileReader = new FileReader(); - fileReader.onload = e => resolve(e.currentTarget.result); + fileReader.onload = (e: Event) => resolve((e.currentTarget as FileReader).result); fileReader.readAsDataURL(file); }); } -const FilesType = ({ knob, onChange }) => ( +const serialize = (): undefined => undefined; +const deserialize = (): undefined => undefined; + +const FilesType: FunctionComponent & { + serialize: typeof serialize; + deserialize: typeof deserialize; +} = ({ knob, onChange }) => ( Promise.all(Array.from(e.target.files).map(fileReaderPromise)).then(onChange)} + onChange={(e: ChangeEvent) => + Promise.all(Array.from(e.target.files).map(fileReaderPromise)).then(onChange) + } accept={knob.accept} size="flex" /> ); FilesType.defaultProps = { - knob: {}, + knob: {} as any, onChange: value => value, }; FilesType.propTypes = { + // TODO: remove `any` once DefinitelyTyped/DefinitelyTyped#31280 has been resolved knob: PropTypes.shape({ name: PropTypes.string, - }), + }) as any, onChange: PropTypes.func, }; -FilesType.serialize = () => undefined; -FilesType.deserialize = () => undefined; +FilesType.serialize = serialize; +FilesType.deserialize = deserialize; export default FilesType; diff --git a/addons/knobs/src/components/types/Number.tsx b/addons/knobs/src/components/types/Number.tsx index 9a6b677cb8f5..e27b3189ea3d 100644 --- a/addons/knobs/src/components/types/Number.tsx +++ b/addons/knobs/src/components/types/Number.tsx @@ -1,45 +1,78 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { Component, ChangeEvent } from 'react'; import { styled } from '@storybook/theming'; - import { Form } from '@storybook/components'; -const base = { - boxSizing: 'border-box', - height: '25px', - outline: 'none', - border: '1px solid #f7f4f4', - borderRadius: 2, - fontSize: 11, - padding: '5px', - color: '#444', -}; - -const RangeInput = styled.input(base, { - display: 'table-cell', - flexGrow: 1, -}); +type NumberTypeKnobValue = number; + +interface NumberTypeProps { + knob: { + name: string; + value: number; + range?: boolean; + min?: number; + max?: number; + step?: number; + }; + onChange: (value: NumberTypeKnobValue) => NumberTypeKnobValue; +} + +const RangeInput = styled.input( + { + boxSizing: 'border-box', + height: '25px', + outline: 'none', + border: '1px solid #f7f4f4', + borderRadius: 2, + fontSize: 11, + padding: '5px', + color: '#444', + }, + { + display: 'table-cell', + flexGrow: 1, + } +); + const RangeLabel = styled.span({ paddingLeft: 5, paddingRight: 5, fontSize: 12, whiteSpace: 'nowrap', }); + const RangeWrapper = styled.div({ display: 'flex', alignItems: 'center', width: '100%', }); -class NumberType extends React.Component { - shouldComponentUpdate(nextProps) { +export default class NumberType extends Component { + static propTypes = { + knob: PropTypes.shape({ + name: PropTypes.string, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + range: PropTypes.bool, + min: PropTypes.number, + max: PropTypes.number, + step: PropTypes.number, + }).isRequired, + onChange: PropTypes.func.isRequired, + }; + + static serialize = (value: NumberTypeKnobValue | null | undefined) => + value === null || value === undefined ? '' : String(value); + + static deserialize = (value: string) => (value === '' ? null : parseFloat(value)); + + shouldComponentUpdate(nextProps: NumberTypeProps) { const { knob } = this.props; return nextProps.knob.value !== knob.value; } - handleChange = event => { + handleChange = (event: ChangeEvent) => { const { onChange } = this.props; const { value } = event.target; @@ -83,20 +116,3 @@ class NumberType extends React.Component { ); } } - -NumberType.propTypes = { - knob: PropTypes.shape({ - name: PropTypes.string, - value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - range: PropTypes.bool, - min: PropTypes.number, - max: PropTypes.number, - step: PropTypes.number, - }).isRequired, - onChange: PropTypes.func.isRequired, -}; - -NumberType.serialize = value => (value === null || value === undefined ? '' : String(value)); -NumberType.deserialize = value => (value === '' ? null : parseFloat(value)); - -export default NumberType; diff --git a/addons/knobs/src/components/types/Object.tsx b/addons/knobs/src/components/types/Object.tsx index b68e55e24fa5..c3a3c245c86b 100644 --- a/addons/knobs/src/components/types/Object.tsx +++ b/addons/knobs/src/components/types/Object.tsx @@ -1,17 +1,41 @@ -import React, { Component } from 'react'; +import React, { Component, ChangeEvent } from 'react'; import PropTypes from 'prop-types'; import deepEqual from 'fast-deep-equal'; +// @ts-ignore import { polyfill } from 'react-lifecycles-compat'; import { Form } from '@storybook/components'; -class ObjectType extends Component { - state = { - value: {}, - failed: false, - json: '', +interface ObjectTypeProps { + knob: { + name: string; + value: T; }; + onChange: (value: T) => T; +} + +interface ObjectTypeState { + value: string; + failed: boolean; + json?: T; +} + +class ObjectType extends Component> { + static propTypes = { + knob: PropTypes.shape({ + name: PropTypes.string, + value: PropTypes.oneOfType([PropTypes.object, PropTypes.array]), + }).isRequired, + onChange: PropTypes.func.isRequired, + }; + + static serialize: { (object: T): string } = object => JSON.stringify(object); - static getDerivedStateFromProps(props, state) { + static deserialize: { (value: string): T } = value => (value ? JSON.parse(value) : {}); + + static getDerivedStateFromProps( + props: ObjectTypeProps, + state: ObjectTypeState + ): ObjectTypeState { if (!deepEqual(props.knob.value, state.json)) { try { return { @@ -26,7 +50,13 @@ class ObjectType extends Component { return null; } - handleChange = e => { + state: ObjectTypeState = { + value: '', + failed: false, + json: {} as any, + }; + + handleChange = (e: ChangeEvent) => { const { value } = e.target; const { json: stateJson } = this.state; const { knob, onChange } = this.props; @@ -65,17 +95,6 @@ class ObjectType extends Component { } } -ObjectType.propTypes = { - knob: PropTypes.shape({ - name: PropTypes.string, - value: PropTypes.oneOfType([PropTypes.object, PropTypes.array]), - }).isRequired, - onChange: PropTypes.func.isRequired, -}; - -ObjectType.serialize = object => JSON.stringify(object); -ObjectType.deserialize = value => (value ? JSON.parse(value) : {}); - polyfill(ObjectType); export default ObjectType; diff --git a/addons/knobs/src/components/types/Options.tsx b/addons/knobs/src/components/types/Options.tsx index 8580f72d7d44..0ce43e5b17cd 100644 --- a/addons/knobs/src/components/types/Options.tsx +++ b/addons/knobs/src/components/types/Options.tsx @@ -1,5 +1,6 @@ -import React from 'react'; +import React, { FunctionComponent } from 'react'; import PropTypes from 'prop-types'; +// @ts-ignore import ReactSelect from 'react-select'; import { styled } from '@storybook/theming'; @@ -8,13 +9,67 @@ import CheckboxesType from './Checkboxes'; // TODO: Apply the Storybook theme to react-select -const OptionsSelect = styled(ReactSelect)({ +export type OptionsKnobOptionsDisplay = + | 'radio' + | 'inline-radio' + | 'check' + | 'inline-check' + | 'select' + | 'multi-select'; + +export interface OptionsKnobOptions { + display?: OptionsKnobOptionsDisplay; +} + +interface OptionsTypeProps { + knob: { + name: string; + value: T; + defaultValue: T; + options: { + [key: string]: T; + }; + optionsObj: OptionsKnobOptions; + }; + display: OptionsKnobOptionsDisplay; + onChange: (value: T) => T; +} + +const OptionsSelect: React.ComponentType = styled(ReactSelect)({ width: '100%', maxWidth: '300px', color: 'black', }); -const OptionsType = props => { +// TODO: These types should come from @types/react-select once installed. +type ReactSelectValueType = + | OptionType + | OptionsType + | null + | undefined; + +type ReactSelectOnChangeFn = ( + value: ReactSelectValueType +) => void; + +interface ReactSelectProps { + value: OptionsSelectValueItem | OptionsSelectValueItem[]; + options: any; + isMulti: boolean; + onChange: ReactSelectOnChangeFn; +} +interface OptionsSelectValueItem { + value: any; + label: string; +} + +const serialize: { (value: T): T } = value => value; +const deserialize: { (value: T): T } = value => value; + +const OptionsType: FunctionComponent> & { + serialize: typeof serialize; + deserialize: typeof deserialize; +} = props => { const { knob, onChange } = props; const { display } = knob.optionsObj; @@ -29,19 +84,19 @@ const OptionsType = props => { } if (display === 'select' || display === 'multi-select') { - const options = Object.keys(knob.options).map(key => ({ + const options: OptionsSelectValueItem[] = Object.keys(knob.options).map(key => ({ value: knob.options[key], label: key, })); const isMulti = display === 'multi-select'; const optionsIndex = options.findIndex(i => i.value === knob.value); - let defaultValue = options[optionsIndex]; - let handleChange = e => onChange(e.value); + let defaultValue: typeof options | typeof options[0] = options[optionsIndex]; + let handleChange: ReactSelectOnChangeFn = (e: OptionsSelectValueItem) => onChange(e.value); if (isMulti) { defaultValue = options.filter(i => knob.value.includes(i.value)); - handleChange = values => onChange(values.map(item => item.value)); + handleChange = (values: OptionsSelectValueItem[]) => onChange(values.map(item => item.value)); } return ( @@ -53,33 +108,35 @@ const OptionsType = props => { /> ); } + return null; }; OptionsType.defaultProps = { - knob: {}, + knob: {} as any, display: 'select', onChange: value => value, }; OptionsType.propTypes = { + // TODO: remove `any` once DefinitelyTyped/DefinitelyTyped#31280 has been resolved knob: PropTypes.shape({ name: PropTypes.string, value: PropTypes.oneOfType([PropTypes.array, PropTypes.string]), options: PropTypes.object, - }), - display: PropTypes.oneOf([ - 'check', - 'inline-check', + }) as any, + display: PropTypes.oneOf([ 'radio', 'inline-radio', + 'check', + 'inline-check', 'select', 'multi-select', ]), onChange: PropTypes.func, }; -OptionsType.serialize = value => value; -OptionsType.deserialize = value => value; +OptionsType.serialize = serialize; +OptionsType.deserialize = deserialize; export default OptionsType; diff --git a/addons/knobs/src/components/types/Radio.tsx b/addons/knobs/src/components/types/Radio.tsx index f4163967e2ea..b9d9bc094c0e 100644 --- a/addons/knobs/src/components/types/Radio.tsx +++ b/addons/knobs/src/components/types/Radio.tsx @@ -1,8 +1,29 @@ -import React, { Component } from 'react'; +import React, { Component, WeakValidationMap } from 'react'; import PropTypes from 'prop-types'; import { styled } from '@storybook/theming'; -const RadiosWrapper = styled.div(({ isInline }) => +type RadiosTypeKnobValue = string; + +interface RadiosTypeKnobProp { + name: string; + value: RadiosTypeKnobValue; + defaultValue: RadiosTypeKnobValue; + options: { + [key: string]: RadiosTypeKnobValue; + }; +} + +interface RadiosTypeProps { + knob: RadiosTypeKnobProp; + isInline: boolean; + onChange: (value: RadiosTypeKnobValue) => RadiosTypeKnobValue; +} + +interface RadiosWrapperProps { + isInline: boolean; +} + +const RadiosWrapper = styled.div(({ isInline }: RadiosWrapperProps) => isInline ? { display: 'flex', @@ -21,15 +42,36 @@ const RadioLabel = styled.label({ display: 'inline-block', }); -class RadiosType extends Component { - renderRadioButtonList({ options }) { +class RadiosType extends Component { + static defaultProps: RadiosTypeProps = { + knob: {} as any, + onChange: value => value, + isInline: false, + }; + + static propTypes: WeakValidationMap = { + // TODO: remove `any` once DefinitelyTyped/DefinitelyTyped#31280 has been resolved + knob: PropTypes.shape({ + name: PropTypes.string, + value: PropTypes.string, + options: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), + }) as any, + onChange: PropTypes.func, + isInline: PropTypes.bool, + }; + + static serialize = (value: RadiosTypeKnobValue) => value; + + static deserialize = (value: RadiosTypeKnobValue) => value; + + renderRadioButtonList({ options }: RadiosTypeKnobProp) { if (Array.isArray(options)) { return options.map(val => this.renderRadioButton(val, val)); } return Object.keys(options).map(key => this.renderRadioButton(key, options[key])); } - renderRadioButton(label, value) { + renderRadioButton(label: string, value: RadiosTypeKnobValue) { const opts = { label, value }; const { onChange, knob } = this.props; const { name } = knob; @@ -57,23 +99,4 @@ class RadiosType extends Component { } } -RadiosType.defaultProps = { - knob: {}, - onChange: value => value, - isInline: false, -}; - -RadiosType.propTypes = { - knob: PropTypes.shape({ - name: PropTypes.string, - value: PropTypes.string, - options: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), - }), - onChange: PropTypes.func, - isInline: PropTypes.bool, -}; - -RadiosType.serialize = value => value; -RadiosType.deserialize = value => value; - export default RadiosType; diff --git a/addons/knobs/src/components/types/Select.tsx b/addons/knobs/src/components/types/Select.tsx index ff3809505ff5..2ecd65caac1a 100644 --- a/addons/knobs/src/components/types/Select.tsx +++ b/addons/knobs/src/components/types/Select.tsx @@ -1,9 +1,28 @@ -import React from 'react'; +import React, { FunctionComponent, ChangeEvent } from 'react'; import PropTypes from 'prop-types'; import { Form } from '@storybook/components'; -const SelectType = ({ knob, onChange }) => { +type SelectTypeKnobValue = string; + +interface SelectTypeProps { + knob: { + name: string; + value: SelectTypeKnobValue; + options: { + [key: string]: SelectTypeKnobValue; + }; + }; + onChange: (value: SelectTypeKnobValue) => SelectTypeKnobValue; +} + +const serialize = (value: SelectTypeKnobValue) => value; +const deserialize = (value: SelectTypeKnobValue) => value; + +const SelectType: FunctionComponent & { + serialize: typeof serialize; + deserialize: typeof deserialize; +} = ({ knob, onChange }) => { const { options } = knob; const entries = Array.isArray(options) ? options.reduce((acc, k) => Object.assign(acc, { [k]: k }), {}) @@ -15,7 +34,7 @@ const SelectType = ({ knob, onChange }) => { { + onChange={(e: ChangeEvent) => { onChange(entries[e.target.value]); }} size="flex" @@ -30,20 +49,21 @@ const SelectType = ({ knob, onChange }) => { }; SelectType.defaultProps = { - knob: {}, + knob: {} as any, onChange: value => value, }; SelectType.propTypes = { + // TODO: remove `any` once DefinitelyTyped/DefinitelyTyped#31280 has been resolved knob: PropTypes.shape({ name: PropTypes.string, value: PropTypes.any, options: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), - }), + }) as any, onChange: PropTypes.func, }; -SelectType.serialize = value => value; -SelectType.deserialize = value => value; +SelectType.serialize = serialize; +SelectType.deserialize = deserialize; export default SelectType; diff --git a/addons/knobs/src/components/types/Text.tsx b/addons/knobs/src/components/types/Text.tsx index 2f95e8f5b0f0..c07466dc84aa 100644 --- a/addons/knobs/src/components/types/Text.tsx +++ b/addons/knobs/src/components/types/Text.tsx @@ -1,16 +1,44 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { Component, ChangeEvent, WeakValidationMap } from 'react'; import { Form } from '@storybook/components'; -class TextType extends React.Component { - shouldComponentUpdate(nextProps) { +type TextTypeKnobValue = string; + +interface TextTypeProps { + knob: { + name: string; + value: TextTypeKnobValue; + }; + onChange: (value: TextTypeKnobValue) => TextTypeKnobValue; +} + +export default class TextType extends Component { + static defaultProps: TextTypeProps = { + knob: {} as any, + onChange: value => value, + }; + + static propTypes: WeakValidationMap = { + // TODO: remove `any` once DefinitelyTyped/DefinitelyTyped#31280 has been resolved + knob: PropTypes.shape({ + name: PropTypes.string, + value: PropTypes.string, + }) as any, + onChange: PropTypes.func, + }; + + static serialize = (value: TextTypeKnobValue) => value; + + static deserialize = (value: TextTypeKnobValue) => value; + + shouldComponentUpdate(nextProps: TextTypeProps) { const { knob } = this.props; return nextProps.knob.value !== knob.value; } - handleChange = event => { + handleChange = (event: ChangeEvent) => { const { onChange } = this.props; const { value } = event.target; @@ -31,21 +59,3 @@ class TextType extends React.Component { ); } } - -TextType.defaultProps = { - knob: {}, - onChange: value => value, -}; - -TextType.propTypes = { - knob: PropTypes.shape({ - name: PropTypes.string, - value: PropTypes.string, - }), - onChange: PropTypes.func, -}; - -TextType.serialize = value => value; -TextType.deserialize = value => value; - -export default TextType; From c2470ce3edad6ae334dd4753cd35af0b21ec6cfd Mon Sep 17 00:00:00 2001 From: Emilio Martinez Date: Tue, 25 Jun 2019 02:23:53 -0700 Subject: [PATCH 3/7] Addon-knobs: install missing related types and refactor --- addons/knobs/package.json | 5 +++++ addons/knobs/src/components/types/Color.tsx | 13 +---------- addons/knobs/src/components/types/Object.tsx | 1 - addons/knobs/src/components/types/Options.tsx | 22 +++++-------------- 4 files changed, 11 insertions(+), 30 deletions(-) diff --git a/addons/knobs/package.json b/addons/knobs/package.json index c743cb42ffee..b45939d6b65e 100644 --- a/addons/knobs/package.json +++ b/addons/knobs/package.json @@ -44,5 +44,10 @@ }, "publishConfig": { "access": "public" + }, + "devDependencies": { + "@types/react-color": "^3.0.1", + "@types/react-lifecycles-compat": "^3.0.1", + "@types/react-select": "^2.0.19" } } diff --git a/addons/knobs/src/components/types/Color.tsx b/addons/knobs/src/components/types/Color.tsx index 43c3c2a20f5e..abfa62fc772f 100644 --- a/addons/knobs/src/components/types/Color.tsx +++ b/addons/knobs/src/components/types/Color.tsx @@ -2,8 +2,7 @@ import { document } from 'global'; import PropTypes from 'prop-types'; import React, { Component, WeakValidationMap } from 'react'; -// @ts-ignore -import { SketchPicker } from 'react-color'; +import { SketchPicker, ColorResult } from 'react-color'; import { styled } from '@storybook/theming'; import { Form } from '@storybook/components'; @@ -30,16 +29,6 @@ interface ColorButtonProps { onClick: () => any; } -// TODO: These types should come from @types/react-color once installed -interface ColorResult { - rgb: { - a?: number; - b: number; - g: number; - r: number; - }; -} - const { Button } = Form; const Swatch = styled.div(({ theme }) => ({ diff --git a/addons/knobs/src/components/types/Object.tsx b/addons/knobs/src/components/types/Object.tsx index c3a3c245c86b..84a8f7f1c285 100644 --- a/addons/knobs/src/components/types/Object.tsx +++ b/addons/knobs/src/components/types/Object.tsx @@ -1,7 +1,6 @@ import React, { Component, ChangeEvent } from 'react'; import PropTypes from 'prop-types'; import deepEqual from 'fast-deep-equal'; -// @ts-ignore import { polyfill } from 'react-lifecycles-compat'; import { Form } from '@storybook/components'; diff --git a/addons/knobs/src/components/types/Options.tsx b/addons/knobs/src/components/types/Options.tsx index 0ce43e5b17cd..23ec55ba2fce 100644 --- a/addons/knobs/src/components/types/Options.tsx +++ b/addons/knobs/src/components/types/Options.tsx @@ -1,7 +1,7 @@ import React, { FunctionComponent } from 'react'; import PropTypes from 'prop-types'; -// @ts-ignore import ReactSelect from 'react-select'; +import { ValueType } from 'react-select/lib/types'; import { styled } from '@storybook/theming'; import RadiosType from './Radio'; @@ -35,29 +35,17 @@ interface OptionsTypeProps { onChange: (value: T) => T; } -const OptionsSelect: React.ComponentType = styled(ReactSelect)({ +// : React.ComponentType +const OptionsSelect = styled(ReactSelect)({ width: '100%', maxWidth: '300px', color: 'black', }); -// TODO: These types should come from @types/react-select once installed. -type ReactSelectValueType = - | OptionType - | OptionsType - | null - | undefined; - -type ReactSelectOnChangeFn = ( - value: ReactSelectValueType +type ReactSelectOnChangeFn = ( + value: ValueType ) => void; -interface ReactSelectProps { - value: OptionsSelectValueItem | OptionsSelectValueItem[]; - options: any; - isMulti: boolean; - onChange: ReactSelectOnChangeFn; -} interface OptionsSelectValueItem { value: any; label: string; From 8a1219e0eb2c946429d15345b4f7f55780c3601a Mon Sep 17 00:00:00 2001 From: Emilio Martinez Date: Tue, 25 Jun 2019 17:22:34 -0700 Subject: [PATCH 4/7] Addon-knobs: migrate registration, manager and panels to ts --- addons/knobs/package.json | 1 + addons/knobs/src/KnobManager.ts | 59 +++++---- addons/knobs/src/KnobStore.ts | 72 ++++++++--- addons/knobs/src/components/Panel.tsx | 113 ++++++++++++------ addons/knobs/src/components/PropForm.tsx | 46 ++++--- addons/knobs/src/components/types/Array.tsx | 12 +- addons/knobs/src/components/types/Boolean.tsx | 14 ++- addons/knobs/src/components/types/Button.tsx | 11 +- .../knobs/src/components/types/Checkboxes.tsx | 6 +- addons/knobs/src/components/types/Color.tsx | 10 +- addons/knobs/src/components/types/Date.tsx | 10 +- addons/knobs/src/components/types/Files.tsx | 14 ++- addons/knobs/src/components/types/Number.tsx | 21 ++-- addons/knobs/src/components/types/Object.tsx | 10 +- addons/knobs/src/components/types/Options.tsx | 24 ++-- addons/knobs/src/components/types/Radio.tsx | 14 ++- addons/knobs/src/components/types/Select.tsx | 20 ++-- addons/knobs/src/components/types/Text.tsx | 10 +- addons/knobs/src/components/types/index.ts | 13 ++ addons/knobs/src/converters.ts | 21 ++-- addons/knobs/src/index.ts | 60 +++++++--- addons/knobs/src/registerKnobs.ts | 7 +- yarn.lock | 37 +++++- 23 files changed, 416 insertions(+), 189 deletions(-) diff --git a/addons/knobs/package.json b/addons/knobs/package.json index b45939d6b65e..eb55c5471209 100644 --- a/addons/knobs/package.json +++ b/addons/knobs/package.json @@ -46,6 +46,7 @@ "access": "public" }, "devDependencies": { + "@types/escape-html": "0.0.20", "@types/react-color": "^3.0.1", "@types/react-lifecycles-compat": "^3.0.1", "@types/react-select": "^2.0.19" diff --git a/addons/knobs/src/KnobManager.ts b/addons/knobs/src/KnobManager.ts index afad9bd3d642..31312e4b13b0 100644 --- a/addons/knobs/src/KnobManager.ts +++ b/addons/knobs/src/KnobManager.ts @@ -1,25 +1,36 @@ /* eslint no-underscore-dangle: 0 */ + +// @ts-ignore import { navigator } from 'global'; import escape from 'escape-html'; - +// TODO: remove ts-ignore once client-api is typed +// @ts-ignore import { getQueryParams } from '@storybook/client-api'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { Channel } from '@storybook/channels'; -import KnobStore from './KnobStore'; +import KnobStore, { Knob, KnobStoreKnob } from './KnobStore'; import { SET } from './shared'; import { deserializers } from './converters'; -const knobValuesFromUrl = Object.entries(getQueryParams()).reduce((acc, [k, v]) => { - if (k.includes('knob-')) { - return { ...acc, [k.replace('knob-', '')]: v }; - } - return acc; -}, {}); +const knobValuesFromUrl: Record = Object.entries(getQueryParams()).reduce( + (acc, [k, v]) => { + if (k.includes('knob-')) { + return { ...acc, [k.replace('knob-', '')]: v }; + } + return acc; + }, + {} +); // This is used by _mayCallChannel to determine how long to wait to before triggering a panel update const PANEL_UPDATE_INTERVAL = 400; -const escapeStrings = obj => { +function escapeStrings(obj: { [key: string]: string }): { [key: string]: string }; +function escapeStrings(obj: (string | string[])[]): (string | string[])[]; +function escapeStrings(obj: string): string; +function escapeStrings(obj: any): any { if (typeof obj === 'string') { return escape(obj); } @@ -31,31 +42,39 @@ const escapeStrings = obj => { const didChange = newArray.some((newValue, key) => newValue !== obj[key]); return didChange ? newArray : obj; } - return Object.entries(obj).reduce((acc, [key, oldValue]) => { + return Object.entries<{ [key: string]: string }>(obj).reduce((acc, [key, oldValue]) => { const newValue = escapeStrings(oldValue); return newValue === oldValue ? acc : { ...acc, [key]: newValue }; }, obj); -}; +} + +interface KnobManagerOptions { + escapeHTML?: boolean; + disableDebounce?: boolean; +} export default class KnobManager { - constructor() { - this.knobStore = new KnobStore(); - this.options = {}; - } + knobStore = new KnobStore(); + + channel: Channel; + + options: KnobManagerOptions = {}; + + calling: boolean; - setChannel(channel) { + setChannel(channel: Channel) { this.channel = channel; } - setOptions(options) { + setOptions(options: KnobManagerOptions) { this.options = options; } - getKnobValue({ value }) { + getKnobValue({ value }: Knob) { return this.options.escapeHTML ? escapeStrings(value) : value; } - knob(name, options) { + knob(name: string, options: Knob) { this._mayCallChannel(); const knobName = options.groupId ? `${name}_${options.groupId}` : name; @@ -77,7 +96,7 @@ export default class KnobManager { return this.getKnobValue(existingKnob); } - const knobInfo = { + const knobInfo: Knob & { name: string; defaultValue?: any } = { ...options, name: knobName, label: name, diff --git a/addons/knobs/src/KnobStore.ts b/addons/knobs/src/KnobStore.ts index 18b5125245a0..4abdb288bf01 100644 --- a/addons/knobs/src/KnobStore.ts +++ b/addons/knobs/src/KnobStore.ts @@ -1,20 +1,64 @@ -const callArg = fn => fn(); -const callAll = fns => fns.forEach(callArg); +import Types, { + TextTypeKnob, + NumberTypeKnob, + ColorTypeKnob, + BooleanTypeKnob, + ObjectTypeKnob, + SelectTypeKnob, + RadiosTypeKnob, + ArrayTypeKnob, + DateTypeKnob, + ButtonTypeOnClickProp, + FileTypeKnob, + OptionsTypeKnob, +} from './components/types'; + +type Callback = () => any; + +type KnobPlus = K & { type: T; groupId?: string }; + +export type Knob = + | KnobPlus<'text', Pick> + | KnobPlus<'boolean', Pick> + | KnobPlus<'number', Pick> + | KnobPlus<'color', Pick> + | KnobPlus<'object', Pick, 'value'>> + | KnobPlus<'select', Pick & { selectV2: true }> + | KnobPlus<'radios', Pick> + | KnobPlus<'array', Pick> + | KnobPlus<'date', Pick> + | KnobPlus<'files', Pick> + | KnobPlus<'button', { value?: unknown; callback: ButtonTypeOnClickProp; hideLabel: true }> + | KnobPlus<'options', Pick, 'options' | 'value' | 'optionsObj'>>; + +export type KnobStoreKnob = Knob & { + name: string; + used?: boolean; + defaultValue?: any; + hideLabel?: boolean; + callback?: () => any; +}; + +const callArg = (fn: Callback) => fn(); +const callAll = (fns: Callback[]) => fns.forEach(callArg); export default class KnobStore { - constructor() { - this.store = {}; - this.callbacks = []; - } + store: Record = {}; + + callbacks: Callback[] = []; + + timer: number; - has(key) { + has(key: string) { return this.store[key] !== undefined; } - set(key, value) { - this.store[key] = value; - this.store[key].used = true; - this.store[key].groupId = value.groupId; + set(key: string, value: KnobStoreKnob) { + this.store[key] = { + ...value, + used: true, + groupId: value.groupId, + }; // debounce the execution of the callbacks for 50 milliseconds if (this.timer) { @@ -23,7 +67,7 @@ export default class KnobStore { this.timer = setTimeout(callAll, 50, this.callbacks); } - get(key) { + get(key: string) { const knob = this.store[key]; if (knob) { knob.used = true; @@ -45,11 +89,11 @@ export default class KnobStore { }); } - subscribe(cb) { + subscribe(cb: Callback) { this.callbacks.push(cb); } - unsubscribe(cb) { + unsubscribe(cb: Callback) { const index = this.callbacks.indexOf(cb); this.callbacks.splice(index, 1); } diff --git a/addons/knobs/src/components/Panel.tsx b/addons/knobs/src/components/Panel.tsx index a256325f9fea..aae80957d7be 100644 --- a/addons/knobs/src/components/Panel.tsx +++ b/addons/knobs/src/components/Panel.tsx @@ -1,6 +1,7 @@ -import React, { PureComponent, Fragment } from 'react'; +import React, { PureComponent, Fragment, ComponentType } from 'react'; import PropTypes from 'prop-types'; import qs from 'qs'; +// @ts-ignore import { document } from 'global'; import { styled } from '@storybook/theming'; import copy from 'copy-to-clipboard'; @@ -18,6 +19,7 @@ import { RESET, SET, CHANGE, SET_OPTIONS, CLICK } from '../shared'; import Types from './types'; import PropForm from './PropForm'; +import { KnobStoreKnob } from '../KnobStore'; const getTimestamp = () => +new Date(); @@ -32,17 +34,60 @@ const PanelWrapper = styled(({ children, className }) => ( width: '100%', }); -export default class KnobPanel extends PureComponent { - constructor(props) { - super(props); - this.state = { - knobs: {}, - }; - this.options = {}; +interface PanelKnobGroups { + title: string; + render: (knob: any) => any; +} - this.lastEdit = getTimestamp(); - this.loadedFromUrl = false; - } +interface KnobPanelProps { + active: boolean; + onReset?: object; + api: { + on: Function; + off: Function; + emit: Function; + getQueryParam: Function; + setQueryParams: Function; + }; +} + +interface KnobPanelState { + knobs: Record; +} + +interface KnobPanelOptions { + timestamps?: boolean; +} + +type KnobControlType = ComponentType & { + serialize: (v: any) => any; + deserialize: (v: any) => any; +}; + +export default class KnobPanel extends PureComponent { + static propTypes = { + active: PropTypes.bool.isRequired, + onReset: PropTypes.object, // eslint-disable-line + api: PropTypes.shape({ + on: PropTypes.func, + getQueryParam: PropTypes.func, + setQueryParams: PropTypes.func, + }).isRequired, + }; + + state: KnobPanelState = { + knobs: {}, + }; + + options: KnobPanelOptions = {}; + + lastEdit: number = getTimestamp(); + + loadedFromUrl = false; + + mounted = false; + + stopListeningOnStory: Function; componentDidMount() { this.mounted = true; @@ -66,12 +111,18 @@ export default class KnobPanel extends PureComponent { this.stopListeningOnStory(); } - setOptions = (options = { timestamps: false }) => { + setOptions = (options: KnobPanelOptions = { timestamps: false }) => { this.options = options; }; - setKnobs = ({ knobs, timestamp }) => { - const queryParams = {}; + setKnobs = ({ + knobs, + timestamp, + }: { + knobs: Record; + timestamp?: number; + }) => { + const queryParams: Record = {}; const { api } = this.props; if (!this.options.timestamps || !timestamp || this.lastEdit <= timestamp) { @@ -83,9 +134,9 @@ export default class KnobPanel extends PureComponent { // If the knob value present in url if (urlValue !== undefined) { - const value = Types[knob.type].deserialize(urlValue); + const value = (Types[knob.type] as KnobControlType).deserialize(urlValue); knob.value = value; - queryParams[`knob-${name}`] = Types[knob.type].serialize(value); + queryParams[`knob-${name}`] = (Types[knob.type] as KnobControlType).serialize(value); api.emit(CHANGE, knob); } @@ -111,7 +162,7 @@ export default class KnobPanel extends PureComponent { const { knobs } = this.state; Object.entries(knobs).forEach(([name, knob]) => { - query[`knob-${name}`] = Types[knob.type].serialize(knob.value); + query[`knob-${name}`] = (Types[knob.type] as KnobControlType).serialize(knob.value); }); copy(`${location.origin + location.pathname}?${qs.stringify(query, { encode: false })}`); @@ -119,13 +170,13 @@ export default class KnobPanel extends PureComponent { // TODO: show some notification of this }; - emitChange = changedKnob => { + emitChange = (changedKnob: KnobStoreKnob) => { const { api } = this.props; api.emit(CHANGE, changedKnob); }; - handleChange = changedKnob => { + handleChange = (changedKnob: KnobStoreKnob) => { this.lastEdit = getTimestamp(); const { api } = this.props; const { knobs } = this.state; @@ -139,18 +190,18 @@ export default class KnobPanel extends PureComponent { this.setState({ knobs: newKnobs }, () => { this.emitChange(changedKnob); - const queryParams = {}; + const queryParams: { [key: string]: any } = {}; Object.keys(newKnobs).forEach(n => { const knob = newKnobs[n]; - queryParams[`knob-${n}`] = Types[knob.type].serialize(knob.value); + queryParams[`knob-${n}`] = (Types[knob.type] as KnobControlType).serialize(knob.value); }); api.setQueryParams(queryParams); }); }; - handleClick = knob => { + handleClick = (knob: KnobStoreKnob) => { const { api } = this.props; api.emit(CLICK, knob); @@ -163,8 +214,8 @@ export default class KnobPanel extends PureComponent { return null; } - const groups = {}; - const groupIds = []; + const groups: Record = {}; + const groupIds: string[] = []; const knobKeysArray = Object.keys(knobs).filter(key => knobs[key].used); @@ -210,12 +261,12 @@ export default class KnobPanel extends PureComponent { } // Always sort DEFAULT_GROUP_ID (ungrouped) tab last without changing the remaining tabs - const sortEntries = g => { + const sortEntries = (g: Record): [string, PanelKnobGroups][] => { const unsortedKeys = Object.keys(g); if (unsortedKeys.indexOf(DEFAULT_GROUP_ID) !== -1) { const sortedKeys = unsortedKeys.filter(key => key !== DEFAULT_GROUP_ID); sortedKeys.push(DEFAULT_GROUP_ID); - return sortedKeys.map(key => [key, g[key]]); + return sortedKeys.map<[string, PanelKnobGroups]>(key => [key, g[key]]); } return Object.entries(g); }; @@ -251,13 +302,3 @@ export default class KnobPanel extends PureComponent { ); } } - -KnobPanel.propTypes = { - active: PropTypes.bool.isRequired, - onReset: PropTypes.object, // eslint-disable-line - api: PropTypes.shape({ - on: PropTypes.func, - getQueryParam: PropTypes.func, - setQueryParams: PropTypes.func, - }).isRequired, -}; diff --git a/addons/knobs/src/components/PropForm.tsx b/addons/knobs/src/components/PropForm.tsx index aefbb46355bd..5b4c381151f9 100644 --- a/addons/knobs/src/components/PropForm.tsx +++ b/addons/knobs/src/components/PropForm.tsx @@ -1,13 +1,38 @@ -import React, { Component } from 'react'; +import React, { Component, WeakValidationMap, ComponentType, Requireable } from 'react'; import PropTypes from 'prop-types'; import { Form } from '@storybook/components'; import TypeMap from './types'; +import { KnobStoreKnob } from '../KnobStore'; + +interface PropFormProps { + knobs: KnobStoreKnob[]; + onFieldChange: Function; + onFieldClick: Function; +} const InvalidType = () => Invalid Type; -export default class PropForm extends Component { - makeChangeHandler(name, type) { +export default class PropForm extends Component { + static displayName = 'PropForm'; + + static defaultProps = { + knobs: [] as KnobStoreKnob[], + }; + + static propTypes: WeakValidationMap = { + // TODO: remove `any` once DefinitelyTyped/DefinitelyTyped#31280 has been resolved + knobs: PropTypes.arrayOf( + PropTypes.shape({ + name: PropTypes.string, + value: PropTypes.any, + }) + ).isRequired as Requireable, + onFieldChange: PropTypes.func.isRequired, + onFieldClick: PropTypes.func.isRequired, + }; + + makeChangeHandler(name: string, type: string) { const { onFieldChange } = this.props; return (value = '') => { const change = { name, type, value }; @@ -23,7 +48,7 @@ export default class PropForm extends Component {
{knobs.map(knob => { const changeHandler = this.makeChangeHandler(knob.name, knob.type); - const InputType = TypeMap[knob.type] || InvalidType; + const InputType: ComponentType = TypeMap[knob.type] || InvalidType; return ( @@ -35,16 +60,3 @@ export default class PropForm extends Component { ); } } - -PropForm.displayName = 'PropForm'; - -PropForm.propTypes = { - knobs: PropTypes.arrayOf( - PropTypes.shape({ - name: PropTypes.string, - value: PropTypes.any, - }) - ).isRequired, - onFieldChange: PropTypes.func.isRequired, - onFieldClick: PropTypes.func.isRequired, -}; diff --git a/addons/knobs/src/components/types/Array.tsx b/addons/knobs/src/components/types/Array.tsx index a0a1fb81e02d..34140af7940c 100644 --- a/addons/knobs/src/components/types/Array.tsx +++ b/addons/knobs/src/components/types/Array.tsx @@ -5,12 +5,14 @@ import { Form } from '@storybook/components'; type ArrayTypeKnobValue = string[]; +export interface ArrayTypeKnob { + name: string; + value: ArrayTypeKnobValue; + separator: string; +} + interface ArrayTypeProps { - knob: { - name: string; - value: ArrayTypeKnobValue; - separator: string; - }; + knob: ArrayTypeKnob; onChange: (value: ArrayTypeKnobValue) => ArrayTypeKnobValue; } diff --git a/addons/knobs/src/components/types/Boolean.tsx b/addons/knobs/src/components/types/Boolean.tsx index 818aa5ef1084..d9c1443d260a 100644 --- a/addons/knobs/src/components/types/Boolean.tsx +++ b/addons/knobs/src/components/types/Boolean.tsx @@ -5,12 +5,14 @@ import { styled } from '@storybook/theming'; type BooleanTypeKnobValue = boolean; -interface BooleanTypeProps { - knob: { - name: string; - value: BooleanTypeKnobValue; - separator: string; - }; +export interface BooleanTypeKnob { + name: string; + value: BooleanTypeKnobValue; + separator: string; +} + +export interface BooleanTypeProps { + knob: BooleanTypeKnob; onChange: (value: BooleanTypeKnobValue) => BooleanTypeKnobValue; } diff --git a/addons/knobs/src/components/types/Button.tsx b/addons/knobs/src/components/types/Button.tsx index d5cd6d13d8cb..4dfe28b66391 100644 --- a/addons/knobs/src/components/types/Button.tsx +++ b/addons/knobs/src/components/types/Button.tsx @@ -3,13 +3,16 @@ import React, { FunctionComponent, Validator } from 'react'; import { Form } from '@storybook/components'; -interface ButtonTypeKnobProp { +export interface ButtonTypeKnob { name: string; + value: unknown; } -interface ButtonTypeProps { - knob: ButtonTypeKnobProp; - onClick: (knob: ButtonTypeKnobProp) => any; +export type ButtonTypeOnClickProp = (knob: ButtonTypeKnob) => any; + +export interface ButtonTypeProps { + knob: ButtonTypeKnob; + onClick: ButtonTypeOnClickProp; } const serialize = (): undefined => undefined; diff --git a/addons/knobs/src/components/types/Checkboxes.tsx b/addons/knobs/src/components/types/Checkboxes.tsx index 1765c014d2df..7b12cf537673 100644 --- a/addons/knobs/src/components/types/Checkboxes.tsx +++ b/addons/knobs/src/components/types/Checkboxes.tsx @@ -8,7 +8,7 @@ interface CheckboxesWrapperProps { isInline: boolean; } -interface CheckboxesTypeKnobProp { +export interface CheckboxesTypeKnob { name: string; value: CheckboxesTypeKnobValue; defaultValue: CheckboxesTypeKnobValue; @@ -18,7 +18,7 @@ interface CheckboxesTypeKnobProp { } interface CheckboxesTypeProps { - knob: CheckboxesTypeKnobProp; + knob: CheckboxesTypeKnob; isInline: boolean; onChange: (value: CheckboxesTypeKnobValue) => CheckboxesTypeKnobValue; } @@ -99,7 +99,7 @@ export default class CheckboxesType extends Component + renderCheckboxList = ({ options }: CheckboxesTypeKnob) => Object.keys(options).map(key => this.renderCheckbox(key, options[key])); renderCheckbox = (label: string, value: string) => { diff --git a/addons/knobs/src/components/types/Color.tsx b/addons/knobs/src/components/types/Color.tsx index abfa62fc772f..5c4a0afaf87a 100644 --- a/addons/knobs/src/components/types/Color.tsx +++ b/addons/knobs/src/components/types/Color.tsx @@ -9,11 +9,13 @@ import { Form } from '@storybook/components'; type ColorTypeKnobValue = string; +export interface ColorTypeKnob { + name: string; + value: ColorTypeKnobValue; +} + interface ColorTypeProps { - knob: { - name: string; - value: ColorTypeKnobValue; - }; + knob: ColorTypeKnob; onChange: (value: ColorTypeKnobValue) => ColorTypeKnobValue; } diff --git a/addons/knobs/src/components/types/Date.tsx b/addons/knobs/src/components/types/Date.tsx index e1fb6cccb665..e27b70eca10c 100644 --- a/addons/knobs/src/components/types/Date.tsx +++ b/addons/knobs/src/components/types/Date.tsx @@ -5,11 +5,13 @@ import { Form } from '@storybook/components'; type DateTypeKnobValue = number; +export interface DateTypeKnob { + name: string; + value: DateTypeKnobValue; +} + interface DateTypeProps { - knob: { - name: string; - value: DateTypeKnobValue; - }; + knob: DateTypeKnob; onChange: (value: DateTypeKnobValue) => DateTypeKnobValue; } diff --git a/addons/knobs/src/components/types/Files.tsx b/addons/knobs/src/components/types/Files.tsx index 628e7fb0f00a..34d345f97be1 100644 --- a/addons/knobs/src/components/types/Files.tsx +++ b/addons/knobs/src/components/types/Files.tsx @@ -8,12 +8,14 @@ import { Form } from '@storybook/components'; type DateTypeKnobValue = string[]; -interface FilesTypeProps { - knob: { - name: string; - accept: string; - value: DateTypeKnobValue; - }; +export interface FileTypeKnob { + name: string; + accept: string; + value: DateTypeKnobValue; +} + +export interface FilesTypeProps { + knob: FileTypeKnob; onChange: (value: DateTypeKnobValue) => DateTypeKnobValue; } diff --git a/addons/knobs/src/components/types/Number.tsx b/addons/knobs/src/components/types/Number.tsx index e27b3189ea3d..686abd9948ee 100644 --- a/addons/knobs/src/components/types/Number.tsx +++ b/addons/knobs/src/components/types/Number.tsx @@ -6,15 +6,20 @@ import { Form } from '@storybook/components'; type NumberTypeKnobValue = number; +export interface NumberTypeKnobOptions { + range?: boolean; + min?: number; + max?: number; + step?: number; +} + +export interface NumberTypeKnob extends NumberTypeKnobOptions { + name: string; + value: number; +} + interface NumberTypeProps { - knob: { - name: string; - value: number; - range?: boolean; - min?: number; - max?: number; - step?: number; - }; + knob: NumberTypeKnob; onChange: (value: NumberTypeKnobValue) => NumberTypeKnobValue; } diff --git a/addons/knobs/src/components/types/Object.tsx b/addons/knobs/src/components/types/Object.tsx index 84a8f7f1c285..3000e24cabe3 100644 --- a/addons/knobs/src/components/types/Object.tsx +++ b/addons/knobs/src/components/types/Object.tsx @@ -4,11 +4,13 @@ import deepEqual from 'fast-deep-equal'; import { polyfill } from 'react-lifecycles-compat'; import { Form } from '@storybook/components'; +export interface ObjectTypeKnob { + name: string; + value: T; +} + interface ObjectTypeProps { - knob: { - name: string; - value: T; - }; + knob: ObjectTypeKnob; onChange: (value: T) => T; } diff --git a/addons/knobs/src/components/types/Options.tsx b/addons/knobs/src/components/types/Options.tsx index 23ec55ba2fce..62a972d1a415 100644 --- a/addons/knobs/src/components/types/Options.tsx +++ b/addons/knobs/src/components/types/Options.tsx @@ -21,16 +21,20 @@ export interface OptionsKnobOptions { display?: OptionsKnobOptionsDisplay; } -interface OptionsTypeProps { - knob: { - name: string; - value: T; - defaultValue: T; - options: { - [key: string]: T; - }; - optionsObj: OptionsKnobOptions; - }; +export interface OptionsTypeKnob { + name: string; + value: T; + defaultValue: T; + options: OptionsTypeOptionsProp; + optionsObj: OptionsKnobOptions; +} + +export interface OptionsTypeOptionsProp { + [key: string]: T; +} + +export interface OptionsTypeProps { + knob: OptionsTypeKnob; display: OptionsKnobOptionsDisplay; onChange: (value: T) => T; } diff --git a/addons/knobs/src/components/types/Radio.tsx b/addons/knobs/src/components/types/Radio.tsx index b9d9bc094c0e..9cd910f67959 100644 --- a/addons/knobs/src/components/types/Radio.tsx +++ b/addons/knobs/src/components/types/Radio.tsx @@ -4,17 +4,19 @@ import { styled } from '@storybook/theming'; type RadiosTypeKnobValue = string; -interface RadiosTypeKnobProp { +export interface RadiosTypeKnob { name: string; value: RadiosTypeKnobValue; defaultValue: RadiosTypeKnobValue; - options: { - [key: string]: RadiosTypeKnobValue; - }; + options: RadiosTypeOptionsProp; +} + +export interface RadiosTypeOptionsProp { + [key: string]: RadiosTypeKnobValue; } interface RadiosTypeProps { - knob: RadiosTypeKnobProp; + knob: RadiosTypeKnob; isInline: boolean; onChange: (value: RadiosTypeKnobValue) => RadiosTypeKnobValue; } @@ -64,7 +66,7 @@ class RadiosType extends Component { static deserialize = (value: RadiosTypeKnobValue) => value; - renderRadioButtonList({ options }: RadiosTypeKnobProp) { + renderRadioButtonList({ options }: RadiosTypeKnob) { if (Array.isArray(options)) { return options.map(val => this.renderRadioButton(val, val)); } diff --git a/addons/knobs/src/components/types/Select.tsx b/addons/knobs/src/components/types/Select.tsx index 2ecd65caac1a..e650527c0172 100644 --- a/addons/knobs/src/components/types/Select.tsx +++ b/addons/knobs/src/components/types/Select.tsx @@ -5,14 +5,18 @@ import { Form } from '@storybook/components'; type SelectTypeKnobValue = string; -interface SelectTypeProps { - knob: { - name: string; - value: SelectTypeKnobValue; - options: { - [key: string]: SelectTypeKnobValue; - }; - }; +export interface SelectTypeKnob { + name: string; + value: SelectTypeKnobValue; + options: SelectTypeOptionsProp; +} + +export interface SelectTypeOptionsProp { + [key: string]: SelectTypeKnobValue; +} + +export interface SelectTypeProps { + knob: SelectTypeKnob; onChange: (value: SelectTypeKnobValue) => SelectTypeKnobValue; } diff --git a/addons/knobs/src/components/types/Text.tsx b/addons/knobs/src/components/types/Text.tsx index c07466dc84aa..cf939ffa3f1e 100644 --- a/addons/knobs/src/components/types/Text.tsx +++ b/addons/knobs/src/components/types/Text.tsx @@ -5,11 +5,13 @@ import { Form } from '@storybook/components'; type TextTypeKnobValue = string; +export interface TextTypeKnob { + name: string; + value: TextTypeKnobValue; +} + interface TextTypeProps { - knob: { - name: string; - value: TextTypeKnobValue; - }; + knob: TextTypeKnob; onChange: (value: TextTypeKnobValue) => TextTypeKnobValue; } diff --git a/addons/knobs/src/components/types/index.ts b/addons/knobs/src/components/types/index.ts index b0c620dd94ca..c8a96d5245f0 100644 --- a/addons/knobs/src/components/types/index.ts +++ b/addons/knobs/src/components/types/index.ts @@ -25,3 +25,16 @@ export default { files: FilesType, options: OptionsType, }; + +export { TextTypeKnob } from './Text'; +export { NumberTypeKnob, NumberTypeKnobOptions } from './Number'; +export { ColorTypeKnob } from './Color'; +export { BooleanTypeKnob } from './Boolean'; +export { ObjectTypeKnob } from './Object'; +export { SelectTypeKnob, SelectTypeOptionsProp } from './Select'; +export { RadiosTypeKnob, RadiosTypeOptionsProp } from './Radio'; +export { ArrayTypeKnob } from './Array'; +export { DateTypeKnob } from './Date'; +export { ButtonTypeKnob, ButtonTypeOnClickProp } from './Button'; +export { FileTypeKnob } from './Files'; +export { OptionsTypeKnob, OptionsTypeOptionsProp, OptionsKnobOptions } from './Options'; diff --git a/addons/knobs/src/converters.ts b/addons/knobs/src/converters.ts index 7e63c8de78a3..0c07813bce0b 100644 --- a/addons/knobs/src/converters.ts +++ b/addons/knobs/src/converters.ts @@ -1,21 +1,22 @@ -const unconvertable = () => undefined; +const unconvertable = (): undefined => undefined; export const converters = { - jsonParse: value => JSON.parse(value), - jsonStringify: value => JSON.stringify(value), - simple: value => value, - stringifyIfSet: value => (value === null || value === undefined ? '' : String(value)), - stringifyIfTruthy: value => (value ? String(value) : null), - toArray: value => { + jsonParse: (value: any): any => JSON.parse(value), + jsonStringify: (value: any): string => JSON.stringify(value), + simple: (value: any): any => value, + stringifyIfSet: (value: any): string => + value === null || value === undefined ? '' : String(value), + stringifyIfTruthy: (value: any): string | null => (value ? String(value) : null), + toArray: (value: any): any[] => { if (Array.isArray(value)) { return value; } return value.split(','); }, - toBoolean: value => value === 'true', - toDate: value => new Date(value).getTime() || new Date().getTime(), - toFloat: value => (value === '' ? null : parseFloat(value)), + toBoolean: (value: any): boolean => value === 'true', + toDate: (value: any): number => new Date(value).getTime() || new Date().getTime(), + toFloat: (value: any): number => (value === '' ? null : parseFloat(value)), }; export const serializers = { diff --git a/addons/knobs/src/index.ts b/addons/knobs/src/index.ts index 7035ead6c55f..4b99546a1bb8 100644 --- a/addons/knobs/src/index.ts +++ b/addons/knobs/src/index.ts @@ -1,22 +1,34 @@ import addons, { makeDecorator } from '@storybook/addons'; import { SET_OPTIONS } from './shared'; - import { manager, registerKnobs } from './registerKnobs'; - -export function knob(name, optionsParam) { +import { + NumberTypeKnobOptions, + ButtonTypeOnClickProp, + RadiosTypeOptionsProp, + SelectTypeOptionsProp, + OptionsTypeOptionsProp, + OptionsKnobOptions, +} from './components/types'; + +export function knob(name: string, optionsParam: any) { return manager.knob(name, optionsParam); } -export function text(name, value, groupId) { +export function text(name: string, value: string, groupId?: string) { return manager.knob(name, { type: 'text', value, groupId }); } -export function boolean(name, value, groupId) { +export function boolean(name: string, value: boolean, groupId?: string) { return manager.knob(name, { type: 'boolean', value, groupId }); } -export function number(name, value, options = {}, groupId) { +export function number( + name: string, + value: number, + options: NumberTypeKnobOptions = {}, + groupId?: string +) { const rangeDefaults = { min: 0, max: 10, @@ -31,8 +43,8 @@ export function number(name, value, options = {}, groupId) { : options; const finalOptions = { + type: 'number' as 'number', ...mergedOptions, - type: 'number', value, groupId, }; @@ -40,40 +52,56 @@ export function number(name, value, options = {}, groupId) { return manager.knob(name, finalOptions); } -export function color(name, value, groupId) { +export function color(name: string, value: string, groupId?: string) { return manager.knob(name, { type: 'color', value, groupId }); } -export function object(name, value, groupId) { +export function object(name: string, value: T, groupId?: string) { return manager.knob(name, { type: 'object', value, groupId }); } -export function select(name, options, value, groupId) { +export function select( + name: string, + options: SelectTypeOptionsProp, + value: string, + groupId?: string +) { return manager.knob(name, { type: 'select', selectV2: true, options, value, groupId }); } -export function radios(name, options, value, groupId) { +export function radios( + name: string, + options: RadiosTypeOptionsProp, + value: string, + groupId?: string +) { return manager.knob(name, { type: 'radios', options, value, groupId }); } -export function array(name, value, separator = ',', groupId) { +export function array(name: string, value: string[], separator = ',', groupId?: string) { return manager.knob(name, { type: 'array', value, separator, groupId }); } -export function date(name, value = new Date(), groupId) { +export function date(name: string, value = new Date(), groupId?: string) { const proxyValue = value ? value.getTime() : null; return manager.knob(name, { type: 'date', value: proxyValue, groupId }); } -export function button(name, callback, groupId) { +export function button(name: string, callback: ButtonTypeOnClickProp, groupId?: string) { return manager.knob(name, { type: 'button', callback, hideLabel: true, groupId }); } -export function files(name, accept, value = [], groupId) { +export function files(name: string, accept: string, value: string[] = [], groupId?: string) { return manager.knob(name, { type: 'files', accept, value, groupId }); } -export function optionsKnob(name, valuesObj, value, optionsObj, groupId) { +export function optionsKnob( + name: string, + valuesObj: OptionsTypeOptionsProp, + value: string, + optionsObj: OptionsKnobOptions, + groupId?: string +) { return manager.knob(name, { type: 'options', options: valuesObj, value, optionsObj, groupId }); } diff --git a/addons/knobs/src/registerKnobs.ts b/addons/knobs/src/registerKnobs.ts index 07e3ecf08d73..866c47ac2ed0 100644 --- a/addons/knobs/src/registerKnobs.ts +++ b/addons/knobs/src/registerKnobs.ts @@ -4,6 +4,7 @@ import debounce from 'lodash/debounce'; import KnobManager from './KnobManager'; import { CHANGE, CLICK, RESET, SET } from './shared'; +import { KnobStoreKnob } from './KnobStore'; export const manager = new KnobManager(); const { knobStore } = manager; @@ -13,7 +14,7 @@ function forceReRender() { addons.getChannel().emit(FORCE_RE_RENDER); } -function setPaneKnobs(timestamp = +new Date()) { +function setPaneKnobs(timestamp: boolean | number = +new Date()) { const channel = addons.getChannel(); channel.emit(SET, { knobs: knobStore.getAll(), timestamp }); } @@ -29,7 +30,7 @@ const debouncedResetAndForceUpdate = debounce( COMPONENT_FORCE_RENDER_DEBOUNCE_DELAY_MS ); -function knobChanged(change) { +function knobChanged(change: KnobStoreKnob) { const { name } = change; const { value } = change; // Update the related knob and it's value. const knobOptions = knobStore.get(name); @@ -42,7 +43,7 @@ function knobChanged(change) { } } -function knobClicked(clicked) { +function knobClicked(clicked: KnobStoreKnob) { const knobOptions = knobStore.get(clicked.name); if (knobOptions.callback() !== false) { forceReRender(); diff --git a/yarn.lock b/yarn.lock index 326e5632faba..7d330f657653 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4197,6 +4197,11 @@ "@types/cheerio" "*" "@types/react" "*" +"@types/escape-html@0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@types/escape-html/-/escape-html-0.0.20.tgz#cae698714dd61ebee5ab3f2aeb9a34ba1011735a" + integrity sha512-6dhZJLbA7aOwkYB2GDGdIqJ20wmHnkDzaxV9PJXe7O02I2dSFTERzRB6JrX6cWKaS+VqhhY7cQUMCbO5kloFUw== + "@types/estree@*", "@types/estree@0.0.39": version "0.0.39" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" @@ -4368,13 +4373,27 @@ "@types/history" "*" "@types/react" "*" -"@types/react-dom@^16.8.2": +"@types/react-color@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/react-color/-/react-color-3.0.1.tgz#5433e2f503ea0e0831cbc6fd0c20f8157d93add0" + integrity sha512-J6mYm43Sid9y+OjZ7NDfJ2VVkeeuTPNVImNFITgQNXodHteKfl/t/5pAR5Z9buodZ2tCctsZjgiMlQOpfntakw== + dependencies: + "@types/react" "*" + +"@types/react-dom@*", "@types/react-dom@^16.8.2": version "16.8.4" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.8.4.tgz#7fb7ba368857c7aa0f4e4511c4710ca2c5a12a88" integrity sha512-eIRpEW73DCzPIMaNBDP5pPIpK1KXyZwNgfxiVagb5iGiz6da+9A5hslSX6GAQKdO7SayVCS/Fr2kjqprgAvkfA== dependencies: "@types/react" "*" +"@types/react-lifecycles-compat@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/react-lifecycles-compat/-/react-lifecycles-compat-3.0.1.tgz#a0b1fe18cfb9435bd52737829a69cbe93faf32e2" + integrity sha512-4KiU5s1Go4xRbf7t6VxUUpBeN5PGjpjpBv9VvET4uiPHC500VNYBclU13f8ehHkHoZL39b2cfwHu6RzbV3b44A== + dependencies: + "@types/react" "*" + "@types/react-native@^0.57.57": version "0.57.60" resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.57.60.tgz#61e97a84e2f64ed971e7d238bb30cec188898235" @@ -4393,6 +4412,15 @@ hoist-non-react-statics "^3.3.0" redux "^4.0.0" +"@types/react-select@^2.0.19": + version "2.0.19" + resolved "https://registry.yarnpkg.com/@types/react-select/-/react-select-2.0.19.tgz#59a80ef81a4a5cb37f59970c53a4894d15065199" + integrity sha512-5GGBO3npQ0G/poQmEn+kI3Vn3DoJ9WjRXCeGcpwLxd5rYmjYPH235lbYPX5aclXE2RqEXyFxd96oh0wYwPXYpg== + dependencies: + "@types/react" "*" + "@types/react-dom" "*" + "@types/react-transition-group" "*" + "@types/react-syntax-highlighter@10.1.0": version "10.1.0" resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-10.1.0.tgz#9c534e29bbe05dba9beae1234f3ae944836685d4" @@ -4407,6 +4435,13 @@ dependencies: "@types/react" "*" +"@types/react-transition-group@*": + version "2.9.2" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-2.9.2.tgz#c48cf2a11977c8b4ff539a1c91d259eaa627028d" + integrity sha512-5Fv2DQNO+GpdPZcxp2x/OQG/H19A01WlmpjVD9cKvVFmoVLOZ9LvBgSWG6pSXIU4og5fgbvGPaCV5+VGkWAEHA== + dependencies: + "@types/react" "*" + "@types/react@*", "@types/react@^16.8.14", "@types/react@^16.8.3": version "16.8.18" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.18.tgz#fe66fb748b0b6ca9709d38b87b2d1356d960a511" From 98235fc57fcdd9e9e67ffd694b5e7af99313371a Mon Sep 17 00:00:00 2001 From: Emilio Martinez Date: Tue, 25 Jun 2019 17:33:22 -0700 Subject: [PATCH 5/7] Addon-knobs: migrate deprecated entrypoints to ts --- addons/knobs/angular.js | 1 - addons/knobs/angular.ts | 1 + addons/knobs/html.js | 1 - addons/knobs/html.ts | 1 + addons/knobs/marko.js | 1 - addons/knobs/marko.ts | 1 + addons/knobs/mithril.js | 1 - addons/knobs/mithril.ts | 1 + addons/knobs/polymer.js | 1 - addons/knobs/polymer.ts | 1 + addons/knobs/react.js | 1 - addons/knobs/react.ts | 1 + addons/knobs/src/{deprecated.js => deprecated.ts} | 0 addons/knobs/vue.js | 1 - addons/knobs/vue.ts | 1 + 15 files changed, 7 insertions(+), 7 deletions(-) delete mode 100644 addons/knobs/angular.js create mode 100644 addons/knobs/angular.ts delete mode 100644 addons/knobs/html.js create mode 100644 addons/knobs/html.ts delete mode 100644 addons/knobs/marko.js create mode 100644 addons/knobs/marko.ts delete mode 100644 addons/knobs/mithril.js create mode 100644 addons/knobs/mithril.ts delete mode 100644 addons/knobs/polymer.js create mode 100644 addons/knobs/polymer.ts delete mode 100644 addons/knobs/react.js create mode 100644 addons/knobs/react.ts rename addons/knobs/src/{deprecated.js => deprecated.ts} (100%) delete mode 100644 addons/knobs/vue.js create mode 100644 addons/knobs/vue.ts diff --git a/addons/knobs/angular.js b/addons/knobs/angular.js deleted file mode 100644 index c22c26b6732d..000000000000 --- a/addons/knobs/angular.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./dist/deprecated'); diff --git a/addons/knobs/angular.ts b/addons/knobs/angular.ts new file mode 100644 index 000000000000..f4c9c9e543e8 --- /dev/null +++ b/addons/knobs/angular.ts @@ -0,0 +1 @@ +export * from './dist/deprecated'; diff --git a/addons/knobs/html.js b/addons/knobs/html.js deleted file mode 100644 index c22c26b6732d..000000000000 --- a/addons/knobs/html.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./dist/deprecated'); diff --git a/addons/knobs/html.ts b/addons/knobs/html.ts new file mode 100644 index 000000000000..f4c9c9e543e8 --- /dev/null +++ b/addons/knobs/html.ts @@ -0,0 +1 @@ +export * from './dist/deprecated'; diff --git a/addons/knobs/marko.js b/addons/knobs/marko.js deleted file mode 100644 index c22c26b6732d..000000000000 --- a/addons/knobs/marko.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./dist/deprecated'); diff --git a/addons/knobs/marko.ts b/addons/knobs/marko.ts new file mode 100644 index 000000000000..f4c9c9e543e8 --- /dev/null +++ b/addons/knobs/marko.ts @@ -0,0 +1 @@ +export * from './dist/deprecated'; diff --git a/addons/knobs/mithril.js b/addons/knobs/mithril.js deleted file mode 100644 index c22c26b6732d..000000000000 --- a/addons/knobs/mithril.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./dist/deprecated'); diff --git a/addons/knobs/mithril.ts b/addons/knobs/mithril.ts new file mode 100644 index 000000000000..f4c9c9e543e8 --- /dev/null +++ b/addons/knobs/mithril.ts @@ -0,0 +1 @@ +export * from './dist/deprecated'; diff --git a/addons/knobs/polymer.js b/addons/knobs/polymer.js deleted file mode 100644 index c22c26b6732d..000000000000 --- a/addons/knobs/polymer.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./dist/deprecated'); diff --git a/addons/knobs/polymer.ts b/addons/knobs/polymer.ts new file mode 100644 index 000000000000..f4c9c9e543e8 --- /dev/null +++ b/addons/knobs/polymer.ts @@ -0,0 +1 @@ +export * from './dist/deprecated'; diff --git a/addons/knobs/react.js b/addons/knobs/react.js deleted file mode 100644 index c22c26b6732d..000000000000 --- a/addons/knobs/react.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./dist/deprecated'); diff --git a/addons/knobs/react.ts b/addons/knobs/react.ts new file mode 100644 index 000000000000..f4c9c9e543e8 --- /dev/null +++ b/addons/knobs/react.ts @@ -0,0 +1 @@ +export * from './dist/deprecated'; diff --git a/addons/knobs/src/deprecated.js b/addons/knobs/src/deprecated.ts similarity index 100% rename from addons/knobs/src/deprecated.js rename to addons/knobs/src/deprecated.ts diff --git a/addons/knobs/vue.js b/addons/knobs/vue.js deleted file mode 100644 index c22c26b6732d..000000000000 --- a/addons/knobs/vue.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./dist/deprecated'); diff --git a/addons/knobs/vue.ts b/addons/knobs/vue.ts new file mode 100644 index 000000000000..f4c9c9e543e8 --- /dev/null +++ b/addons/knobs/vue.ts @@ -0,0 +1 @@ +export * from './dist/deprecated'; From d8bbc68adcab0d928815fd19bf35af921ee29124 Mon Sep 17 00:00:00 2001 From: Emilio Martinez Date: Sat, 6 Jul 2019 12:56:52 -0700 Subject: [PATCH 6/7] Addon-knobs: fixes after rebase --- addons/knobs/src/KnobManager.ts | 2 +- addons/knobs/src/KnobStore.ts | 1 + addons/knobs/src/register.tsx | 1 - docs/yarn.lock | 55 +++++++++------------------------ 4 files changed, 17 insertions(+), 42 deletions(-) diff --git a/addons/knobs/src/KnobManager.ts b/addons/knobs/src/KnobManager.ts index 31312e4b13b0..a35a9731baae 100644 --- a/addons/knobs/src/KnobManager.ts +++ b/addons/knobs/src/KnobManager.ts @@ -96,7 +96,7 @@ export default class KnobManager { return this.getKnobValue(existingKnob); } - const knobInfo: Knob & { name: string; defaultValue?: any } = { + const knobInfo: Knob & { name: string; label: string; defaultValue?: any } = { ...options, name: knobName, label: name, diff --git a/addons/knobs/src/KnobStore.ts b/addons/knobs/src/KnobStore.ts index 4abdb288bf01..0ba53a7bc58f 100644 --- a/addons/knobs/src/KnobStore.ts +++ b/addons/knobs/src/KnobStore.ts @@ -33,6 +33,7 @@ export type Knob = export type KnobStoreKnob = Knob & { name: string; + label: string; used?: boolean; defaultValue?: any; hideLabel?: boolean; diff --git a/addons/knobs/src/register.tsx b/addons/knobs/src/register.tsx index 2808815b3958..a6753c557a9d 100644 --- a/addons/knobs/src/register.tsx +++ b/addons/knobs/src/register.tsx @@ -6,7 +6,6 @@ import { ADDON_ID, PANEL_ID, PARAM_KEY } from './shared'; addons.register(ADDON_ID, api => { addons.addPanel(PANEL_ID, { title: 'Knobs', - // eslint-disable-next-line react/prop-types render: ({ active, key }) => , paramKey: PARAM_KEY, }); diff --git a/docs/yarn.lock b/docs/yarn.lock index d29449f476bc..5a6d19e1b3a6 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -261,27 +261,20 @@ dependencies: "@ndhoule/map" "^2.0.1" -"@storybook/client-logger@5.2.0-alpha.34": - version "5.2.0-alpha.34" - resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-5.2.0-alpha.34.tgz#9de93faf736fb58b1f3bd04b500f40dc6231e3d9" - integrity sha512-ukvCH+Fudey0RagrbxbVCy/ZwWfScVDFjGe8yieFCXVLRyfMERuEWgz/WBrgwFIVhzGt4JQgewolv27QLDysNg== +"@storybook/client-logger@5.2.0-alpha.36": + version "5.2.0-alpha.36" + resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-5.2.0-alpha.36.tgz#aff65cf246982176803e751c418d0052f507aa20" + integrity sha512-5gTBxaASFAgXh12m5GX0cvbSrIJWXhfcOuXSD48g6PuHHRup9PaoM9PNwgnB1HolJFYl8CG6jC5I/hnaBQr/QQ== dependencies: core-js "^3.0.1" -"@storybook/client-logger@5.2.0-alpha.35": - version "5.2.0-alpha.35" - resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-5.2.0-alpha.35.tgz#73ddcf2bfa5885407da580b0ffac2bcbe904e260" - integrity sha512-/G/nmW2k4H45nl/cghbmfXraixkFAJd0NrZ5Ch9ZsCuZYYsvMLfasF6Y4FQYykzadMCsdlrpK1dVK2P9YqhyNQ== +"@storybook/components@5.2.0-alpha.36": + version "5.2.0-alpha.36" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-5.2.0-alpha.36.tgz#370e3e593923ce6e5698d560156bd84c4a3c78cd" + integrity sha512-H/Z9wLV/ZJH73Ko1R3Anw4oVodf4z/hcQP0UK3XRIOejv1iI/2ZoRWyIkD/b5IoouHPIXlWkplTkLp2eCFDvPA== dependencies: - core-js "^3.0.1" - -"@storybook/components@5.2.0-alpha.34": - version "5.2.0-alpha.34" - resolved "https://registry.yarnpkg.com/@storybook/components/-/components-5.2.0-alpha.34.tgz#397f146419d6570661d50db6e5638b98803a4b30" - integrity sha512-LStRAgiKSlIouZiK2xtSF430gg42CHD/gwpncdJYQFLY8mWr46Ms0Fzk5YWbXi/xrjFnuX6aQxl7n28CjPmoqg== - dependencies: - "@storybook/client-logger" "5.2.0-alpha.34" - "@storybook/theming" "5.2.0-alpha.34" + "@storybook/client-logger" "5.2.0-alpha.36" + "@storybook/theming" "5.2.0-alpha.36" core-js "^3.0.1" global "^4.3.2" markdown-to-jsx "^6.9.1" @@ -299,32 +292,14 @@ recompose "^0.30.0" simplebar-react "^1.0.0-alpha.6" -"@storybook/theming@5.2.0-alpha.34": - version "5.2.0-alpha.34" - resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-5.2.0-alpha.34.tgz#fa65fa34af6231a5332e2bebd0f4174023f9bb4d" - integrity sha512-F900cTk2JkwrghPr5wFZ95zhAb+Ygp8zKp9MGe0aL4c/2OJrKrNhhA7nvtskdcLg2jJoBXSkwaRqe9rxtzND/g== - dependencies: - "@emotion/core" "^10.0.9" - "@emotion/styled" "^10.0.7" - "@storybook/client-logger" "5.2.0-alpha.34" - common-tags "^1.8.0" - core-js "^3.0.1" - deep-object-diff "^1.1.0" - emotion-theming "^10.0.9" - global "^4.3.2" - memoizerific "^1.11.3" - polished "^3.3.1" - prop-types "^15.7.2" - resolve-from "^5.0.0" - -"@storybook/theming@5.2.0-alpha.35": - version "5.2.0-alpha.35" - resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-5.2.0-alpha.35.tgz#6d97548b7c31e7b50f4e28acbbae8b04cef26111" - integrity sha512-s+6b3BvxPs+LLsU2JIGDEId5+WNd+xkTzyKZAmf5/aS9e6j0p3tSJFiB+7gObt256yyo/SPe9Ke2szQdPfTIUg== +"@storybook/theming@5.2.0-alpha.36": + version "5.2.0-alpha.36" + resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-5.2.0-alpha.36.tgz#52c963d9697740762ef1f5e14edae30885a8fce5" + integrity sha512-nDLK6j0rR2lmNuOsvySuv2QsCbLf+u/CWsSHcIAajGIQWyVqoXAHf/bbDOPNxUPIWMj9noZ2zOxakVQ69Ksl3w== dependencies: "@emotion/core" "^10.0.9" "@emotion/styled" "^10.0.7" - "@storybook/client-logger" "5.2.0-alpha.35" + "@storybook/client-logger" "5.2.0-alpha.36" common-tags "^1.8.0" core-js "^3.0.1" deep-object-diff "^1.1.0" From 7d1fe63a4d1c88b0d60f60fffcb4657302ada704 Mon Sep 17 00:00:00 2001 From: Emilio Martinez Date: Sun, 7 Jul 2019 17:22:04 -0700 Subject: [PATCH 7/7] Addon-knobs: add typings.d.ts to remove ts-ignore usage --- addons/knobs/src/KnobManager.ts | 5 +---- addons/knobs/src/components/Panel.tsx | 1 - addons/knobs/src/components/types/Color.tsx | 1 - addons/knobs/src/components/types/Files.tsx | 3 +-- addons/knobs/src/typings.d.ts | 2 ++ addons/knobs/tsconfig.json | 8 ++------ 6 files changed, 6 insertions(+), 14 deletions(-) create mode 100644 addons/knobs/src/typings.d.ts diff --git a/addons/knobs/src/KnobManager.ts b/addons/knobs/src/KnobManager.ts index a35a9731baae..60bbfb0be888 100644 --- a/addons/knobs/src/KnobManager.ts +++ b/addons/knobs/src/KnobManager.ts @@ -1,15 +1,12 @@ /* eslint no-underscore-dangle: 0 */ -// @ts-ignore import { navigator } from 'global'; import escape from 'escape-html'; -// TODO: remove ts-ignore once client-api is typed -// @ts-ignore import { getQueryParams } from '@storybook/client-api'; // eslint-disable-next-line import/no-extraneous-dependencies import { Channel } from '@storybook/channels'; -import KnobStore, { Knob, KnobStoreKnob } from './KnobStore'; +import KnobStore, { Knob } from './KnobStore'; import { SET } from './shared'; import { deserializers } from './converters'; diff --git a/addons/knobs/src/components/Panel.tsx b/addons/knobs/src/components/Panel.tsx index aae80957d7be..a5bbb2d8daf6 100644 --- a/addons/knobs/src/components/Panel.tsx +++ b/addons/knobs/src/components/Panel.tsx @@ -1,7 +1,6 @@ import React, { PureComponent, Fragment, ComponentType } from 'react'; import PropTypes from 'prop-types'; import qs from 'qs'; -// @ts-ignore import { document } from 'global'; import { styled } from '@storybook/theming'; import copy from 'copy-to-clipboard'; diff --git a/addons/knobs/src/components/types/Color.tsx b/addons/knobs/src/components/types/Color.tsx index 5c4a0afaf87a..45121177a256 100644 --- a/addons/knobs/src/components/types/Color.tsx +++ b/addons/knobs/src/components/types/Color.tsx @@ -1,4 +1,3 @@ -// @ts-ignore import { document } from 'global'; import PropTypes from 'prop-types'; import React, { Component, WeakValidationMap } from 'react'; diff --git a/addons/knobs/src/components/types/Files.tsx b/addons/knobs/src/components/types/Files.tsx index 34d345f97be1..762bdd67d938 100644 --- a/addons/knobs/src/components/types/Files.tsx +++ b/addons/knobs/src/components/types/Files.tsx @@ -1,4 +1,3 @@ -// @ts-ignore import { FileReader } from 'global'; import PropTypes from 'prop-types'; import React, { ChangeEvent, FunctionComponent } from 'react'; @@ -26,7 +25,7 @@ const FileInput = styled(Form.Input)({ function fileReaderPromise(file: File) { return new Promise(resolve => { const fileReader = new FileReader(); - fileReader.onload = (e: Event) => resolve((e.currentTarget as FileReader).result); + fileReader.onload = (e: Event) => resolve((e.currentTarget as FileReader).result as string); fileReader.readAsDataURL(file); }); } diff --git a/addons/knobs/src/typings.d.ts b/addons/knobs/src/typings.d.ts new file mode 100644 index 000000000000..b0b788b35a7e --- /dev/null +++ b/addons/knobs/src/typings.d.ts @@ -0,0 +1,2 @@ +declare module 'global'; +declare module '@storybook/client-api'; diff --git a/addons/knobs/tsconfig.json b/addons/knobs/tsconfig.json index 8876bb6737a1..82ce44329cc6 100644 --- a/addons/knobs/tsconfig.json +++ b/addons/knobs/tsconfig.json @@ -4,10 +4,6 @@ "rootDir": "./src", "types": ["webpack-env"] }, - "include": [ - "src/**/*" - ], - "exclude": [ - "src/__tests__/**/*" - ] + "include": ["src/**/*"], + "exclude": ["src/__tests__/**/*"] }