Skip to content

Commit

Permalink
Merge pull request #25787 from storybookjs/charles-fix-mobile-addon-p…
Browse files Browse the repository at this point in the history
…anel

UI: Improve how the addon panel work on mobile
  • Loading branch information
cdedreuille authored Jan 30, 2024
2 parents 60277c5 + e43d14a commit 0b53fa0
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 84 deletions.
16 changes: 2 additions & 14 deletions code/lib/router/src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React, { useCallback } from 'react';
import type { ReactNode, ReactElement, ComponentProps } from 'react';

import * as R from 'react-router-dom';
import { ToggleVisibility } from './visibility';
import { queryFromString, parsePath, getMatch } from './utils';
import type { LinkProps, NavigateOptions, RenderData } from './types';

Expand Down Expand Up @@ -31,13 +30,11 @@ interface MatchPropsDefault {
interface RoutePropsStartsWith {
path: string;
startsWith?: boolean;
hideOnly?: boolean;
children: ReactNode;
}
interface RoutePropsDefault {
path: RegExp;
startsWith?: false;
hideOnly?: boolean;
children: ReactNode;
}

Expand Down Expand Up @@ -128,23 +125,14 @@ Match.displayName = 'QueryMatch';
function Route(props: RoutePropsDefault): ReactElement;
function Route(props: RoutePropsStartsWith): ReactElement;
function Route(input: RoutePropsDefault | RoutePropsStartsWith) {
const { children, hideOnly, ...rest } = input;
const { children, ...rest } = input;
if (rest.startsWith === undefined) {
rest.startsWith = false;
}

const matchProps = rest as Omit<ComponentProps<typeof Match>, 'children'>;

return (
<Match {...matchProps}>
{({ match }) => {
if (hideOnly) {
return <ToggleVisibility hidden={!match}>{children}</ToggleVisibility>;
}
return match ? children : null;
}}
</Match>
);
return <Match {...matchProps}>{({ match }) => (match ? children : null)}</Match>;
}
Route.displayName = 'Route';

Expand Down
9 changes: 1 addition & 8 deletions code/ui/manager/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import type { ComponentProps } from 'react';
import React from 'react';

import { Route } from '@storybook/router';

import { Global, createGlobal } from '@storybook/theming';
import type { Addon_PageType } from '@storybook/types';
import Sidebar from './container/Sidebar';
Expand All @@ -27,11 +24,7 @@ export const App = ({ managerLayoutState, setManagerLayoutState, pages }: Props)
<Layout
managerLayoutState={managerLayoutState}
setManagerLayoutState={setManagerLayoutState}
slotMain={
<Route path={/(^\/story|docs|onboarding\/|^\/$)/} hideOnly>
<Preview id="main" withLoader />
</Route>
}
slotMain={<Preview id="main" withLoader />}
slotSidebar={<Sidebar onMenuClick={() => setMobileAboutOpen((state) => !state)} />}
slotPanel={<Panel />}
slotPages={pages.map(({ id, render: Content }) => (
Expand Down
7 changes: 6 additions & 1 deletion code/ui/manager/src/components/layout/Layout.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { fn } from '@storybook/test';
import { Layout } from './Layout';
import { LayoutProvider } from './LayoutProvider';
import { LocationProvider } from '@storybook/router';
import MobileNavigationStoriesMeta from '../mobile/navigation/MobileNavigation.stories';

const PlaceholderBlock = styled.div({
Expand Down Expand Up @@ -67,7 +68,11 @@ const meta = {
},
decorators: [
MobileNavigationStoriesMeta.decorators[0] as any,
(storyFn) => <LayoutProvider>{storyFn()}</LayoutProvider>,
(storyFn) => (
<LocationProvider>
<LayoutProvider>{storyFn()}</LayoutProvider>
</LocationProvider>
),
],
render: (args) => {
const [managerLayoutState, setManagerLayoutState] = useState(args.managerLayoutState);
Expand Down
10 changes: 7 additions & 3 deletions code/ui/manager/src/components/layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { MobileNavigation } from '../mobile/navigation/MobileNavigation';
import { MEDIA_DESKTOP_BREAKPOINT } from '../../constants';
import { useLayout } from './LayoutProvider';
import { Notifications } from '../../container/Notifications';
import { Match } from '@storybook/router';

interface InternalLayoutState {
isDragging: boolean;
Expand Down Expand Up @@ -145,7 +146,9 @@ export const Layout = ({ managerLayoutState, setManagerLayoutState, ...slots }:
>
<Notifications />
{showPages && <PagesContainer>{slots.slotPages}</PagesContainer>}
<ContentContainer>{slots.slotMain}</ContentContainer>
<Match path={/(^\/story|docs|onboarding\/|^\/$)/} startsWith={false}>
{({ match }) => <ContentContainer shown={!!match}>{slots.slotMain}</ContentContainer>}
</Match>
{isDesktop && (
<>
<SidebarContainer>
Expand Down Expand Up @@ -210,11 +213,12 @@ const SidebarContainer = styled.div(({ theme }) => ({
borderRight: `1px solid ${theme.color.border}`,
}));

const ContentContainer = styled.div(({ theme }) => ({
const ContentContainer = styled.div<{ shown: boolean }>(({ theme, shown }) => ({
flex: 1,
position: 'relative',
backgroundColor: theme.background.content,
display: 'grid', // This is needed to make the content container fill the available space
display: shown ? 'grid' : 'none', // This is needed to make the content container fill the available space
overflow: 'auto',

[MEDIA_DESKTOP_BREAKPOINT]: {
flex: 'auto',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,59 +1,21 @@
import type { FC, ReactNode } from 'react';
import React, { useRef } from 'react';
import React from 'react';
import { styled } from '@storybook/theming';
import { Transition } from 'react-transition-group';
import type { TransitionStatus } from 'react-transition-group/Transition';
import { useLayout } from '../../layout/LayoutProvider';

interface MobileAddonsDrawerProps {
children: ReactNode;
}

const TRANSITION_DURATION = 200;

const Container = styled.div<{ state: TransitionStatus }>(({ theme, state }) => ({
position: 'fixed',
const Container = styled.div(({ theme }) => ({
position: 'relative',
boxSizing: 'border-box',
width: '100%',
background: theme.background.content,
height: '50%',
bottom: 0,
left: 0,
height: '42vh',
zIndex: 11,
transition: `all ${TRANSITION_DURATION}ms ease-in-out`,
overflow: 'hidden',
borderTop: `1px solid ${theme.appBorderColor}`,
transform: `${(() => {
switch (state) {
case 'entering':
case 'entered':
return 'translateY(0)';
case 'exiting':
case 'exited':
return 'translateY(100%)';
default:
return 'translateY(0)';
}
})()}`,
}));

export const MobileAddonsDrawer: FC<MobileAddonsDrawerProps> = ({ children }) => {
const { isMobilePanelOpen } = useLayout();
const containerRef = useRef(null);

return (
<Transition
nodeRef={containerRef}
in={isMobilePanelOpen}
timeout={TRANSITION_DURATION}
mountOnEnter
unmountOnExit
>
{(state) => (
<Container ref={containerRef} state={state}>
{children}
</Container>
)}
</Transition>
);
return <Container>{children}</Container>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -35,40 +35,50 @@ const useFullStoryName = () => {
};

export const MobileNavigation: FC<MobileNavigationProps> = ({ menu, panel, showPanel }) => {
const { isMobileMenuOpen, setMobileMenuOpen, setMobilePanelOpen } = useLayout();
const { isMobileMenuOpen, isMobilePanelOpen, setMobileMenuOpen, setMobilePanelOpen } =
useLayout();
const fullStoryName = useFullStoryName();

return (
<Container>
<MobileMenuDrawer>{menu}</MobileMenuDrawer>
<MobileAddonsDrawer>{panel}</MobileAddonsDrawer>
<Button onClick={() => setMobileMenuOpen(!isMobileMenuOpen)} title="Open navigation menu">
<MenuIcon />
<Text>{fullStoryName}</Text>
</Button>
{showPanel && (
<IconButton onClick={() => setMobilePanelOpen(true)} title="Open addon panel">
<BottomBarToggleIcon />
</IconButton>
{isMobilePanelOpen ? (
<MobileAddonsDrawer>{panel}</MobileAddonsDrawer>
) : (
<Nav>
<Button onClick={() => setMobileMenuOpen(!isMobileMenuOpen)} title="Open navigation menu">
<MenuIcon />
<Text>{fullStoryName}</Text>
</Button>
{showPanel && (
<IconButton onClick={() => setMobilePanelOpen(true)} title="Open addon panel">
<BottomBarToggleIcon />
</IconButton>
)}
</Nav>
)}
</Container>
);
};

const Container = styled.div(({ theme }) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
bottom: 0,
left: 0,
width: '100%',
height: 40,
zIndex: 10,
background: theme.barBg,
padding: '0 6px',
borderTop: `1px solid ${theme.appBorderColor}`,
}));

const Nav = styled.div({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
height: 40,
padding: '0 6px',
});

const Button = styled.button(({ theme }) => ({
all: 'unset',
display: 'flex',
Expand Down

0 comments on commit 0b53fa0

Please sign in to comment.