mirror of
https://github.com/storybookjs/storybook.git
synced 2025-03-31 05:03:21 +08:00
Merge pull request #10040 from storybookjs/tech/use-getdata
Tech/improvements extracted from composition
This commit is contained in:
commit
6ba9b32221
@ -64,7 +64,7 @@ const getSelectedBackgroundColor = (list: Input[], currentSelectedValue: string)
|
||||
};
|
||||
|
||||
const mapper = ({ api, state }: Combo): { items: Input[]; selected: string | null } => {
|
||||
const story = state.storiesHash[state.storyId];
|
||||
const story = api.getData(state.storyId);
|
||||
const list = story ? api.getParameters(story.id, PARAM_KEY) : [];
|
||||
const selected = state.addons[PARAM_KEY] || null;
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { ChangeEvent, Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { polyfill } from 'react-lifecycles-compat';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
@ -109,19 +108,7 @@ interface ItemState {
|
||||
}
|
||||
|
||||
class Item extends Component<ItemProps, ItemState> {
|
||||
static propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
onEmit: PropTypes.func.isRequired,
|
||||
// eslint-disable-next-line react/forbid-prop-types
|
||||
payload: PropTypes.any,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
payload: {},
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps = ({ payload }: ItemProps, { prevPayload }: ItemState) => {
|
||||
static getDerivedStateFromProps = ({ payload = {} }: ItemProps, { prevPayload }: ItemState) => {
|
||||
if (!isEqual(payload, prevPayload)) {
|
||||
const payloadString = json.plain(payload);
|
||||
const refinedPayload = getJSONFromString(payloadString);
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { styled } from '@storybook/theming';
|
||||
import { API } from '@storybook/api';
|
||||
@ -24,15 +23,6 @@ interface EventsPanelState {
|
||||
}
|
||||
|
||||
export default class EventsPanel extends Component<EventsPanelProps, EventsPanelState> {
|
||||
static propTypes = {
|
||||
active: PropTypes.bool.isRequired,
|
||||
api: PropTypes.shape({
|
||||
emit: PropTypes.func,
|
||||
off: PropTypes.func,
|
||||
on: PropTypes.func,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
state: EventsPanelState = {
|
||||
events: [],
|
||||
};
|
||||
|
@ -1,10 +1,6 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { style } from './style';
|
||||
|
||||
export const FullScreen: FunctionComponent = ({ children }) => {
|
||||
return <div style={style.wrapper}>{children}</div>;
|
||||
};
|
||||
|
||||
FullScreen.defaultProps = { children: null };
|
||||
FullScreen.propTypes = { children: PropTypes.node };
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import GraphiQL from 'graphiql';
|
||||
import 'graphiql/graphiql.css';
|
||||
|
||||
@ -16,7 +15,7 @@ const GQL: FunctionComponent<GQLProps> = ({ active }) => {
|
||||
return active ? (
|
||||
<Consumer>
|
||||
{({ api, state }: Combo) => {
|
||||
const story = state.storiesHash[state.storyId];
|
||||
const story = api.getData(state.storyId);
|
||||
const parameters = story ? api.getParameters(story.id, PARAM_KEY) : null;
|
||||
|
||||
if (parameters) {
|
||||
@ -32,8 +31,5 @@ const GQL: FunctionComponent<GQLProps> = ({ active }) => {
|
||||
</Consumer>
|
||||
) : null;
|
||||
};
|
||||
GQL.propTypes = {
|
||||
active: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default GQL;
|
||||
|
@ -5,7 +5,6 @@ import React, {
|
||||
FunctionComponent,
|
||||
HTMLAttributes,
|
||||
} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
type MainProps = Omit<DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>, 'style'>;
|
||||
const Main: FunctionComponent<MainProps> = props => (
|
||||
@ -25,12 +24,6 @@ type TitleProps = DetailedHTMLProps<HTMLAttributes<HTMLHeadingElement>, HTMLHead
|
||||
const Title: FunctionComponent<TitleProps> = ({ children, ...props }) => (
|
||||
<h1 {...props}>{children}</h1>
|
||||
);
|
||||
Title.propTypes = {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
Title.defaultProps = {
|
||||
children: undefined,
|
||||
};
|
||||
|
||||
type NoteProps = Omit<
|
||||
DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>,
|
||||
@ -85,14 +78,6 @@ const Link: FunctionComponent<LinkProps> = ({ children, href, target, rel, ...pr
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
Link.propTypes = {
|
||||
href: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
};
|
||||
Link.defaultProps = {
|
||||
href: undefined,
|
||||
children: undefined,
|
||||
};
|
||||
|
||||
type NavButtonProps = Omit<
|
||||
DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>,
|
||||
@ -120,12 +105,6 @@ const NavButton: FunctionComponent<NavButtonProps> = ({ children, onClick, ...pr
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
NavButton.propTypes = {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
NavButton.defaultProps = {
|
||||
children: undefined,
|
||||
};
|
||||
|
||||
type WelcomeProps = {
|
||||
showApp: () => void;
|
||||
@ -176,8 +155,5 @@ const Welcome: FunctionComponent<WelcomeProps> = ({ showApp }) => (
|
||||
</Main>
|
||||
);
|
||||
Welcome.displayName = 'Welcome';
|
||||
Welcome.defaultProps = {
|
||||
showApp: null,
|
||||
};
|
||||
|
||||
export { Welcome as default };
|
||||
|
@ -1 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="132.948" height="37.739"><path fill="#currentColor" d="M31.314 14.575c.36 0 .48.13.48.48v7.92h4.07c.35 0 .48.13.48.48v.7c0 .35-.13.48-.48.48h-5.36c-.35 0-.49-.13-.49-.48v-9.1c0-.35.14-.48.49-.48zM39.894 18.065c0-2.31 1.46-3.7 4-3.7s4 1.39 4 3.7v3.08c0 2.31-1.47 3.7-4 3.7s-4-1.39-4-3.7zm6.32 0c0-1.3-.81-2-2.27-2s-2.26.71-2.26 2v3.1c0 1.31.8 2 2.26 2s2.27-.72 2.27-2zM59.804 19.125c.39 0 .48.13.48.48v1.9a3 3 0 01-1.06 2.36 4.36 4.36 0 01-3 .94c-2.59 0-4-1.39-4-3.7v-3.1c0-2.28 1.46-3.68 4-3.68 2 0 3.3.79 3.85 2.37a.43.43 0 01-.3.62l-.78.27c-.34.12-.48.05-.6-.3a2.06 2.06 0 00-2.17-1.3c-1.47 0-2.27.71-2.27 2v3.18c0 1.31.81 2 2.27 2s2.36-.67 2.36-1.65v-.74h-2.17c-.36 0-.49-.14-.49-.49v-.68c0-.35.13-.48.49-.48zM64.414 18.065c0-2.31 1.46-3.7 4-3.7s4 1.39 4 3.7v3.08c0 2.31-1.47 3.7-4 3.7s-4-1.39-4-3.7zm6.31 0c0-1.3-.81-2-2.26-2s-2.27.71-2.27 2v3.1c0 1.31.81 2 2.27 2s2.26-.72 2.26-2zM77.124 14.865c0-.22.07-.29.29-.29h.46c.22 0 .27.07.27.29v9.48c0 .22 0 .29-.27.29h-.46c-.22 0-.29-.07-.29-.29zM87.254 14.575a3.14 3.14 0 110 6.28h-3v3.49c0 .22-.05.29-.26.29h-.47c-.21 0-.29-.07-.29-.29v-9.48c0-.22.08-.29.29-.29zm-.09 5.29a2.18 2.18 0 100-4.36h-3v4.36zM98.204 14.375a3.61 3.61 0 013.72 2.18c.08.16 0 .29-.16.37l-.44.2c-.18.07-.25.06-.36-.13a2.72 2.72 0 00-2.76-1.64c-1.69 0-2.61.67-2.61 1.87a1.52 1.52 0 001.27 1.54 6.79 6.79 0 001.66.32 6.88 6.88 0 012 .41 2.25 2.25 0 011.56 2.37c0 1.87-1.36 3-3.86 3a3.61 3.61 0 01-3.83-2.43.27.27 0 01.17-.38l.44-.16a.27.27 0 01.36.17 2.86 2.86 0 002.86 1.8c1.89 0 2.82-.66 2.82-2a1.49 1.49 0 00-1.17-1.53 7 7 0 00-1.59-.28l-1.08-.14a9.5 9.5 0 01-1-.27 2.63 2.63 0 01-.89-.47 2.44 2.44 0 01-.8-1.91c.07-1.75 1.38-2.89 3.69-2.89zM107.474 21.215a2.78 2.78 0 005.55 0v-6.35c0-.22.07-.29.29-.29h.46c.22 0 .29.07.29.29v6.34c0 2.27-1.34 3.64-3.81 3.64s-3.81-1.37-3.81-3.64v-6.34c0-.22.07-.29.28-.29h.47c.21 0 .28.07.28.29zM127.864 14.575c.22 0 .29.07.29.29v9.48c0 .22-.07.29-.29.29h-.42c-.21 0-.28-.07-.28-.29v-5.77a18.55 18.55 0 01.17-2.51h-.06a18 18 0 01-1.09 2.21l-2.15 3.79a.35.35 0 01-.33.22h-.28a.37.37 0 01-.34-.22l-2.18-3.83a16.07 16.07 0 01-1-2.18h-.06a21.76 21.76 0 01.16 2.53v5.76c0 .22-.07.29-.29.29h-.39c-.22 0-.29-.07-.29-.29v-9.48c0-.22.07-.29.29-.29h.36a.4.4 0 01.4.23l3.5 6.22 3.48-6.16c.11-.21.17-.24.39-.24zM13.524 22.415v7.23a1 1 0 01-2.09 0v-7.22a1.77 1.77 0 001 .34 1.72 1.72 0 001.09-.35zm8.9-2.09a1 1 0 00-1 1v1.26a1 1 0 102.09 0v-1.21a1 1 0 00-1.09-1.05zm-13.26 4.83a1.8 1.8 0 01-1-.34v7.25a1.05 1.05 0 002.1 0v-7.2a1.83 1.83 0 01-1.1.29zm10-7a1 1 0 00-1.05 1v5.57a1.05 1.05 0 102.1 0v-5.5a1 1 0 00-1.1-1.05zm-3.32 2.14a1.83 1.83 0 01-1.05-.34v7.27a1.05 1.05 0 102.1 0v-7.26a1.77 1.77 0 01-1.1.35zm-8.95 5.57V5.555a.94.94 0 00-.94-.93h-.22a.94.94 0 00-.94.93v20.31a.94.94 0 00.94.94h.22a.94.94 0 00.94-.94zm2.38-1.48h-.22a.94.94 0 01-.94-.94V7.975a.94.94 0 01.94-.93h.22a.94.94 0 01.94.93v15.49a.94.94 0 01-.94.94zm3.31-2.38h-.23a.93.93 0 01-.93-.93v-10.7a.93.93 0 01.93-.94h.23a.94.94 0 01.93.94v10.7a.93.93 0 01-.93.93zm3.31-2.45h-.21a.94.94 0 01-.94-.93v-5.76a.94.94 0 01.94-1h.22a.94.94 0 01.94.94v5.8a.94.94 0 01-.94.95zm3.32-2.15h-.22a.94.94 0 01-.94-.94v-1.49a.94.94 0 01.94-.93h.22a.94.94 0 01.94.93v1.49a.94.94 0 01-.93.94z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="132.948" height="37.739">
|
||||
<path fill="white" d="M31.314 14.575c.36 0 .48.13.48.48v7.92h4.07c.35 0 .48.13.48.48v.7c0 .35-.13.48-.48.48h-5.36c-.35 0-.49-.13-.49-.48v-9.1c0-.35.14-.48.49-.48zM39.894 18.065c0-2.31 1.46-3.7 4-3.7s4 1.39 4 3.7v3.08c0 2.31-1.47 3.7-4 3.7s-4-1.39-4-3.7zm6.32 0c0-1.3-.81-2-2.27-2s-2.26.71-2.26 2v3.1c0 1.31.8 2 2.26 2s2.27-.72 2.27-2zM59.804 19.125c.39 0 .48.13.48.48v1.9a3 3 0 01-1.06 2.36 4.36 4.36 0 01-3 .94c-2.59 0-4-1.39-4-3.7v-3.1c0-2.28 1.46-3.68 4-3.68 2 0 3.3.79 3.85 2.37a.43.43 0 01-.3.62l-.78.27c-.34.12-.48.05-.6-.3a2.06 2.06 0 00-2.17-1.3c-1.47 0-2.27.71-2.27 2v3.18c0 1.31.81 2 2.27 2s2.36-.67 2.36-1.65v-.74h-2.17c-.36 0-.49-.14-.49-.49v-.68c0-.35.13-.48.49-.48zM64.414 18.065c0-2.31 1.46-3.7 4-3.7s4 1.39 4 3.7v3.08c0 2.31-1.47 3.7-4 3.7s-4-1.39-4-3.7zm6.31 0c0-1.3-.81-2-2.26-2s-2.27.71-2.27 2v3.1c0 1.31.81 2 2.27 2s2.26-.72 2.26-2zM77.124 14.865c0-.22.07-.29.29-.29h.46c.22 0 .27.07.27.29v9.48c0 .22 0 .29-.27.29h-.46c-.22 0-.29-.07-.29-.29zM87.254 14.575a3.14 3.14 0 110 6.28h-3v3.49c0 .22-.05.29-.26.29h-.47c-.21 0-.29-.07-.29-.29v-9.48c0-.22.08-.29.29-.29zm-.09 5.29a2.18 2.18 0 100-4.36h-3v4.36zM98.204 14.375a3.61 3.61 0 013.72 2.18c.08.16 0 .29-.16.37l-.44.2c-.18.07-.25.06-.36-.13a2.72 2.72 0 00-2.76-1.64c-1.69 0-2.61.67-2.61 1.87a1.52 1.52 0 001.27 1.54 6.79 6.79 0 001.66.32 6.88 6.88 0 012 .41 2.25 2.25 0 011.56 2.37c0 1.87-1.36 3-3.86 3a3.61 3.61 0 01-3.83-2.43.27.27 0 01.17-.38l.44-.16a.27.27 0 01.36.17 2.86 2.86 0 002.86 1.8c1.89 0 2.82-.66 2.82-2a1.49 1.49 0 00-1.17-1.53 7 7 0 00-1.59-.28l-1.08-.14a9.5 9.5 0 01-1-.27 2.63 2.63 0 01-.89-.47 2.44 2.44 0 01-.8-1.91c.07-1.75 1.38-2.89 3.69-2.89zM107.474 21.215a2.78 2.78 0 005.55 0v-6.35c0-.22.07-.29.29-.29h.46c.22 0 .29.07.29.29v6.34c0 2.27-1.34 3.64-3.81 3.64s-3.81-1.37-3.81-3.64v-6.34c0-.22.07-.29.28-.29h.47c.21 0 .28.07.28.29zM127.864 14.575c.22 0 .29.07.29.29v9.48c0 .22-.07.29-.29.29h-.42c-.21 0-.28-.07-.28-.29v-5.77a18.55 18.55 0 01.17-2.51h-.06a18 18 0 01-1.09 2.21l-2.15 3.79a.35.35 0 01-.33.22h-.28a.37.37 0 01-.34-.22l-2.18-3.83a16.07 16.07 0 01-1-2.18h-.06a21.76 21.76 0 01.16 2.53v5.76c0 .22-.07.29-.29.29h-.39c-.22 0-.29-.07-.29-.29v-9.48c0-.22.07-.29.29-.29h.36a.4.4 0 01.4.23l3.5 6.22 3.48-6.16c.11-.21.17-.24.39-.24zM13.524 22.415v7.23a1 1 0 01-2.09 0v-7.22a1.77 1.77 0 001 .34 1.72 1.72 0 001.09-.35zm8.9-2.09a1 1 0 00-1 1v1.26a1 1 0 102.09 0v-1.21a1 1 0 00-1.09-1.05zm-13.26 4.83a1.8 1.8 0 01-1-.34v7.25a1.05 1.05 0 002.1 0v-7.2a1.83 1.83 0 01-1.1.29zm10-7a1 1 0 00-1.05 1v5.57a1.05 1.05 0 102.1 0v-5.5a1 1 0 00-1.1-1.05zm-3.32 2.14a1.83 1.83 0 01-1.05-.34v7.27a1.05 1.05 0 102.1 0v-7.26a1.77 1.77 0 01-1.1.35zm-8.95 5.57V5.555a.94.94 0 00-.94-.93h-.22a.94.94 0 00-.94.93v20.31a.94.94 0 00.94.94h.22a.94.94 0 00.94-.94zm2.38-1.48h-.22a.94.94 0 01-.94-.94V7.975a.94.94 0 01.94-.93h.22a.94.94 0 01.94.93v15.49a.94.94 0 01-.94.94zm3.31-2.38h-.23a.93.93 0 01-.93-.93v-10.7a.93.93 0 01.93-.94h.23a.94.94 0 01.93.94v10.7a.93.93 0 01-.93.93zm3.31-2.45h-.21a.94.94 0 01-.94-.93v-5.76a.94.94 0 01.94-1h.22a.94.94 0 01.94.94v5.8a.94.94 0 01-.94.95zm3.32-2.15h-.22a.94.94 0 01-.94-.94v-1.49a.94.94 0 01.94-.93h.22a.94.94 0 01.94.93v1.49a.94.94 0 01-.93.94z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
@ -13,6 +13,7 @@ addons.setConfig({
|
||||
brandImage: logo,
|
||||
brandTitle: 'Custom - Storybook',
|
||||
...themes.dark,
|
||||
appContentBg: 'white',
|
||||
},
|
||||
panelPosition: 'bottom',
|
||||
selectedPanel: 'storybook/roundtrip',
|
||||
|
@ -33,6 +33,7 @@
|
||||
"@storybook/client-logger": "6.0.0-alpha.20",
|
||||
"@storybook/core-events": "6.0.0-alpha.20",
|
||||
"@storybook/router": "6.0.0-alpha.20",
|
||||
"@storybook/theming": "6.0.0-alpha.20",
|
||||
"core-js": "^3.0.1",
|
||||
"global": "^4.3.2",
|
||||
"regenerator-runtime": "^0.13.3",
|
||||
|
@ -4,6 +4,7 @@ import { Channel } from '@storybook/channels';
|
||||
import { API } from '@storybook/api';
|
||||
import { RenderData as RouterData } from '@storybook/router';
|
||||
import { logger } from '@storybook/client-logger';
|
||||
import { ThemeVars } from '@storybook/theming';
|
||||
import { types, Types } from './types';
|
||||
|
||||
export { Channel };
|
||||
@ -38,6 +39,7 @@ interface Elements {
|
||||
}
|
||||
|
||||
interface Config {
|
||||
theme?: ThemeVars;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,7 @@ export type Module = StoreData &
|
||||
ProviderData & {
|
||||
mode?: 'production' | 'development';
|
||||
state: State;
|
||||
fullAPI: API;
|
||||
};
|
||||
|
||||
export type State = Other &
|
||||
@ -124,7 +125,7 @@ type StatePartial = Partial<State>;
|
||||
export type ManagerProviderProps = Children & RouterData & ProviderData & DocsModeData;
|
||||
|
||||
class ManagerProvider extends Component<ManagerProviderProps, State> {
|
||||
api: API;
|
||||
api: API = {} as API;
|
||||
|
||||
modules: any[];
|
||||
|
||||
@ -178,15 +179,17 @@ class ManagerProvider extends Component<ManagerProviderProps, State> {
|
||||
initStories,
|
||||
initURL,
|
||||
initVersions,
|
||||
].map(initModule => initModule({ ...routeData, ...apiData, state: this.state }));
|
||||
].map(initModule =>
|
||||
initModule({ ...routeData, ...apiData, state: this.state, fullAPI: this.api })
|
||||
);
|
||||
|
||||
// Create our initial state by combining the initial state of all modules, then overlaying any saved state
|
||||
const state = getInitialState(...this.modules.map(m => m.state));
|
||||
|
||||
// Get our API by combining the APIs exported by each module
|
||||
const combo = Object.assign({ navigate }, ...this.modules.map(m => m.api));
|
||||
const api: API = Object.assign(this.api, { navigate }, ...this.modules.map(m => m.api));
|
||||
|
||||
const api = initProviderApi({ provider, store, api: combo });
|
||||
initProviderApi({ provider, store, api });
|
||||
|
||||
api.on(STORY_CHANGED, (id: string) => {
|
||||
const options = api.getParameters(id, 'options');
|
||||
@ -223,7 +226,8 @@ class ManagerProvider extends Component<ManagerProviderProps, State> {
|
||||
...state,
|
||||
location: props.location,
|
||||
path: props.path,
|
||||
viewMode: props.viewMode,
|
||||
// if its a docsOnly page, even the 'story' view mode is considered 'docs'
|
||||
viewMode: (props.docsMode && props.viewMode) === 'story' ? 'docs' : props.viewMode,
|
||||
storyId: props.storyId,
|
||||
};
|
||||
}
|
||||
|
@ -247,7 +247,7 @@ export const transformStoriesRawToStoriesHash = (
|
||||
return acc;
|
||||
}
|
||||
|
||||
return Object.values(storiesHashOutOfOrder).reduce(addItem, base);
|
||||
return Object.values(storiesHashOutOfOrder).reduce(addItem, { ...base });
|
||||
};
|
||||
|
||||
export type Item = StoriesHash[keyof StoriesHash];
|
||||
|
@ -85,20 +85,20 @@ export function ensurePanel(panels: Panels, selectedPanel?: string, currentPanel
|
||||
return currentPanel;
|
||||
}
|
||||
|
||||
export default ({ provider, store }: Module) => {
|
||||
export default ({ provider, store, fullAPI }: Module) => {
|
||||
const api: SubAPI = {
|
||||
getElements: type => provider.getElements(type),
|
||||
getPanels: () => api.getElements(types.PANEL),
|
||||
getStoryPanels: () => {
|
||||
const allPanels = api.getPanels();
|
||||
const { storyId, storiesHash } = store.getState();
|
||||
const storyInput = storyId && (storiesHash[storyId] as StoryInput);
|
||||
const { storyId } = store.getState();
|
||||
const story = fullAPI.getData(storyId);
|
||||
|
||||
if (!allPanels || !storyInput) {
|
||||
if (!allPanels || !story) {
|
||||
return allPanels;
|
||||
}
|
||||
|
||||
const { parameters } = storyInput;
|
||||
const { parameters } = story;
|
||||
|
||||
const filteredPanels: Collection = {};
|
||||
Object.entries(allPanels).forEach(([id, panel]) => {
|
||||
|
@ -61,7 +61,7 @@ describe('Addons API', () => {
|
||||
describe('#getStoryPanels', () => {
|
||||
it('should return all panels by default', () => {
|
||||
// given
|
||||
const { api } = initAddons({ provider, store });
|
||||
const { api } = initAddons({ provider, store, fullAPI: { getData: () => undefined } });
|
||||
|
||||
// when
|
||||
const filteredPanels = api.getStoryPanels();
|
||||
@ -73,21 +73,27 @@ describe('Addons API', () => {
|
||||
it('should filter disabled addons', () => {
|
||||
// given
|
||||
const storyId = 'story 1';
|
||||
const storiesHash = {
|
||||
[storyId]: {
|
||||
parameters: {
|
||||
a11y: { disabled: true },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const storeWithStory = {
|
||||
getState: () => ({
|
||||
storyId,
|
||||
storiesHash: {
|
||||
[storyId]: {
|
||||
parameters: {
|
||||
a11y: { disabled: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
storiesHash,
|
||||
}),
|
||||
setState: jest.fn(),
|
||||
};
|
||||
|
||||
const { api } = initAddons({ provider, store: storeWithStory });
|
||||
const { api } = initAddons({
|
||||
provider,
|
||||
store: storeWithStory,
|
||||
fullAPI: { getData: id => storiesHash[id] },
|
||||
});
|
||||
|
||||
// when
|
||||
const filteredPanels = api.getStoryPanels();
|
||||
|
@ -4,10 +4,6 @@ import { logger } from '@storybook/client-logger';
|
||||
|
||||
import { isJSON, parse, stringify } from 'telejson';
|
||||
|
||||
interface RawEvent {
|
||||
data: string;
|
||||
}
|
||||
|
||||
interface Config {
|
||||
page: 'manager' | 'preview';
|
||||
}
|
||||
@ -106,7 +102,7 @@ export class PostmsgTransport {
|
||||
return window.parent;
|
||||
}
|
||||
|
||||
private handleEvent(rawEvent: RawEvent): void {
|
||||
private handleEvent(rawEvent: MessageEvent): void {
|
||||
try {
|
||||
const { data } = rawEvent;
|
||||
const { key, event } = typeof data === 'string' && isJSON(data) ? parse(data) : data;
|
||||
|
@ -138,7 +138,7 @@ export class Channel {
|
||||
private handleEvent(event: ChannelEvent, isPeer = false) {
|
||||
const listeners = this.listeners(event.type);
|
||||
if (listeners && (isPeer || event.from !== this.sender)) {
|
||||
listeners.forEach(fn => !(isPeer && fn.ignorePeer) && fn(...event.args));
|
||||
listeners.forEach(fn => !(isPeer && fn.ignorePeer) && fn.apply(event, event.args));
|
||||
}
|
||||
this.data[event.type] = event.args;
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import React, { FunctionComponent, ComponentProps } from 'react';
|
||||
import { styled } from '@storybook/theming';
|
||||
import icons, { IconKey } from './icons';
|
||||
|
||||
import Svg, { SvgProps } from './svg';
|
||||
import Svg from './svg';
|
||||
|
||||
const Path = styled.path({
|
||||
fill: 'currentColor',
|
||||
});
|
||||
|
||||
export interface IconsProps extends SvgProps {
|
||||
export interface IconsProps extends ComponentProps<typeof Svg> {
|
||||
icon: IconKey;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { styled } from '@storybook/theming';
|
||||
|
||||
export interface SvgProps {
|
||||
interface SvgProps {
|
||||
inline?: boolean;
|
||||
}
|
||||
|
||||
|
@ -121,12 +121,12 @@ const WithToolTipState: FunctionComponent<WithTooltipPureProps & {
|
||||
try {
|
||||
iframe.contentWindow.document.removeEventListener('click', hide);
|
||||
} catch (e) {
|
||||
logger.warn('Removing a click listener from iframe failed: ', e);
|
||||
logger.debug('Removing a click listener from iframe failed: ', e);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn('Adding a click listener to iframe failed: ', e);
|
||||
logger.debug('Adding a click listener to iframe failed: ', e);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
"@storybook/router": "6.0.0-alpha.20",
|
||||
"@storybook/theming": "6.0.0-alpha.20",
|
||||
"@types/markdown-to-jsx": "^6.9.1",
|
||||
"@types/rfdc": "^1.1.0",
|
||||
"copy-to-clipboard": "^3.0.8",
|
||||
"core-js": "^3.0.1",
|
||||
"core-js-pure": "^3.0.1",
|
||||
@ -50,6 +51,7 @@
|
||||
"memoizerific": "^1.11.3",
|
||||
"polished": "^3.4.4",
|
||||
"qs": "^6.6.0",
|
||||
"rfdc": "^1.1.4",
|
||||
"react": "^16.8.3",
|
||||
"react-dom": "^16.8.3",
|
||||
"react-draggable": "^4.0.3",
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import React, { FunctionComponent, useMemo } from 'react';
|
||||
import { Global, createGlobal, styled } from '@storybook/theming';
|
||||
import memoize from 'memoizerific';
|
||||
import sizeMe from 'react-sizeme';
|
||||
|
||||
import { Route } from '@storybook/router';
|
||||
@ -15,24 +14,6 @@ import Notifications from './containers/notifications';
|
||||
|
||||
import SettingsPages from './settings';
|
||||
|
||||
const createProps = memoize(1)(() => ({
|
||||
Sidebar,
|
||||
Preview,
|
||||
Panel,
|
||||
Notifications,
|
||||
pages: [
|
||||
{
|
||||
key: 'settings',
|
||||
render: () => <SettingsPages />,
|
||||
route: (({ children }) => (
|
||||
<Route path="/settings" startsWith>
|
||||
{children}
|
||||
</Route>
|
||||
)) as FunctionComponent,
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
const View = styled.div({
|
||||
position: 'fixed',
|
||||
overflow: 'hidden',
|
||||
@ -53,10 +34,29 @@ export interface AppProps {
|
||||
|
||||
const App = React.memo<AppProps>(
|
||||
({ viewMode, docsOnly, layout, panelCount, size: { width, height } }) => {
|
||||
const props = createProps();
|
||||
|
||||
let content;
|
||||
|
||||
const props = useMemo(
|
||||
() => ({
|
||||
Sidebar,
|
||||
Preview,
|
||||
Panel,
|
||||
Notifications,
|
||||
pages: [
|
||||
{
|
||||
key: 'settings',
|
||||
render: () => <SettingsPages />,
|
||||
route: (({ children }) => (
|
||||
<Route path="/settings" startsWith>
|
||||
{children}
|
||||
</Route>
|
||||
)) as FunctionComponent,
|
||||
},
|
||||
],
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
if (!width || !height) {
|
||||
content = (
|
||||
<div>
|
||||
|
@ -4,7 +4,7 @@ import { DOCS_MODE } from 'global';
|
||||
import { SyntheticEvent } from 'react';
|
||||
import { StoriesHash, isRoot, isStory } from '@storybook/api';
|
||||
|
||||
const FUZZY_SEARCH_THRESHOLD = 0.4;
|
||||
const FUZZY_SEARCH_THRESHOLD = 0.35;
|
||||
|
||||
export const prevent = (e: SyntheticEvent) => {
|
||||
e.preventDefault();
|
||||
@ -14,7 +14,6 @@ export const prevent = (e: SyntheticEvent) => {
|
||||
const toList = memoize(1)((dataset: Dataset) => Object.values(dataset));
|
||||
|
||||
export type Item = StoriesHash[keyof StoriesHash];
|
||||
|
||||
export type Dataset = Record<string, Item>;
|
||||
export type SelectedSet = Record<string, boolean>;
|
||||
export type ExpandedSet = Record<string, boolean>;
|
||||
@ -192,12 +191,20 @@ const fuse = memoize(5)(
|
||||
})
|
||||
);
|
||||
|
||||
const exactMatch = memoize(1)(filter => (i: Item) =>
|
||||
(isStory(i) && i.kind.includes(filter)) ||
|
||||
(i.name && i.name.includes(filter)) ||
|
||||
(i.parameters && i.parameters.fileName && i.parameters.fileName.includes(filter)) ||
|
||||
(i.parameters && typeof i.parameters.notes === 'string' && i.parameters.notes.includes(filter))
|
||||
);
|
||||
const exactMatch = (filter: string) => {
|
||||
const reg = new RegExp(filter, 'i');
|
||||
|
||||
return (i: Item) =>
|
||||
i.isLeaf &&
|
||||
((isStory(i) && reg.test(i.kind)) ||
|
||||
(i.name && reg.test(i.name)) ||
|
||||
(i.parameters &&
|
||||
typeof i.parameters.fileName === 'string' &&
|
||||
reg.test(i.parameters.fileName.toString())) ||
|
||||
(i.parameters &&
|
||||
typeof i.parameters.notes === 'string' &&
|
||||
reg.test(i.parameters.notes.toString())));
|
||||
};
|
||||
|
||||
export const toId = (base: string, addition: string) =>
|
||||
base === '' ? `${addition}` : `${base}-${addition}`;
|
||||
@ -207,7 +214,8 @@ export const toFiltered = (dataset: Dataset, filter: string) => {
|
||||
if (filter.length && filter.length > 2) {
|
||||
found = fuse(dataset).search(filter);
|
||||
} else {
|
||||
found = toList(dataset).filter(exactMatch(filter));
|
||||
const matcher = exactMatch(filter);
|
||||
found = toList(dataset).filter(matcher);
|
||||
}
|
||||
|
||||
// get all parents for all results
|
||||
|
218
lib/ui/src/containers/menu.tsx
Normal file
218
lib/ui/src/containers/menu.tsx
Normal file
@ -0,0 +1,218 @@
|
||||
import React, { useMemo, ComponentProps } from 'react';
|
||||
|
||||
import { Badge, Icons } from '@storybook/components';
|
||||
import { API } from '@storybook/api';
|
||||
|
||||
import { styled } from '@storybook/theming';
|
||||
import { shortcutToHumanString } from '../libs/shortcut';
|
||||
|
||||
const focusableUIElements = {
|
||||
storySearchField: 'storybook-explorer-searchfield',
|
||||
storyListMenu: 'storybook-explorer-menu',
|
||||
storyPanelRoot: 'storybook-panel-root',
|
||||
};
|
||||
|
||||
const shortcutToHumanStringIfEnabled = (shortcuts: string[], enableShortcuts: boolean) =>
|
||||
enableShortcuts ? shortcutToHumanString(shortcuts) : null;
|
||||
|
||||
const sharedStyles = {
|
||||
height: 10,
|
||||
width: 10,
|
||||
marginLeft: -5,
|
||||
marginRight: -5,
|
||||
display: 'block',
|
||||
};
|
||||
|
||||
const Icon = styled(Icons)(sharedStyles, ({ theme }) => ({
|
||||
color: theme.color.secondary,
|
||||
}));
|
||||
|
||||
const Img = styled.img(sharedStyles);
|
||||
const Placeholder = styled.div({ sharedStyles });
|
||||
|
||||
export interface ListItemIconProps {
|
||||
icon?: ComponentProps<typeof Icons>['icon'];
|
||||
imgSrc?: string;
|
||||
}
|
||||
export const MenuItemIcon = ({ icon, imgSrc }: ListItemIconProps) => {
|
||||
if (icon) {
|
||||
return <Icon icon={icon} />;
|
||||
}
|
||||
if (imgSrc) {
|
||||
return <Img src={imgSrc} alt="image" />;
|
||||
}
|
||||
return <Placeholder />;
|
||||
};
|
||||
|
||||
export const useMenu = (
|
||||
api: API,
|
||||
isFullscreen: boolean,
|
||||
showPanel: boolean,
|
||||
showNav: boolean,
|
||||
enableShortcuts: boolean
|
||||
) => {
|
||||
const shortcutKeys = api.getShortcutKeys();
|
||||
|
||||
const sidebarToggle = useMemo(
|
||||
() => ({
|
||||
id: 'S',
|
||||
title: 'Show sidebar',
|
||||
onClick: () => api.toggleNav(),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.toggleNav, enableShortcuts),
|
||||
left: showNav ? <MenuItemIcon icon="check" /> : <MenuItemIcon />,
|
||||
}),
|
||||
[api, shortcutToHumanStringIfEnabled, enableShortcuts, shortcutKeys, showNav]
|
||||
);
|
||||
|
||||
const addonsToggle = useMemo(
|
||||
() => ({
|
||||
id: 'A',
|
||||
title: 'Show addons',
|
||||
onClick: () => api.togglePanel(),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.togglePanel, enableShortcuts),
|
||||
left: showPanel ? <MenuItemIcon icon="check" /> : <MenuItemIcon />,
|
||||
}),
|
||||
[api, shortcutToHumanStringIfEnabled, enableShortcuts, shortcutKeys, showPanel]
|
||||
);
|
||||
|
||||
const addonsOrientationToggle = useMemo(
|
||||
() => ({
|
||||
id: 'D',
|
||||
title: 'Change addons orientation',
|
||||
onClick: () => api.togglePanelPosition(),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.panelPosition, enableShortcuts),
|
||||
left: <MenuItemIcon />,
|
||||
}),
|
||||
[api, shortcutToHumanStringIfEnabled, enableShortcuts, shortcutKeys]
|
||||
);
|
||||
|
||||
const fullscreenToggle = useMemo(
|
||||
() => ({
|
||||
id: 'F',
|
||||
title: 'Go full screen',
|
||||
onClick: () => api.toggleFullscreen(),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.fullScreen, enableShortcuts),
|
||||
left: isFullscreen ? 'check' : <MenuItemIcon />,
|
||||
}),
|
||||
[api, shortcutToHumanStringIfEnabled, enableShortcuts, shortcutKeys, isFullscreen]
|
||||
);
|
||||
|
||||
const searchToggle = useMemo(
|
||||
() => ({
|
||||
id: '/',
|
||||
title: 'Search',
|
||||
onClick: () => api.focusOnUIElement(focusableUIElements.storySearchField),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.search, enableShortcuts),
|
||||
left: <MenuItemIcon />,
|
||||
}),
|
||||
[api, shortcutToHumanStringIfEnabled, enableShortcuts, shortcutKeys]
|
||||
);
|
||||
|
||||
const up = useMemo(
|
||||
() => ({
|
||||
id: 'up',
|
||||
title: 'Previous component',
|
||||
onClick: () => api.jumpToComponent(-1),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.prevComponent, enableShortcuts),
|
||||
left: <MenuItemIcon />,
|
||||
}),
|
||||
[api, shortcutToHumanStringIfEnabled, enableShortcuts, shortcutKeys]
|
||||
);
|
||||
|
||||
const down = useMemo(
|
||||
() => ({
|
||||
id: 'down',
|
||||
title: 'Next component',
|
||||
onClick: () => api.jumpToComponent(1),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.nextComponent, enableShortcuts),
|
||||
left: <MenuItemIcon />,
|
||||
}),
|
||||
[api, shortcutToHumanStringIfEnabled, enableShortcuts, shortcutKeys]
|
||||
);
|
||||
|
||||
const prev = useMemo(
|
||||
() => ({
|
||||
id: 'prev',
|
||||
title: 'Previous story',
|
||||
onClick: () => api.jumpToStory(-1),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.prevStory, enableShortcuts),
|
||||
left: <MenuItemIcon />,
|
||||
}),
|
||||
[api, shortcutToHumanStringIfEnabled, enableShortcuts, shortcutKeys]
|
||||
);
|
||||
|
||||
const next = useMemo(
|
||||
() => ({
|
||||
id: 'next',
|
||||
title: 'Next story',
|
||||
onClick: () => api.jumpToStory(1),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.nextStory, enableShortcuts),
|
||||
left: <MenuItemIcon />,
|
||||
}),
|
||||
[api, shortcutToHumanStringIfEnabled, enableShortcuts, shortcutKeys]
|
||||
);
|
||||
|
||||
const about = useMemo(
|
||||
() => ({
|
||||
id: 'about',
|
||||
title: 'About your Storybook',
|
||||
onClick: () => api.navigate('/settings/about'),
|
||||
right: api.versionUpdateAvailable() && <Badge status="positive">Update</Badge>,
|
||||
left: <MenuItemIcon />,
|
||||
}),
|
||||
[api, shortcutToHumanStringIfEnabled, enableShortcuts, shortcutKeys]
|
||||
);
|
||||
|
||||
const shortcuts = useMemo(
|
||||
() => ({
|
||||
id: 'shortcuts',
|
||||
title: 'Keyboard shortcuts',
|
||||
onClick: () => api.navigate('/settings/shortcuts'),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.shortcutsPage, enableShortcuts),
|
||||
left: <MenuItemIcon />,
|
||||
}),
|
||||
[api, shortcutToHumanStringIfEnabled, enableShortcuts, shortcutKeys]
|
||||
);
|
||||
|
||||
const collapse = useMemo(
|
||||
() => ({
|
||||
id: 'collapse',
|
||||
title: 'Collapse all',
|
||||
onClick: () => api.collapseAll(),
|
||||
right: shortcutToHumanString(shortcutKeys.collapseAll),
|
||||
left: <MenuItemIcon />,
|
||||
}),
|
||||
[api, shortcutToHumanStringIfEnabled, enableShortcuts, shortcutKeys]
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
() => [
|
||||
sidebarToggle,
|
||||
addonsToggle,
|
||||
addonsOrientationToggle,
|
||||
fullscreenToggle,
|
||||
searchToggle,
|
||||
up,
|
||||
down,
|
||||
prev,
|
||||
next,
|
||||
about,
|
||||
shortcuts,
|
||||
collapse,
|
||||
],
|
||||
[
|
||||
sidebarToggle,
|
||||
addonsToggle,
|
||||
addonsOrientationToggle,
|
||||
fullscreenToggle,
|
||||
searchToggle,
|
||||
up,
|
||||
down,
|
||||
prev,
|
||||
next,
|
||||
about,
|
||||
shortcuts,
|
||||
collapse,
|
||||
]
|
||||
);
|
||||
};
|
@ -1,11 +1,13 @@
|
||||
import { PREVIEW_URL } from 'global';
|
||||
import React from 'react';
|
||||
|
||||
import { State, Consumer, Combo, StoriesHash } from '@storybook/api';
|
||||
import { Consumer, Combo, StoriesHash, isRoot, isGroup, isStory } from '@storybook/api';
|
||||
|
||||
import { Preview } from '../components/preview/preview';
|
||||
import { PreviewProps } from '../components/preview/PreviewProps';
|
||||
|
||||
type Item = StoriesHash[keyof StoriesHash];
|
||||
|
||||
const nonAlphanumSpace = /[^a-z0-9 ]/gi;
|
||||
const doubleSpace = /\s\s/gi;
|
||||
const replacer = (match: string) => ` ${match} `;
|
||||
@ -13,40 +15,46 @@ const replacer = (match: string) => ` ${match} `;
|
||||
const addExtraWhiteSpace = (input: string) =>
|
||||
input.replace(nonAlphanumSpace, replacer).replace(doubleSpace, ' ');
|
||||
|
||||
const getDescription = (storiesHash: StoriesHash, storyId: string) => {
|
||||
const storyInfo = storiesHash[storyId];
|
||||
|
||||
if (storyInfo) {
|
||||
// @ts-ignore
|
||||
const { kind, name } = storyInfo;
|
||||
return kind && name ? addExtraWhiteSpace(`${kind} - ${name}`) : '';
|
||||
const getDescription = (item: Item) => {
|
||||
if (isRoot(item)) {
|
||||
return item.name ? `${item.name} ⋅ Storybook` : 'Storybook';
|
||||
}
|
||||
if (isGroup(item)) {
|
||||
return item.name ? `${item.name} ⋅ Storybook` : 'Storybook';
|
||||
}
|
||||
if (isStory(item)) {
|
||||
const { kind, name } = item;
|
||||
return kind && name ? addExtraWhiteSpace(`${kind} - ${name} ⋅ Storybook`) : 'Storybook';
|
||||
}
|
||||
|
||||
return '';
|
||||
return 'Storybook';
|
||||
};
|
||||
|
||||
const mapper = ({ api, state }: Combo) => {
|
||||
const { layout, location, customQueryParams, storiesHash, storyId } = state;
|
||||
const { parameters } = storiesHash[storyId] || {};
|
||||
const { layout, location, customQueryParams, storyId } = state;
|
||||
const story = api.getData(storyId);
|
||||
const parameters = story ? story.parameters : {};
|
||||
const docsOnly = story && story.parameters ? !!story.parameters.docsOnly : false;
|
||||
|
||||
return {
|
||||
api,
|
||||
options: layout,
|
||||
description: getDescription(storiesHash, storyId),
|
||||
description: getDescription(story),
|
||||
...api.getUrlState(),
|
||||
queryParams: customQueryParams,
|
||||
docsOnly: (parameters && parameters.docsOnly) as boolean,
|
||||
docsOnly,
|
||||
location,
|
||||
parameters,
|
||||
};
|
||||
};
|
||||
|
||||
function getBaseUrl(): string {
|
||||
const getBaseUrl = (): string => {
|
||||
try {
|
||||
return PREVIEW_URL || 'iframe.html';
|
||||
} catch (e) {
|
||||
return 'iframe.html';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const PreviewConnected = React.memo<{ id: string; withLoader: boolean }>(props => (
|
||||
<Consumer filter={mapper}>
|
||||
|
@ -1,115 +1,14 @@
|
||||
import { DOCS_MODE } from 'global';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import memoize from 'memoizerific';
|
||||
import React, { FunctionComponent, useMemo } from 'react';
|
||||
import rfdc from 'rfdc';
|
||||
|
||||
import { Badge } from '@storybook/components';
|
||||
import { Consumer, Combo, StoriesHash, Story } from '@storybook/api';
|
||||
|
||||
import { shortcutToHumanString } from '../libs/shortcut';
|
||||
|
||||
import ListItemIcon from '../components/sidebar/ListItemIcon';
|
||||
import SidebarComponent from '../components/sidebar/Sidebar';
|
||||
import { useMenu } from './menu';
|
||||
|
||||
type Item = StoriesHash[keyof StoriesHash];
|
||||
|
||||
const focusableUIElements = {
|
||||
storySearchField: 'storybook-explorer-searchfield',
|
||||
storyListMenu: 'storybook-explorer-menu',
|
||||
storyPanelRoot: 'storybook-panel-root',
|
||||
};
|
||||
|
||||
const shortcutToHumanStringIfEnabled = (shortcuts: string[], enableShortcuts: boolean) =>
|
||||
enableShortcuts ? shortcutToHumanString(shortcuts) : null;
|
||||
|
||||
const createMenu = memoize(1)(
|
||||
(api, shortcutKeys, isFullscreen, showPanel, showNav, enableShortcuts) => [
|
||||
{
|
||||
id: 'S',
|
||||
title: 'Show sidebar',
|
||||
onClick: () => api.toggleNav(),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.toggleNav, enableShortcuts),
|
||||
left: showNav ? <ListItemIcon icon="check" /> : <ListItemIcon />,
|
||||
},
|
||||
{
|
||||
id: 'A',
|
||||
title: 'Show addons',
|
||||
onClick: () => api.togglePanel(),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.togglePanel, enableShortcuts),
|
||||
left: showPanel ? <ListItemIcon icon="check" /> : <ListItemIcon />,
|
||||
},
|
||||
{
|
||||
id: 'D',
|
||||
title: 'Change addons orientation',
|
||||
onClick: () => api.togglePanelPosition(),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.panelPosition, enableShortcuts),
|
||||
left: <ListItemIcon />,
|
||||
},
|
||||
{
|
||||
id: 'F',
|
||||
title: 'Go full screen',
|
||||
onClick: api.toggleFullscreen,
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.fullScreen, enableShortcuts),
|
||||
left: isFullscreen ? 'check' : <ListItemIcon />,
|
||||
},
|
||||
{
|
||||
id: '/',
|
||||
title: 'Search',
|
||||
onClick: () => api.focusOnUIElement(focusableUIElements.storySearchField),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.search, enableShortcuts),
|
||||
left: <ListItemIcon />,
|
||||
},
|
||||
{
|
||||
id: 'up',
|
||||
title: 'Previous component',
|
||||
onClick: () => api.jumpToComponent(-1),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.prevComponent, enableShortcuts),
|
||||
left: <ListItemIcon />,
|
||||
},
|
||||
{
|
||||
id: 'down',
|
||||
title: 'Next component',
|
||||
onClick: () => api.jumpToComponent(1),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.nextComponent, enableShortcuts),
|
||||
left: <ListItemIcon />,
|
||||
},
|
||||
{
|
||||
id: 'prev',
|
||||
title: 'Previous story',
|
||||
onClick: () => api.jumpToStory(-1),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.prevStory, enableShortcuts),
|
||||
left: <ListItemIcon />,
|
||||
},
|
||||
{
|
||||
id: 'next',
|
||||
title: 'Next story',
|
||||
onClick: () => api.jumpToStory(1),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.nextStory, enableShortcuts),
|
||||
left: <ListItemIcon />,
|
||||
},
|
||||
{
|
||||
id: 'about',
|
||||
title: 'About your Storybook',
|
||||
onClick: () => api.navigate('/settings/about'),
|
||||
right: api.versionUpdateAvailable() && <Badge status="positive">Update</Badge>,
|
||||
left: <ListItemIcon />,
|
||||
},
|
||||
{
|
||||
id: 'shortcuts',
|
||||
title: 'Keyboard shortcuts',
|
||||
onClick: () => api.navigate('/settings/shortcuts'),
|
||||
right: shortcutToHumanStringIfEnabled(shortcutKeys.shortcutsPage, enableShortcuts),
|
||||
left: <ListItemIcon />,
|
||||
},
|
||||
{
|
||||
id: 'collapse',
|
||||
title: 'Collapse all',
|
||||
onClick: () => api.collapseAll(),
|
||||
right: shortcutToHumanString(shortcutKeys.collapseAll),
|
||||
left: <ListItemIcon />,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
export const collapseAllStories = (stories: StoriesHash) => {
|
||||
// keep track of component IDs that have been rewritten to the ID of their first leaf child
|
||||
const componentIdToLeafId: Record<string, string> = {};
|
||||
@ -221,34 +120,48 @@ export const collapseDocsOnlyStories = (storiesHash: StoriesHash) => {
|
||||
return result;
|
||||
};
|
||||
|
||||
export const mapper = ({ state, api }: Combo) => {
|
||||
const {
|
||||
ui: { name, url, enableShortcuts },
|
||||
viewMode,
|
||||
storyId,
|
||||
layout: { isFullscreen, showPanel, showNav },
|
||||
storiesHash,
|
||||
storiesConfigured,
|
||||
} = state;
|
||||
const stories = DOCS_MODE
|
||||
? collapseAllStories(storiesHash)
|
||||
: collapseDocsOnlyStories(storiesHash);
|
||||
const clone = rfdc({ circles: true });
|
||||
|
||||
const shortcutKeys = api.getShortcutKeys();
|
||||
return {
|
||||
loading: !storiesConfigured,
|
||||
title: name,
|
||||
url,
|
||||
stories,
|
||||
storyId,
|
||||
viewMode,
|
||||
menu: createMenu(api, shortcutKeys, isFullscreen, showPanel, showNav, enableShortcuts),
|
||||
menuHighlighted: api.versionUpdateAvailable(),
|
||||
const Sidebar: FunctionComponent<{}> = React.memo(() => {
|
||||
const mapper = ({ state, api }: Combo) => {
|
||||
const {
|
||||
storiesConfigured,
|
||||
ui: { name, url, enableShortcuts },
|
||||
viewMode,
|
||||
storyId,
|
||||
layout: { isFullscreen, showPanel, showNav },
|
||||
storiesHash,
|
||||
refs,
|
||||
} = state;
|
||||
|
||||
const stories = useMemo(() => {
|
||||
// protect against mutation
|
||||
const copy = clone(storiesHash);
|
||||
|
||||
return DOCS_MODE ? collapseAllStories(copy) : collapseDocsOnlyStories(copy);
|
||||
}, [DOCS_MODE, storiesHash]);
|
||||
|
||||
const menu = useMenu(api, isFullscreen, showPanel, showNav, enableShortcuts);
|
||||
|
||||
return {
|
||||
loading: !storiesConfigured,
|
||||
title: name,
|
||||
url,
|
||||
stories,
|
||||
refs,
|
||||
storyId,
|
||||
viewMode,
|
||||
menu,
|
||||
menuHighlighted: api.versionUpdateAvailable(),
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const Sidebar: FunctionComponent<any> = props => (
|
||||
<Consumer filter={mapper}>{fromState => <SidebarComponent {...props} {...fromState} />}</Consumer>
|
||||
);
|
||||
return (
|
||||
<Consumer filter={mapper}>
|
||||
{fromState => {
|
||||
return <SidebarComponent {...fromState} />;
|
||||
}}
|
||||
</Consumer>
|
||||
);
|
||||
});
|
||||
|
||||
export default Sidebar;
|
||||
|
@ -43,7 +43,7 @@ export const Root: FunctionComponent<RootProps> = ({ provider }) => (
|
||||
>
|
||||
{({ state, api }: Combo) => {
|
||||
const panelCount = Object.keys(api.getPanels()).length;
|
||||
const story = state.storiesHash[state.storyId];
|
||||
const story = api.getData(state.storyId);
|
||||
|
||||
return (
|
||||
<ThemeProvider key="theme.provider" theme={ensureTheme(state.theme)}>
|
||||
|
10
yarn.lock
10
yarn.lock
@ -4362,6 +4362,11 @@
|
||||
"@types/prop-types" "*"
|
||||
csstype "^2.2.0"
|
||||
|
||||
"@types/rfdc@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/rfdc/-/rfdc-1.1.0.tgz#1fc5ffdc679575e2ca31399d4ee75f353afdd37b"
|
||||
integrity sha512-Ez0Pc0H6m8C2L3Wif9SR5YlJTB/UnZIq0N9G/dPB2fmGo42oLo95o73hHHtoGvUucMD4OdlquscflSuKCZE8qA==
|
||||
|
||||
"@types/rimraf@^2.0.2":
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-2.0.3.tgz#0199a46af106729ba14213fda7b981278d8c84f2"
|
||||
@ -26249,6 +26254,11 @@ rfc6902@^3.0.1:
|
||||
resolved "https://registry.yarnpkg.com/rfc6902/-/rfc6902-3.0.4.tgz#82965f13536fd20cb7799ce0376e9ce7cd3ebfe6"
|
||||
integrity sha512-OnzreaZXrwT5w2ikKXWr5QcuI7NZpL+J3hIkAwozjOnKVUL7fPsB8Vcmu8YBiiou1/r3V0Jc0T1uQDyfAPvLzA==
|
||||
|
||||
rfdc@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2"
|
||||
integrity sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==
|
||||
|
||||
rgb-regex@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1"
|
||||
|
Loading…
x
Reference in New Issue
Block a user