storybook/lib/ui/src/components/sidebar/SidebarStories.tsx
2020-01-28 13:37:54 +08:00

203 lines
5.1 KiB
TypeScript

import React, { Fragment, FunctionComponent, memo } from 'react';
import PropTypes from 'prop-types';
import { styled } from '@storybook/theming';
import { Placeholder, Link as StyledLink } from '@storybook/components';
import { State } from '@storybook/api';
import { Location, Link as RouterLink } from '@storybook/router';
import { TreeState } from './treeview/treeview';
import SidebarItem from './SidebarItem';
import SidebarSearch from './SidebarSearch';
import SidebarSubheading from './SidebarSubheading';
const Search = styled(SidebarSearch)({
margin: '0 20px 1rem',
});
const Subheading = styled(SidebarSubheading)({
margin: '0 20px',
});
Subheading.propTypes = {
className: PropTypes.string,
};
Subheading.defaultProps = {
className: 'sidebar-subheading',
};
const Section = styled.section({
'& + section': {
marginTop: 20,
},
'&:last-of-type': {
marginBottom: 40,
},
});
const List = styled.div();
List.displayName = 'List';
const plain = {
color: 'inherit',
display: 'block',
textDecoration: 'none',
userSelect: 'none',
};
// @ts-ignore
const PlainRouterLink = styled(RouterLink)(plain);
// @ts-ignore
const PlainLink = styled.a(plain);
const Wrapper = styled.div({});
export const viewMode = (
currentViewMode: string | undefined,
isDocsOnly: boolean,
parameters: { viewMode?: string } = {}
) => {
const { viewMode: paramViewMode } = parameters;
switch (true) {
case typeof paramViewMode === 'string':
return paramViewMode;
case isDocsOnly:
return 'docs';
case currentViewMode === 'settings' || !currentViewMode:
return 'story';
default:
return currentViewMode;
}
};
const targetId = (childIds?: string[]) =>
childIds && childIds.find((childId: string) => /.*--.*/.exec(childId));
export const Link = ({
id,
prefix,
name,
children,
isLeaf,
isComponent,
onClick,
onKeyUp,
childIds,
isExpanded,
parameters,
}) => {
return isLeaf || (isComponent && !isExpanded) ? (
<Location>
{({ viewMode: currentViewMode }) => (
<PlainRouterLink
title={name}
id={prefix + id}
to={`/${viewMode(currentViewMode, isLeaf && isComponent, parameters)}/${targetId(
childIds
) || id}`}
onKeyUp={onKeyUp}
onClick={onClick}
>
{children}
</PlainRouterLink>
)}
</Location>
) : (
<PlainLink title={name} id={prefix + id} onKeyUp={onKeyUp} onClick={onClick}>
{children}
</PlainLink>
);
};
Link.displayName = 'Link';
Link.propTypes = {
children: PropTypes.node.isRequired,
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
isLeaf: PropTypes.bool.isRequired,
prefix: PropTypes.string.isRequired,
onKeyUp: PropTypes.func.isRequired,
onClick: PropTypes.func.isRequired,
childIds: PropTypes.arrayOf(PropTypes.string),
isExpanded: PropTypes.bool,
};
Link.defaultProps = {
childIds: null,
isExpanded: false,
};
export interface StoriesProps {
loading: boolean;
stories: State['StoriesHash'];
storyId?: undefined | string;
className?: undefined | string;
}
const SidebarStories: FunctionComponent<StoriesProps> = memo(
({ stories, storyId, loading, className, ...rest }) => {
const list = Object.entries(stories);
if (loading) {
return (
<Wrapper className={className}>
<SidebarItem loading />
<SidebarItem loading />
<SidebarItem depth={1} loading />
<SidebarItem depth={1} loading />
<SidebarItem depth={2} loading />
<SidebarItem depth={3} loading />
<SidebarItem depth={3} loading />
<SidebarItem depth={3} loading />
<SidebarItem depth={1} loading />
<SidebarItem depth={1} loading />
<SidebarItem depth={1} loading />
<SidebarItem depth={2} loading />
<SidebarItem depth={2} loading />
<SidebarItem depth={2} loading />
<SidebarItem depth={3} loading />
<SidebarItem loading />
<SidebarItem loading />
</Wrapper>
);
}
if (list.length < 1) {
return (
<Wrapper className={className}>
<Placeholder key="empty">
<Fragment key="title">No stories found</Fragment>
<Fragment>
Learn how to&nbsp;
<StyledLink href="https://storybook.js.org/basics/writing-stories/" target="_blank">
write stories
</StyledLink>
</Fragment>
</Placeholder>
</Wrapper>
);
}
return (
<Wrapper className={className}>
<TreeState
key="treestate"
dataset={stories}
prefix="explorer"
selectedId={storyId}
filter=""
List={List}
Head={SidebarItem}
Link={Link}
Leaf={SidebarItem}
Title={Subheading}
Section={Section}
Message={Placeholder}
// eslint-disable-next-line react/jsx-no-duplicate-props
Filter={Search}
{...rest}
/>
</Wrapper>
);
}
);
export default SidebarStories;