storybook/addons/links/src/preview.ts
Norbert de Langen 4e52c534d3
FIX linting
2020-02-24 14:18:59 +01:00

122 lines
3.1 KiB
TypeScript

import {
document,
HTMLElement,
__STORYBOOK_STORY_STORE__ as storyStore,
__STORYBOOK_CLIENT_API__ as clientApi,
} from 'global';
import qs from 'qs';
import addons, { makeDecorator } from '@storybook/addons';
import { STORY_CHANGED, SELECT_STORY } from '@storybook/core-events';
import { toId } from '@storybook/csf';
import { logger } from '@storybook/client-logger';
import { PARAM_KEY } from './constants';
interface ParamsId {
storyId: string;
}
interface ParamsCombo {
kind: string;
story: string;
}
export const navigate = (params: ParamsId | ParamsCombo) =>
addons.getChannel().emit(SELECT_STORY, params);
const generateUrl = (id: string) => {
const { location } = document;
const query = qs.parse(location.search, { ignoreQueryPrefix: true });
return `${location.origin + location.pathname}?${qs.stringify(
{ ...query, id },
{ encode: false }
)}`;
};
const valueOrCall = (args: string[]) => (value: string | ((...args: string[]) => string)) =>
typeof value === 'function' ? value(...args) : value;
export const linkTo = (
idOrKindInput: string,
storyInput?: string | ((...args: any[]) => string)
) => (...args: any[]) => {
const resolver = valueOrCall(args);
const { storyId } = storyStore.getSelection();
const current = storyStore.fromId(storyId) || {};
const kindVal = resolver(idOrKindInput);
const storyVal = resolver(storyInput);
const fromid = storyStore.fromId(kindVal);
const item =
fromid ||
clientApi.raw().find((i: any) => {
if (kindVal && storyVal) {
return i.kind === kindVal && i.story === storyVal;
}
if (!kindVal && storyVal) {
return i.kind === current.kind && i.story === storyVal;
}
if (kindVal && !storyVal) {
return i.kind === kindVal;
}
if (!kindVal && !storyVal) {
return i.kind === current.kind;
}
return false;
});
if (item) {
navigate({
kind: item.kind,
story: item.story,
});
} else {
logger.error('could not navigate to provided story');
}
};
export const hrefTo = (kind: string, name: string): Promise<string> => {
return new Promise(resolve => {
const { storyId } = storyStore.getSelection();
const current = storyStore.fromId(storyId);
resolve(generateUrl(toId(kind || current.kind, name)));
});
};
const linksListener = (e: Event) => {
const { target } = e;
if (!(target instanceof HTMLElement)) {
return;
}
const element = target as HTMLElement;
const { sbKind: kind, sbStory: story } = element.dataset;
if (kind || story) {
e.preventDefault();
navigate({ kind, story });
}
};
let hasListener = false;
const on = () => {
if (!hasListener) {
hasListener = true;
document.addEventListener('click', linksListener);
}
};
const off = () => {
if (hasListener) {
hasListener = false;
document.removeEventListener('click', linksListener);
}
};
export const withLinks = makeDecorator({
name: 'withLinks',
parameterName: PARAM_KEY,
wrapper: (getStory, context, { parameters }) => {
on();
addons.getChannel().once(STORY_CHANGED, off);
return getStory(context);
},
});