mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-04 21:11:07 +08:00
Merge pull request #11224 from storybookjs/composition-styling-qa
Composition QA
This commit is contained in:
commit
e313d777b9
@ -25,7 +25,7 @@ const ButtonWrapper = styled.button<ButtonWrapperProps>(
|
||||
cursor: 'pointer',
|
||||
display: 'inline-block',
|
||||
overflow: 'hidden',
|
||||
padding: small ? '10px 16px' : '13px 20px',
|
||||
padding: small ? '8px 16px' : '13px 20px',
|
||||
position: 'relative',
|
||||
textAlign: 'center',
|
||||
textDecoration: 'none',
|
||||
@ -56,7 +56,7 @@ const ButtonWrapper = styled.button<ButtonWrapperProps>(
|
||||
pointerEvents: 'none',
|
||||
|
||||
path: {
|
||||
fill: 'currentColor }',
|
||||
fill: 'currentColor',
|
||||
},
|
||||
},
|
||||
}),
|
||||
@ -154,8 +154,9 @@ const ButtonWrapper = styled.button<ButtonWrapperProps>(
|
||||
color: transparentize(0.3, theme.color.defaultText),
|
||||
background: 'transparent',
|
||||
|
||||
'&:hover': {
|
||||
'&:hover, &:focus': {
|
||||
boxShadow: `${transparentize(0.5, theme.color.defaultText)} 0 0 0 1px inset`,
|
||||
outline: 'none',
|
||||
},
|
||||
|
||||
'&:active': {
|
||||
@ -184,7 +185,7 @@ const ButtonWrapper = styled.button<ButtonWrapperProps>(
|
||||
'&:active': {
|
||||
background: color,
|
||||
boxShadow: `${color} 0 0 0 1px inset`,
|
||||
color: theme.color.lightest,
|
||||
color: theme.color.tertiary,
|
||||
},
|
||||
'&:focus': {
|
||||
boxShadow: `${color} 0 0 0 1px inset, ${rgba(color, 0.4)} 0 1px 9px 2px`,
|
||||
@ -221,7 +222,7 @@ const ButtonWrapper = styled.button<ButtonWrapperProps>(
|
||||
'&:active': {
|
||||
background: color,
|
||||
boxShadow: `${color} 0 0 0 1px inset`,
|
||||
color: theme.color.lightest,
|
||||
color: theme.color.tertiary,
|
||||
},
|
||||
'&:focus': {
|
||||
boxShadow: `${color} 0 0 0 1px inset, ${rgba(color, 0.4)} 0 1px 9px 2px`,
|
||||
|
@ -8,10 +8,11 @@ import React, {
|
||||
ComponentProps,
|
||||
} from 'react';
|
||||
|
||||
import { Icons, WithTooltip, Spaced, Button } from '@storybook/components';
|
||||
import { Icons, WithTooltip, Spaced, Button, Link } from '@storybook/components';
|
||||
import { logger } from '@storybook/client-logger';
|
||||
import { useStorybookApi } from '@storybook/api';
|
||||
import { styled } from '@storybook/theming';
|
||||
import { transparentize } from 'polished';
|
||||
import { Location } from '@storybook/router';
|
||||
|
||||
import { Tree } from './Tree/Tree';
|
||||
@ -31,17 +32,24 @@ const RootHeading = styled.div(({ theme }) => ({
|
||||
fontWeight: theme.typography.weight.black,
|
||||
fontSize: theme.typography.size.s1 - 1,
|
||||
lineHeight: '24px',
|
||||
color: theme.color.mediumdark,
|
||||
color: transparentize(0.5, theme.color.defaultText),
|
||||
margin: '0 20px',
|
||||
}));
|
||||
|
||||
const Text = styled.p(({ theme }) => ({
|
||||
fontSize: theme.typography.size.s2 - 1,
|
||||
lineHeight: '20px',
|
||||
margin: 0,
|
||||
}));
|
||||
|
||||
const RedIcon = styled(Icons)(({ theme }) => ({
|
||||
color: theme.color.negative,
|
||||
code: {
|
||||
fontSize: theme.typography.size.s1,
|
||||
},
|
||||
|
||||
ul: {
|
||||
paddingLeft: 20,
|
||||
marginTop: 8,
|
||||
marginBottom: 8,
|
||||
},
|
||||
}));
|
||||
|
||||
const Head: FunctionComponent<ListitemProps> = (props) => {
|
||||
@ -112,10 +120,10 @@ const firstLineRegex = /(Error): (.*)\n/;
|
||||
const linesRegex = /at (?:(.*) )?\(?(.+)\)?/;
|
||||
const ErrorFormatter: FunctionComponent<{ error: Error }> = ({ error }) => {
|
||||
if (!error) {
|
||||
return <Fragment>this error has no stack or message</Fragment>;
|
||||
return <Fragment>This error has no stack or message</Fragment>;
|
||||
}
|
||||
if (!error.stack) {
|
||||
return <Fragment>{error.message || 'this error has no stack or message'}</Fragment>;
|
||||
return <Fragment>{error.message || 'This error has no stack or message'}</Fragment>;
|
||||
}
|
||||
|
||||
const input = error.stack.toString();
|
||||
@ -192,23 +200,23 @@ export const AuthBlock: FunctionComponent<{ loginUrl: string; id: string }> = ({
|
||||
{isAuthAttempted ? (
|
||||
<Fragment>
|
||||
<Text>
|
||||
Authentication on <strong>{loginUrl}</strong> seems to have concluded, refresh the
|
||||
page to fetch this storybook
|
||||
Authentication on <strong>{loginUrl}</strong> concluded. Refresh the page to fetch
|
||||
this Storybook.
|
||||
</Text>
|
||||
<div>
|
||||
<Button small gray onClick={refresh}>
|
||||
<Icons icon="sync" />
|
||||
Refresh the page
|
||||
Refresh now
|
||||
</Button>
|
||||
</div>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment>
|
||||
<Text>Browse this secure storybook</Text>
|
||||
<Text>Browse this secure Storybook</Text>
|
||||
<div>
|
||||
<Button small gray onClick={open}>
|
||||
<Icons icon="lock" />
|
||||
Login
|
||||
Log in
|
||||
</Button>
|
||||
</div>
|
||||
</Fragment>
|
||||
@ -221,21 +229,27 @@ export const AuthBlock: FunctionComponent<{ loginUrl: string; id: string }> = ({
|
||||
export const ErrorBlock: FunctionComponent<{ error: Error }> = ({ error }) => (
|
||||
<Contained>
|
||||
<Spaced>
|
||||
<Text>Ow no! something went wrong loading this storybook</Text>
|
||||
<WithTooltip
|
||||
trigger="click"
|
||||
closeOnClick={false}
|
||||
tooltip={
|
||||
<ErrorDisplay>
|
||||
<ErrorFormatter error={error} />
|
||||
</ErrorDisplay>
|
||||
}
|
||||
>
|
||||
<Button small gray>
|
||||
<Icons icon="doclist" />
|
||||
View error
|
||||
</Button>
|
||||
</WithTooltip>
|
||||
<Text>
|
||||
Oh no! Something went wrong loading this Storybook.
|
||||
<br />
|
||||
<WithTooltip
|
||||
trigger="click"
|
||||
closeOnClick={false}
|
||||
tooltip={
|
||||
<ErrorDisplay>
|
||||
<ErrorFormatter error={error} />
|
||||
</ErrorDisplay>
|
||||
}
|
||||
>
|
||||
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
||||
<Link isButton>
|
||||
View error <Icons icon="arrowdown" />
|
||||
</Link>
|
||||
</WithTooltip>{' '}
|
||||
<Link withArrow href="https://storybook.js.org/docs" cancel={false} target="_blank">
|
||||
View docs
|
||||
</Link>
|
||||
</Text>
|
||||
</Spaced>
|
||||
</Contained>
|
||||
);
|
||||
@ -251,30 +265,22 @@ export const EmptyBlock: FunctionComponent<any> = ({ isMain }) => (
|
||||
<Contained>
|
||||
<FlexSpaced col={1}>
|
||||
<WideSpaced>
|
||||
{/* */}
|
||||
<div>
|
||||
<Text>
|
||||
<strong>Ow now! {isMain ? 'Your storybook' : 'this ref'} is empty!</strong>
|
||||
</Text>
|
||||
</div>
|
||||
{isMain ? (
|
||||
<Fragment>
|
||||
<div>
|
||||
<Text>
|
||||
Perhaps the glob specified in <code>main.js</code> found no files?
|
||||
</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text>Or your story-files don't define any stories?</Text>
|
||||
</div>
|
||||
</Fragment>
|
||||
) : (
|
||||
<div>
|
||||
<Text>This ref defined no stories</Text>
|
||||
</div>
|
||||
)}
|
||||
<Text>
|
||||
{isMain ? (
|
||||
<>
|
||||
Oh no! Your Storybook is empty. Possible reasons why:
|
||||
<ul>
|
||||
<li>
|
||||
The glob specified in <code>main.js</code> isn't correct.
|
||||
</li>
|
||||
<li>No stories are defined in your story files.</li>
|
||||
</ul>{' '}
|
||||
</>
|
||||
) : (
|
||||
<>Yikes! Something went wrong loading these stories.</>
|
||||
)}
|
||||
</Text>
|
||||
</WideSpaced>
|
||||
<RedIcon icon="info" width={14} height={14} />
|
||||
</FlexSpaced>
|
||||
</Contained>
|
||||
);
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { document, window } from 'global';
|
||||
import React, { FunctionComponent, useMemo, ComponentProps, useCallback, forwardRef } from 'react';
|
||||
|
||||
import { Icons, WithTooltip, Spaced, TooltipLinkList } from '@storybook/components';
|
||||
import { styled } from '@storybook/theming';
|
||||
import { transparentize } from 'polished';
|
||||
import { useStorybookApi } from '@storybook/api';
|
||||
|
||||
import { getStateType, RefType } from './RefHelpers';
|
||||
@ -16,42 +18,54 @@ export interface CurrentVersionProps {
|
||||
versions: RefType['versions'];
|
||||
}
|
||||
|
||||
const IndicatorPlacement = styled.aside(
|
||||
({ theme }) => ({
|
||||
height: 14,
|
||||
display: 'flex',
|
||||
const IndicatorPlacement = styled.aside(({ theme }) => ({
|
||||
height: 16,
|
||||
|
||||
'& > * + *': {
|
||||
marginLeft: theme.layoutMargin,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
|
||||
'& > * + *': {
|
||||
marginLeft: theme.layoutMargin,
|
||||
},
|
||||
}));
|
||||
|
||||
const IndicatorClickTarget = styled.span(({ theme }) => ({
|
||||
height: 16,
|
||||
width: 16,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
|
||||
svg: {
|
||||
height: 12,
|
||||
width: 12,
|
||||
transition: 'all 150ms ease-out',
|
||||
color:
|
||||
theme.base === 'light'
|
||||
? transparentize(0.3, theme.color.defaultText)
|
||||
: transparentize(0.6, theme.color.defaultText),
|
||||
|
||||
'&:hover': {
|
||||
color: theme.barSelectedColor,
|
||||
},
|
||||
}),
|
||||
({ theme }) => ({ color: theme.color.mediumdark })
|
||||
);
|
||||
},
|
||||
}));
|
||||
|
||||
const IndicatorIcon: FunctionComponent<IndicatorIconProps> = ({ type }) => {
|
||||
let icon: ComponentProps<typeof Icons>['icon'];
|
||||
|
||||
switch (true) {
|
||||
case type === 'error': {
|
||||
icon = 'alert';
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
icon = 'globe';
|
||||
}
|
||||
}
|
||||
|
||||
return <Icons width="14" height="14" icon={icon} />;
|
||||
};
|
||||
const MessageTitle = styled.span(({ theme }) => ({
|
||||
fontWeight: theme.typography.weight.bold,
|
||||
}));
|
||||
|
||||
const Message = styled.a(({ theme }) => ({
|
||||
textDecoration: 'none',
|
||||
lineHeight: '18px',
|
||||
padding: 10,
|
||||
lineHeight: '16px',
|
||||
padding: 15,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
color: theme.color.darker,
|
||||
color: theme.color.defaultText,
|
||||
'&:not(:last-child)': {
|
||||
borderBottom: `1px solid ${theme.appBorderColor}`,
|
||||
},
|
||||
'&:hover': {
|
||||
background: theme.background.hoverable,
|
||||
color: theme.color.darker,
|
||||
@ -69,8 +83,9 @@ const Message = styled.a(({ theme }) => ({
|
||||
flex: 1,
|
||||
},
|
||||
'& > svg': {
|
||||
marginTop: 5,
|
||||
width: 20,
|
||||
marginTop: 3,
|
||||
width: 16,
|
||||
height: 16,
|
||||
marginRight: 10,
|
||||
flex: 'unset',
|
||||
},
|
||||
@ -91,15 +106,33 @@ const YellowIcon = styled(Icons)(({ theme }) => ({
|
||||
color: theme.color.gold,
|
||||
}));
|
||||
|
||||
const Version = styled.div({
|
||||
const RedIcon = styled(Icons)(({ theme }) => ({
|
||||
color: theme.color.negative,
|
||||
}));
|
||||
|
||||
const GreenIcon = styled(Icons)(({ theme }) => ({
|
||||
color: theme.color.green,
|
||||
}));
|
||||
|
||||
const Version = styled.div(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
fontSize: 11,
|
||||
fontSize: theme.typography.size.s1,
|
||||
fontWeight: theme.typography.weight.regular,
|
||||
color:
|
||||
theme.base === 'light'
|
||||
? transparentize(0.3, theme.color.defaultText)
|
||||
: transparentize(0.6, theme.color.defaultText),
|
||||
|
||||
'& > * + *': {
|
||||
marginLeft: 4,
|
||||
},
|
||||
});
|
||||
|
||||
svg: {
|
||||
height: 10,
|
||||
width: 10,
|
||||
},
|
||||
}));
|
||||
|
||||
const CurrentVersion: FunctionComponent<CurrentVersionProps> = ({ url, versions }) => {
|
||||
const currentVersionId = useMemo(() => {
|
||||
@ -110,7 +143,7 @@ const CurrentVersion: FunctionComponent<CurrentVersionProps> = ({ url, versions
|
||||
return (
|
||||
<Version>
|
||||
<span>{currentVersionId}</span>
|
||||
<Icons width="12" height="12" icon="chevrondown" />
|
||||
<Icons icon="chevrondown" />
|
||||
</Version>
|
||||
);
|
||||
};
|
||||
@ -143,16 +176,20 @@ export const RefIndicator = forwardRef<
|
||||
<MessageWrapper>
|
||||
<Spaced row={0}>
|
||||
{state === 'loading' && <LoadingMessage url={ref.url} />}
|
||||
{(state === 'error' || state === 'empty') && <ErrorOccurredMessage url={ref.url} />}
|
||||
{state === 'ready' && (
|
||||
<ReadyMessage {...{ url: ref.url, componentCount, leafCount }} />
|
||||
)}
|
||||
{state === 'auth' && <LoginRequiredMessage {...ref} />}
|
||||
{ref.type === 'auto-inject' && state !== 'error' && <PerformanceDegradedMessage />}
|
||||
{state === 'error' && <ErrorOccurredMessage />}
|
||||
{state !== 'loading' && <ReadDocsMessage />}
|
||||
</Spaced>
|
||||
</MessageWrapper>
|
||||
}
|
||||
>
|
||||
<IndicatorIcon type={state} />
|
||||
<IndicatorClickTarget>
|
||||
<Icons icon="globe" />
|
||||
</IndicatorClickTarget>
|
||||
</WithTooltip>
|
||||
|
||||
{ref.versions ? (
|
||||
@ -178,26 +215,6 @@ export const RefIndicator = forwardRef<
|
||||
);
|
||||
});
|
||||
|
||||
const PerformanceDegradedMessage: FunctionComponent = () => (
|
||||
<Message href="https://storybook.js.org" target="_blank">
|
||||
<YellowIcon icon="lightning" />
|
||||
<div>
|
||||
<strong>Reduce lag</strong>
|
||||
<div>Learn how to speed up Storybook Composition performance</div>
|
||||
</div>
|
||||
</Message>
|
||||
);
|
||||
|
||||
const ErrorOccurredMessage: FunctionComponent = () => (
|
||||
<Message href="https://storybook.js.org" target="_blank">
|
||||
<YellowIcon icon="book" />
|
||||
<div>
|
||||
<strong>A problem occurred</strong>
|
||||
<div>Explore the documentation</div>
|
||||
</div>
|
||||
</Message>
|
||||
);
|
||||
|
||||
const ReadyMessage: FunctionComponent<{
|
||||
url: string;
|
||||
componentCount: number;
|
||||
@ -206,20 +223,77 @@ const ReadyMessage: FunctionComponent<{
|
||||
<Message href={url} target="_blank">
|
||||
<BlueIcon icon="globe" />
|
||||
<div>
|
||||
<strong>View external storybook</strong>
|
||||
<MessageTitle>View external Storybook</MessageTitle>
|
||||
<div>
|
||||
Explore {componentCount} components and {leafCount} stories in a new browser tab
|
||||
Explore {componentCount} components and {leafCount} stories in a new browser tab.
|
||||
</div>
|
||||
</div>
|
||||
</Message>
|
||||
);
|
||||
|
||||
const LoginRequiredMessage: FunctionComponent<RefType> = ({ loginUrl, id }) => {
|
||||
const open = useCallback((e) => {
|
||||
e.preventDefault();
|
||||
const childWindow = window.open(loginUrl, `storybook_auth_${id}`, 'resizable,scrollbars');
|
||||
|
||||
// poll for window to close
|
||||
const timer = setInterval(() => {
|
||||
if (!childWindow) {
|
||||
clearInterval(timer);
|
||||
} else if (childWindow.closed) {
|
||||
clearInterval(timer);
|
||||
document.location.reload();
|
||||
}
|
||||
}, 1000);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Message onClick={open}>
|
||||
<YellowIcon icon="lock" />
|
||||
<div>
|
||||
<MessageTitle>Log in required</MessageTitle>
|
||||
<div>You need to authenticate to view this Storybook's components.</div>
|
||||
</div>
|
||||
</Message>
|
||||
);
|
||||
};
|
||||
|
||||
const ReadDocsMessage: FunctionComponent = () => (
|
||||
<Message href="https://storybook.js.org" target="_blank">
|
||||
<GreenIcon icon="document" />
|
||||
<div>
|
||||
<MessageTitle>Read Composition docs</MessageTitle>
|
||||
<div>Learn how to combine multiple Storybooks into one.</div>
|
||||
</div>
|
||||
</Message>
|
||||
);
|
||||
|
||||
const ErrorOccurredMessage: FunctionComponent<{ url: string }> = ({ url }) => (
|
||||
<Message href={url} target="_blank">
|
||||
<RedIcon icon="alert" />
|
||||
<div>
|
||||
<MessageTitle>Something went wrong</MessageTitle>
|
||||
<div>This external Storybook didn't load. Debug it in a new tab now.</div>
|
||||
</div>
|
||||
</Message>
|
||||
);
|
||||
|
||||
const LoadingMessage: FunctionComponent<{ url: string }> = ({ url }) => (
|
||||
<Message href={url} target="_blank">
|
||||
<BlueIcon icon="time" />
|
||||
<div>
|
||||
<strong>Please wait</strong>
|
||||
<div>This storybook is being loaded, explore in a new browser tab</div>
|
||||
<MessageTitle>Please wait</MessageTitle>
|
||||
<div>This Storybook is loading.</div>
|
||||
</div>
|
||||
</Message>
|
||||
);
|
||||
|
||||
const PerformanceDegradedMessage: FunctionComponent = () => (
|
||||
<Message href="https://storybook.js.org/docs" target="_blank">
|
||||
<YellowIcon icon="lightning" />
|
||||
<div>
|
||||
<MessageTitle>Reduce lag</MessageTitle>
|
||||
<div>Learn how to speed up Composition performance.</div>
|
||||
</div>
|
||||
</Message>
|
||||
);
|
||||
|
132
lib/ui/src/components/sidebar/Refs.stories.tsx
Normal file
132
lib/ui/src/components/sidebar/Refs.stories.tsx
Normal file
@ -0,0 +1,132 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Ref } from './Refs';
|
||||
import { standardData as standardHeaderData } from './Heading.stories';
|
||||
import { mockDataset } from './mockdata';
|
||||
import { RefType } from './RefHelpers';
|
||||
|
||||
export default {
|
||||
component: Ref,
|
||||
title: 'UI/Sidebar/Refs',
|
||||
excludeStories: /.*Data$/,
|
||||
};
|
||||
|
||||
const { menu } = standardHeaderData;
|
||||
const stories = mockDataset.withRoot;
|
||||
const storyId = '1-12-121';
|
||||
|
||||
export const simpleData = { menu, stories, storyId };
|
||||
export const loadingData = { menu, stories: {} };
|
||||
|
||||
const error: Error = (() => {
|
||||
try {
|
||||
throw new Error('There was a severe problem');
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
})();
|
||||
|
||||
const refs: Record<string, RefType> = {
|
||||
optimized: {
|
||||
id: 'optimized',
|
||||
title: 'It is optimized',
|
||||
url: 'https://example.com',
|
||||
ready: false,
|
||||
type: 'lazy',
|
||||
stories,
|
||||
},
|
||||
empty: {
|
||||
id: 'empty',
|
||||
title: 'It is empty because no stories were loaded',
|
||||
url: 'https://example.com',
|
||||
ready: false,
|
||||
type: 'lazy',
|
||||
stories: {},
|
||||
},
|
||||
startInjected_unknown: {
|
||||
id: 'startInjected_unknown',
|
||||
title: 'It started injected and is unknown',
|
||||
url: 'https://example.com',
|
||||
type: 'unknown',
|
||||
ready: false,
|
||||
stories,
|
||||
},
|
||||
startInjected_loading: {
|
||||
id: 'startInjected_loading',
|
||||
title: 'It started injected and is loading',
|
||||
url: 'https://example.com',
|
||||
type: 'auto-inject',
|
||||
ready: false,
|
||||
stories,
|
||||
},
|
||||
startInjected_ready: {
|
||||
id: 'startInjected_ready',
|
||||
title: 'It started injected and is ready',
|
||||
url: 'https://example.com',
|
||||
type: 'auto-inject',
|
||||
ready: true,
|
||||
stories,
|
||||
},
|
||||
versions: {
|
||||
id: 'versions',
|
||||
title: 'It has versions',
|
||||
url: 'https://example.com',
|
||||
type: 'lazy',
|
||||
stories,
|
||||
versions: { '1.0.0': 'https://example.com/v1', '2.0.0': 'https://example.com' },
|
||||
},
|
||||
versionsMissingCurrent: {
|
||||
id: 'versions_missing_current',
|
||||
title: 'It has versions',
|
||||
url: 'https://example.com',
|
||||
type: 'lazy',
|
||||
stories,
|
||||
versions: { '1.0.0': 'https://example.com/v1', '2.0.0': 'https://example.com/v2' },
|
||||
},
|
||||
error: {
|
||||
id: 'error',
|
||||
title: 'This has problems',
|
||||
url: 'https://example.com',
|
||||
type: 'lazy',
|
||||
stories: {},
|
||||
error,
|
||||
},
|
||||
auth: {
|
||||
id: 'Authentication',
|
||||
title: 'This requires a login',
|
||||
url: 'https://example.com',
|
||||
type: 'lazy',
|
||||
stories: {},
|
||||
loginUrl: 'https://example.com',
|
||||
},
|
||||
long: {
|
||||
id: 'long',
|
||||
title: 'This storybook has a very very long name for some reason',
|
||||
url: 'https://example.com',
|
||||
stories,
|
||||
type: 'lazy',
|
||||
versions: {
|
||||
'111.111.888-new': 'https://example.com/new',
|
||||
'111.111.888': 'https://example.com',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const Optimized = () => <Ref {...refs.optimized} storyId="" filter="" isHidden={false} />;
|
||||
export const IsEmpty = () => <Ref {...refs.empty} storyId="" filter="" isHidden={false} />;
|
||||
export const StartInjectedUnknown = () => (
|
||||
<Ref {...refs.startInjected_unknown} storyId="" filter="" isHidden={false} />
|
||||
);
|
||||
export const StartInjectedLoading = () => (
|
||||
<Ref {...refs.startInjected_loading} storyId="" filter="" isHidden={false} />
|
||||
);
|
||||
export const StartInjectedReady = () => (
|
||||
<Ref {...refs.startInjected_ready} storyId="" filter="" isHidden={false} />
|
||||
);
|
||||
export const Versions = () => <Ref {...refs.versions} storyId="" filter="" isHidden={false} />;
|
||||
export const VersionsMissingCurrent = () => (
|
||||
<Ref {...refs.versionsMissingCurrent} storyId="" filter="" isHidden={false} />
|
||||
);
|
||||
export const Errored = () => <Ref {...refs.error} storyId="" filter="" isHidden={false} />;
|
||||
export const Auth = () => <Ref {...refs.auth} storyId="" filter="" isHidden={false} />;
|
||||
export const Long = () => <Ref {...refs.long} storyId="" filter="" isHidden={false} />;
|
@ -1,5 +1,6 @@
|
||||
import React, { FunctionComponent, MouseEvent, useMemo, useState, useRef } from 'react';
|
||||
import React, { FunctionComponent, useMemo, useState, useRef, useCallback } from 'react';
|
||||
import { styled } from '@storybook/theming';
|
||||
import { transparentize } from 'polished';
|
||||
|
||||
import { ExpanderContext, useDataset } from './Tree/State';
|
||||
import { Expander } from './Tree/ListItem';
|
||||
@ -13,41 +14,53 @@ export interface RefProps {
|
||||
isHidden: boolean;
|
||||
}
|
||||
|
||||
const RefHead = styled.button({
|
||||
alignItems: 'center',
|
||||
background: 'transparent',
|
||||
const RefHead = styled.button(({ theme }) => ({
|
||||
// Reset button
|
||||
border: 'none',
|
||||
boxSizing: 'content-box',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
marginLeft: -20,
|
||||
padding: 0,
|
||||
paddingLeft: 20,
|
||||
position: 'relative',
|
||||
textAlign: 'left',
|
||||
|
||||
fontWeight: theme.typography.weight.black,
|
||||
fontSize: theme.typography.size.s2 - 1,
|
||||
|
||||
// Similar to ListItem.tsx
|
||||
textDecoration: 'none',
|
||||
lineHeight: '16px',
|
||||
paddingTop: 4,
|
||||
paddingBottom: 4,
|
||||
paddingRight: theme.layoutMargin * 2,
|
||||
paddingLeft: 20, // 1px more padding than ListItem for optical correction
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
background: 'transparent',
|
||||
|
||||
marginLeft: -20,
|
||||
width: '100%',
|
||||
});
|
||||
|
||||
const RefTitle = styled.header(({ theme }) => ({
|
||||
fontWeight: theme.typography.weight.bold,
|
||||
fontSize: theme.typography.size.s2,
|
||||
color: theme.color.darkest,
|
||||
color:
|
||||
theme.base === 'light' ? theme.color.defaultText : transparentize(0.2, theme.color.defaultText),
|
||||
'&:hover, &:focus': {
|
||||
outline: 'none',
|
||||
color: theme.color.defaultText,
|
||||
background: theme.background.hoverable,
|
||||
},
|
||||
}));
|
||||
|
||||
flex: 1,
|
||||
height: 24,
|
||||
const RefTitle = styled.span(({ theme }) => ({
|
||||
display: 'block',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
paddingRight: theme.layoutMargin,
|
||||
flex: 1,
|
||||
overflow: 'hidden',
|
||||
|
||||
lineHeight: '24px',
|
||||
}));
|
||||
|
||||
const Wrapper = styled.div<{ isMain: boolean }>(({ isMain }) => ({
|
||||
position: 'relative',
|
||||
marginLeft: -20,
|
||||
marginRight: -20,
|
||||
marginTop: isMain ? undefined : 4,
|
||||
marginTop: isMain ? undefined : 0,
|
||||
}));
|
||||
|
||||
export const Ref: FunctionComponent<RefType & RefProps> = (ref) => {
|
||||
@ -61,11 +74,9 @@ export const Ref: FunctionComponent<RefType & RefProps> = (ref) => {
|
||||
storyId
|
||||
);
|
||||
|
||||
const handleClick = ({ target }: MouseEvent) => {
|
||||
// Don't fire if the click is from the indicator.
|
||||
if (target === indicatorRef.current || indicatorRef.current?.contains(target as Node)) return;
|
||||
const handleClick = useCallback(() => {
|
||||
setIsExpanded(!isExpanded);
|
||||
};
|
||||
}, [isExpanded]);
|
||||
|
||||
const combo = useMemo(() => ({ setExpanded, expandedSet }), [setExpanded, expandedSet]);
|
||||
|
||||
@ -87,11 +98,16 @@ export const Ref: FunctionComponent<RefType & RefProps> = (ref) => {
|
||||
<RefHead
|
||||
aria-label={`${isExpanded ? 'Hide' : 'Show'} ${title} stories`}
|
||||
aria-expanded={isExpanded}
|
||||
type="button"
|
||||
onClick={handleClick}
|
||||
>
|
||||
<Expander className="sidebar-ref-expander" depth={0} isExpanded={isExpanded} />
|
||||
<RefTitle title={title}>{title}</RefTitle>
|
||||
<Expander
|
||||
onClick={handleClick}
|
||||
className="sidebar-ref-expander"
|
||||
depth={0}
|
||||
isExpanded={isExpanded}
|
||||
/>
|
||||
<RefTitle onClick={handleClick} title={title}>
|
||||
{title}
|
||||
</RefTitle>
|
||||
<RefIndicator {...ref} state={state} ref={indicatorRef} />
|
||||
</RefHead>
|
||||
)}
|
||||
|
@ -21,93 +21,12 @@ export const loadingData = { menu, stories: {} };
|
||||
const refs: Record<string, RefType> = {
|
||||
optimized: {
|
||||
id: 'optimized',
|
||||
title: 'It is optimized',
|
||||
title: 'This is a ref',
|
||||
url: 'https://example.com',
|
||||
ready: false,
|
||||
type: 'lazy',
|
||||
stories,
|
||||
},
|
||||
empty: {
|
||||
id: 'empty',
|
||||
title: 'It is empty',
|
||||
url: 'https://example.com',
|
||||
ready: false,
|
||||
type: 'lazy',
|
||||
stories: {},
|
||||
},
|
||||
startInjected_unknown: {
|
||||
id: 'startInjected_unknown',
|
||||
title: 'It started injected and is ready',
|
||||
url: 'https://example.com',
|
||||
type: 'unknown',
|
||||
ready: false,
|
||||
stories,
|
||||
},
|
||||
startInjected_loading: {
|
||||
id: 'startInjected_loading',
|
||||
title: 'It started injected and is loading',
|
||||
url: 'https://example.com',
|
||||
type: 'auto-inject',
|
||||
ready: false,
|
||||
stories,
|
||||
},
|
||||
startInjected_ready: {
|
||||
id: 'startInjected_ready',
|
||||
title: 'It started injected and is ready',
|
||||
url: 'https://example.com',
|
||||
type: 'auto-inject',
|
||||
ready: true,
|
||||
stories,
|
||||
},
|
||||
versions: {
|
||||
id: 'versions',
|
||||
title: 'It has versions',
|
||||
url: 'https://example.com',
|
||||
type: 'lazy',
|
||||
stories,
|
||||
versions: { '1.0.0': 'https://example.com/v1', '2.0.0': 'https://example.com' },
|
||||
},
|
||||
versionsMissingCurrent: {
|
||||
id: 'versions_missing_current',
|
||||
title: 'It has versions',
|
||||
url: 'https://example.com',
|
||||
type: 'lazy',
|
||||
stories,
|
||||
versions: { '1.0.0': 'https://example.com/v1', '2.0.0': 'https://example.com/v2' },
|
||||
},
|
||||
error: {
|
||||
id: 'error',
|
||||
title: 'This has problems',
|
||||
url: 'https://example.com',
|
||||
type: 'lazy',
|
||||
stories: {},
|
||||
error: (() => {
|
||||
try {
|
||||
throw new Error('There was a severe problem');
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
})(),
|
||||
},
|
||||
auth: {
|
||||
id: 'Authentication',
|
||||
title: 'This requires a login',
|
||||
url: 'https://example.com',
|
||||
type: 'lazy',
|
||||
stories: {},
|
||||
loginUrl: 'https://example.com',
|
||||
},
|
||||
long: {
|
||||
id: 'long',
|
||||
title: 'This storybook has a very very long name for some reason',
|
||||
url: 'https://example.com',
|
||||
stories,
|
||||
type: 'lazy',
|
||||
versions: {
|
||||
'111.111.888-new': 'https://example.com/new',
|
||||
'111.111.888': 'https://example.com',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const simple = () => (
|
||||
|
@ -23,6 +23,10 @@ const Container = styled.nav({
|
||||
height: '100%',
|
||||
});
|
||||
|
||||
const StyledSpaced = styled(Spaced)({
|
||||
paddingBottom: '2.5rem',
|
||||
});
|
||||
|
||||
const CustomScrollArea = styled(ScrollArea)({
|
||||
'&&&&& .os-scrollbar-handle:before': {
|
||||
left: -12,
|
||||
@ -37,7 +41,7 @@ const Hr = styled.hr(({ theme }) => ({
|
||||
border: '0 none',
|
||||
height: 0,
|
||||
marginBottom: 0,
|
||||
borderTop: `1px solid ${theme.color.mediumlight}`,
|
||||
borderTop: `1px solid ${theme.appBorderColor}`,
|
||||
}));
|
||||
|
||||
export interface SidebarProps {
|
||||
@ -114,7 +118,7 @@ const Sidebar: FunctionComponent<SidebarProps> = ({
|
||||
return (
|
||||
<Container className="container sidebar-container">
|
||||
<CustomScrollArea vertical>
|
||||
<Spaced row={2}>
|
||||
<StyledSpaced row={1.6}>
|
||||
<Heading className="sidebar-header" menuHighlighted={menuHighlighted} menu={menu} />
|
||||
|
||||
<Search key="filter" onChange={setFilter} />
|
||||
@ -133,7 +137,7 @@ const Sidebar: FunctionComponent<SidebarProps> = ({
|
||||
);
|
||||
})}
|
||||
</Fragment>
|
||||
</Spaced>
|
||||
</StyledSpaced>
|
||||
</CustomScrollArea>
|
||||
</Container>
|
||||
);
|
||||
|
@ -72,7 +72,7 @@ export const Item = styled.a<{
|
||||
({ theme }) => ({
|
||||
position: 'relative',
|
||||
textDecoration: 'none',
|
||||
fontSize: theme.typography.size.s2,
|
||||
fontSize: theme.typography.size.s2 - 1,
|
||||
lineHeight: '16px',
|
||||
paddingTop: 4,
|
||||
paddingBottom: 4,
|
||||
@ -99,9 +99,10 @@ export const Item = styled.a<{
|
||||
theme.base === 'light'
|
||||
? theme.color.defaultText
|
||||
: transparentize(0.2, theme.color.defaultText),
|
||||
'&:hover': {
|
||||
'&:hover, &:focus': {
|
||||
color: theme.color.defaultText,
|
||||
background: theme.background.hoverable,
|
||||
outline: 'none',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user