Skip to content

Commit

Permalink
Addon-knobs: fix knobs function return types
Browse files Browse the repository at this point in the history
Applies generics and conditional types to ensure a proper return value from each of the knob functions respectively.
  • Loading branch information
emilio-martinez committed Jul 11, 2019
1 parent 3876f2f commit a740977
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 24 deletions.
8 changes: 4 additions & 4 deletions addons/knobs/src/KnobManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getQueryParams } from '@storybook/client-api';
// eslint-disable-next-line import/no-extraneous-dependencies
import { Channel } from '@storybook/channels';

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

import { deserializers } from './converters';
Expand Down Expand Up @@ -71,7 +71,7 @@ export default class KnobManager {
return this.options.escapeHTML ? escapeStrings(value) : value;
}

knob(name: string, options: Knob) {
knob<T extends KnobType = any>(name: string, options: Knob<T>): Knob<T>['value'] {
this._mayCallChannel();

const knobName = options.groupId ? `${name}_${options.groupId}` : name;
Expand All @@ -93,7 +93,7 @@ export default class KnobManager {
return this.getKnobValue(existingKnob);
}

const knobInfo: Knob & { name: string; label: string; defaultValue?: any } = {
const knobInfo: Knob<T> & { name: string; label: string; defaultValue?: any } = {
...options,
name: knobName,
label: name,
Expand All @@ -110,7 +110,7 @@ export default class KnobManager {
knobInfo.defaultValue = options.value;
}

knobStore.set(knobName, knobInfo);
knobStore.set(knobName, knobInfo as KnobStoreKnob);
return this.getKnobValue(knobStore.get(knobName));
}

Expand Down
44 changes: 29 additions & 15 deletions addons/knobs/src/KnobStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,35 @@ import Types, {

type Callback = () => any;

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

export type Knob =
| KnobPlus<'text', Pick<TextTypeKnob, 'value'>>
| KnobPlus<'boolean', Pick<BooleanTypeKnob, 'value'>>
| KnobPlus<'number', Pick<NumberTypeKnob, 'value' | 'range' | 'min' | 'max' | 'step'>>
| KnobPlus<'color', Pick<ColorTypeKnob, 'value'>>
| KnobPlus<'object', Pick<ObjectTypeKnob<any>, 'value'>>
| KnobPlus<'select', Pick<SelectTypeKnob, 'value' | 'options'> & { selectV2: true }>
| KnobPlus<'radios', Pick<RadiosTypeKnob, 'value' | 'options'>>
| KnobPlus<'array', Pick<ArrayTypeKnob, 'value' | 'separator'>>
| KnobPlus<'date', Pick<DateTypeKnob, 'value'>>
| KnobPlus<'files', Pick<FileTypeKnob, 'value' | 'accept'>>
| KnobPlus<'button', { value?: unknown; callback: ButtonTypeOnClickProp; hideLabel: true }>
| KnobPlus<'options', Pick<OptionsTypeKnob<any>, 'options' | 'value' | 'optionsObj'>>;
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;
Expand Down
11 changes: 6 additions & 5 deletions addons/knobs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import addons, { makeDecorator } from '@storybook/addons';

import { SET_OPTIONS } from './shared';
import { manager, registerKnobs } from './registerKnobs';
import { Knob, KnobType } from './KnobStore';
import {
NumberTypeKnobOptions,
ButtonTypeOnClickProp,
Expand All @@ -12,8 +13,8 @@ import {
OptionsKnobOptions,
} from './components/types';

export function knob(name: string, optionsParam: any) {
return manager.knob(name, optionsParam);
export function knob<T extends KnobType>(name: string, options: Knob<T>) {
return manager.knob(name, options);
}

export function text(name: string, value: string, groupId?: string) {
Expand Down Expand Up @@ -57,7 +58,7 @@ export function color(name: string, value: string, groupId?: string) {
return manager.knob(name, { type: 'color', value, groupId });
}

export function object<T>(name: string, value: T, groupId?: string) {
export function object<T>(name: string, value: T, groupId?: string): T {
return manager.knob(name, { type: 'object', value, groupId });
}

Expand Down Expand Up @@ -99,10 +100,10 @@ export function files(name: string, accept: string, value: string[] = [], groupI
export function optionsKnob<T>(
name: string,
valuesObj: OptionsTypeOptionsProp<T>,
value: string,
value: T,
optionsObj: OptionsKnobOptions,
groupId?: string
) {
): T {
return manager.knob(name, { type: 'options', options: valuesObj, value, optionsObj, groupId });
}

Expand Down

0 comments on commit a740977

Please sign in to comment.