Skip to content

Commit

Permalink
Addon-knobs: migrate registration, manager and panels to ts
Browse files Browse the repository at this point in the history
  • Loading branch information
emilio-martinez committed Jun 26, 2019
1 parent 23873e5 commit bed1ae7
Show file tree
Hide file tree
Showing 23 changed files with 416 additions and 189 deletions.
1 change: 1 addition & 0 deletions addons/knobs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
59 changes: 39 additions & 20 deletions addons/knobs/src/KnobManager.ts
Original file line number Diff line number Diff line change
@@ -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<string, string> = 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);
}
Expand All @@ -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 { knobStore } = this;
Expand All @@ -75,7 +94,7 @@ export default class KnobManager {
return this.getKnobValue(existingKnob);
}

const knobInfo = {
const knobInfo: Knob & { name: string; defaultValue?: any } = {
...options,
name,
};
Expand Down
72 changes: 58 additions & 14 deletions addons/knobs/src/KnobStore.ts
Original file line number Diff line number Diff line change
@@ -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<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 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<string, KnobStoreKnob> = {};

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) {
Expand All @@ -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;
Expand All @@ -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);
}
Expand Down
Loading

0 comments on commit bed1ae7

Please sign in to comment.