Open gloriaJun opened 3 years ago
storybook v6.3.x
// .storybook/decorators/RouterProvider.tsx import React from 'react'; import { Route, Router } from 'react-router'; // import { createMemoryHistory } from 'history'; import { StoryContext, StoryGetter } from '@storybook/addons'; import { HistoryUtility } from '@utils'; import { history } from '@lib/history'; type IRouteInfo = { path: string; pathname: string; search: Record<string, string>; hash: Record<string, string>; state: Record<string, unknown>; }; export function RouteProvider(Story: StoryGetter, context: StoryContext) { const { route } = context.parameters as { route: IRouteInfo; }; const { path = '/', pathname = '/', search, additionalSearch, hash, additionalHash, state } = (route ? { ...route, search: HistoryUtility.generateQueryParam(route.search), additionalSearch: route.search, hash: HistoryUtility.generateHashParam(route.hash), additionalHash: route.hash, } : {}) as Omit<IRouteInfo, 'search' | 'hash'> & { search: string; additionalSearch: Record<string, string>; hash: string; additionalHash: Record<string, string>; }; const { search: documentLocationSearch } = HistoryUtility.parseLocation() as { search: Record<string, string>; hash: Record<string, string>; }; const currentSearch = Object.entries(documentLocationSearch).reduce( (result: Record<string, string>, [key, value]) => { if (['id', 'viewMode'].includes(key)) { result[key] = value; } return result; }, {} ); const newLocation = new URL(document.location.href); newLocation.search = HistoryUtility.generateQueryParam({ ...currentSearch, ...additionalSearch }); newLocation.hash = HistoryUtility.generateHashParam(additionalHash); // replace the location information global.history.replaceState({}, document.title, newLocation.toString()); history.replace({ pathname, search, hash, state, }); return ( <Router history={history}> <Route path={path} // @ts-ignore component={() => Story(context)} /> </Router> ); }
import { createBrowserHistory, createMemoryHistory } from 'history'; import * as DevUtility from '@utils/dev'; import { ILocationState } from '@lib/history'; import { logger } from '@utils/logger'; import * as ObjectUtility from '@utils/object'; type IUrl = { pathname: string; search?: string; searchParams?: Record<string, string>; hash?: string; hashParams?: Record<string, string>; }; export const history = DevUtility.isTestEnv() ? createMemoryHistory<ILocationState>() : createBrowserHistory<ILocationState>({ basename: '/' }); export const parseParam = (str: string | null) => { if (ObjectUtility.isEmptyString(str)) { return {}; } const params = str.replace(new RegExp(`^(\\?|#)`), '').split('&'); return params.reduce((result: Record<string, string>, param) => { const pair = param.split('='); result[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || ''); return result; }, {}); }; export const parseLocation = () => { const { search, hash } = window.location; const { state } = history.location; return { search: parseParam(search), hash: parseParam(hash), state: state as Required<ILocationState>, }; }; export const parseUrl = (url?: string | null): IUrl => { if (ObjectUtility.isEmptyString(url)) { return {} as IUrl; } const parser = document.createElement('a'); // parser.href = 'http://example.com:3000/pathname/?search=test#hash&name=username'; parser.href = url; return { pathname: parser.pathname, search: parser.search, searchParams: parseParam(parser.search), hash: parser.hash, hashParams: parseParam(parser.hash), }; }; export const generateQueryParam = (obj?: Record<string, string | boolean | number> | null, del = '?') => { if (ObjectUtility.isNullOrUndefined(obj) || ObjectUtility.isEmpty(obj)) { return ''; } if (!(obj instanceof Object)) { logger.error(null, 'can not generate parameter', obj); return obj; } const queryParam = Object.keys(obj).reduce((result: string[], key) => { if (obj[key]) { result.push(`${key}=${encodeURIComponent(obj[key].toString())}`); } return result; }, []); return queryParam.length > 0 ? `${del}${queryParam.join('&')}` : ''; }; export const generateHashParam = (obj?: Record<string, string | boolean | number> | null) => { return generateQueryParam(obj, '#'); };
// .storybook/preview.js import { StoryLayout, PlayUserEventProvider, StoreProvider, RouteProvider } from './decorators'; export const decorators = [StoreProvider, RouteProvider, PlayUserEventProvider, StoryLayout];
export const RecentTransferList = Template.bind({}); RecentTransferList.parameters = { route: { path: '/user/:userId', pathname: '/user/username', search: { search: 'search' }, hash: { hash: 'hash' }, state: { state: 'state' }, }, };
const params = useParams(); const location = useLocation(); console.log(params); // { userId: "username" } console.log(location.search); // ?search=search console.log(window.location.search); // ?search=search console.log(location.hash); // ?hash=hash console.log(window.location.hash); // #hash=hash console.log(location.state); // { state: "state" }
Version
storybook v6.3.x
Create the provider
RouterProvider
Register RouterProvider
Usage
story
component