Skip to content

Commit

Permalink
Addon-knobs: organize type definitions a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
emilio-martinez committed Jul 12, 2019
1 parent a740977 commit 20ef9e5
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 60 deletions.
3 changes: 2 additions & 1 deletion addons/knobs/src/KnobManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { getQueryParams } from '@storybook/client-api';
// eslint-disable-next-line import/no-extraneous-dependencies
import { Channel } from '@storybook/channels';

import KnobStore, { Knob, KnobStoreKnob, KnobType } from './KnobStore';
import KnobStore, { KnobStoreKnob } from './KnobStore';
import { Knob, KnobType } from './type-defs';
import { SET } from './shared';

import { deserializers } from './converters';
Expand Down
45 changes: 1 addition & 44 deletions addons/knobs/src/KnobStore.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,7 @@
import Types, {
TextTypeKnob,
NumberTypeKnob,
ColorTypeKnob,
BooleanTypeKnob,
ObjectTypeKnob,
SelectTypeKnob,
RadiosTypeKnob,
ArrayTypeKnob,
DateTypeKnob,
ButtonTypeOnClickProp,
FileTypeKnob,
OptionsTypeKnob,
} from './components/types';
import { Knob } from './type-defs';

type Callback = () => any;

export type KnobType = keyof typeof Types;

type KnobPlus<T extends KnobType, K> = K & { type: T; groupId?: string };

export type Knob<T extends KnobType = any> = T extends 'text'
? KnobPlus<T, Pick<TextTypeKnob, 'value'>>
: T extends 'boolean'
? KnobPlus<T, Pick<BooleanTypeKnob, 'value'>>
: T extends 'number'
? KnobPlus<T, Pick<NumberTypeKnob, 'value' | 'range' | 'min' | 'max' | 'step'>>
: T extends 'color'
? KnobPlus<T, Pick<ColorTypeKnob, 'value'>>
: T extends 'object'
? KnobPlus<T, Pick<ObjectTypeKnob<any>, 'value'>>
: T extends 'select'
? KnobPlus<T, Pick<SelectTypeKnob, 'value' | 'options'> & { selectV2: true }>
: T extends 'radios'
? KnobPlus<T, Pick<RadiosTypeKnob, 'value' | 'options'>>
: T extends 'array'
? KnobPlus<T, Pick<ArrayTypeKnob, 'value' | 'separator'>>
: T extends 'date'
? KnobPlus<T, Pick<DateTypeKnob, 'value'>>
: T extends 'files'
? KnobPlus<T, Pick<FileTypeKnob, 'value' | 'accept'>>
: T extends 'button'
? KnobPlus<T, { value?: never; callback: ButtonTypeOnClickProp; hideLabel: true }>
: T extends 'options'
? KnobPlus<T, Pick<OptionsTypeKnob<any>, 'options' | 'value' | 'optionsObj'>>
: never;

export type KnobStoreKnob = Knob & {
name: string;
label: string;
Expand Down
17 changes: 6 additions & 11 deletions addons/knobs/src/components/Panel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { PureComponent, Fragment, ComponentType } from 'react';
import React, { PureComponent, Fragment } from 'react';
import PropTypes from 'prop-types';
import qs from 'qs';
import { document } from 'global';
Expand All @@ -16,7 +16,7 @@ import {
} from '@storybook/components';
import { RESET, SET, CHANGE, SET_OPTIONS, CLICK } from '../shared';

import Types from './types';
import { getKnobControl } from './types';
import PropForm from './PropForm';
import { KnobStoreKnob } from '../KnobStore';

Expand Down Expand Up @@ -58,11 +58,6 @@ interface KnobPanelOptions {
timestamps?: boolean;
}

type KnobControlType = ComponentType<any> & {
serialize: (v: any) => any;
deserialize: (v: any) => any;
};

export default class KnobPanel extends PureComponent<KnobPanelProps> {
static propTypes = {
active: PropTypes.bool.isRequired,
Expand Down Expand Up @@ -133,9 +128,9 @@ export default class KnobPanel extends PureComponent<KnobPanelProps> {

// If the knob value present in url
if (urlValue !== undefined) {
const value = (Types[knob.type] as KnobControlType).deserialize(urlValue);
const value = getKnobControl(knob.type).deserialize(urlValue);
knob.value = value;
queryParams[`knob-${name}`] = (Types[knob.type] as KnobControlType).serialize(value);
queryParams[`knob-${name}`] = getKnobControl(knob.type).serialize(value);

api.emit(CHANGE, knob);
}
Expand All @@ -161,7 +156,7 @@ export default class KnobPanel extends PureComponent<KnobPanelProps> {
const { knobs } = this.state;

Object.entries(knobs).forEach(([name, knob]) => {
query[`knob-${name}`] = (Types[knob.type] as KnobControlType).serialize(knob.value);
query[`knob-${name}`] = getKnobControl(knob.type).serialize(knob.value);
});

copy(`${location.origin + location.pathname}?${qs.stringify(query, { encode: false })}`);
Expand Down Expand Up @@ -193,7 +188,7 @@ export default class KnobPanel extends PureComponent<KnobPanelProps> {

Object.keys(newKnobs).forEach(n => {
const knob = newKnobs[n];
queryParams[`knob-${n}`] = (Types[knob.type] as KnobControlType).serialize(knob.value);
queryParams[`knob-${n}`] = getKnobControl(knob.type).serialize(knob.value);
});

api.setQueryParams(queryParams);
Expand Down
4 changes: 2 additions & 2 deletions addons/knobs/src/components/PropForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { Component, WeakValidationMap, ComponentType, Requireable } from
import PropTypes from 'prop-types';

import { Form } from '@storybook/components';
import TypeMap from './types';
import { getKnobControl } from './types';
import { KnobStoreKnob } from '../KnobStore';

interface PropFormProps {
Expand Down Expand Up @@ -48,7 +48,7 @@ export default class PropForm extends Component<PropFormProps> {
<Form>
{knobs.map(knob => {
const changeHandler = this.makeChangeHandler(knob.name, knob.type);
const InputType: ComponentType<any> = TypeMap[knob.type] || InvalidType;
const InputType: ComponentType<any> = getKnobControl(knob.type) || InvalidType;

return (
<Form.Field key={knob.name} label={!knob.hideLabel && `${knob.label || knob.name}`}>
Expand Down
15 changes: 14 additions & 1 deletion addons/knobs/src/components/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ComponentType } from 'react';

import TextType from './Text';
import NumberType from './Number';
import ColorType from './Color';
Expand All @@ -11,7 +13,7 @@ import ButtonType from './Button';
import FilesType from './Files';
import OptionsType from './Options';

export default {
const KnobControls = {
text: TextType,
number: NumberType,
color: ColorType,
Expand All @@ -25,6 +27,17 @@ export default {
files: FilesType,
options: OptionsType,
};
export default KnobControls;

export type KnobType = keyof typeof KnobControls;

export type KnobControlType = ComponentType<any> & {
serialize: (v: any) => any;
deserialize: (v: any) => any;
};

// Note: this is a utility function that helps in resolving types more orderly
export const getKnobControl = (type: KnobType): KnobControlType => KnobControls[type];

export { TextTypeKnob } from './Text';
export { NumberTypeKnob, NumberTypeKnobOptions } from './Number';
Expand Down
2 changes: 1 addition & 1 deletion addons/knobs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import addons, { makeDecorator } from '@storybook/addons';

import { SET_OPTIONS } from './shared';
import { manager, registerKnobs } from './registerKnobs';
import { Knob, KnobType } from './KnobStore';
import { Knob, KnobType } from './type-defs';
import {
NumberTypeKnobOptions,
ButtonTypeOnClickProp,
Expand Down
45 changes: 45 additions & 0 deletions addons/knobs/src/type-defs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
TextTypeKnob,
NumberTypeKnob,
ColorTypeKnob,
BooleanTypeKnob,
ObjectTypeKnob,
SelectTypeKnob,
RadiosTypeKnob,
ArrayTypeKnob,
DateTypeKnob,
ButtonTypeOnClickProp,
FileTypeKnob,
OptionsTypeKnob,
KnobType,
} from './components/types';

type KnobPlus<T extends KnobType, K> = K & { type: T; groupId?: string };

export type Knob<T extends KnobType = any> = T extends 'text'
? KnobPlus<T, Pick<TextTypeKnob, 'value'>>
: T extends 'boolean'
? KnobPlus<T, Pick<BooleanTypeKnob, 'value'>>
: T extends 'number'
? KnobPlus<T, Pick<NumberTypeKnob, 'value' | 'range' | 'min' | 'max' | 'step'>>
: T extends 'color'
? KnobPlus<T, Pick<ColorTypeKnob, 'value'>>
: T extends 'object'
? KnobPlus<T, Pick<ObjectTypeKnob<any>, 'value'>>
: T extends 'select'
? KnobPlus<T, Pick<SelectTypeKnob, 'value' | 'options'> & { selectV2: true }>
: T extends 'radios'
? KnobPlus<T, Pick<RadiosTypeKnob, 'value' | 'options'>>
: T extends 'array'
? KnobPlus<T, Pick<ArrayTypeKnob, 'value' | 'separator'>>
: T extends 'date'
? KnobPlus<T, Pick<DateTypeKnob, 'value'>>
: T extends 'files'
? KnobPlus<T, Pick<FileTypeKnob, 'value' | 'accept'>>
: T extends 'button'
? KnobPlus<T, { value?: never; callback: ButtonTypeOnClickProp; hideLabel: true }>
: T extends 'options'
? KnobPlus<T, Pick<OptionsTypeKnob<any>, 'options' | 'value' | 'optionsObj'>>
: never;

export { KnobType };

0 comments on commit 20ef9e5

Please sign in to comment.