Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI: Add bottom navigation on mobile #24228

Merged
merged 31 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e917749
Bring back mobile drawers
cdedreuille Sep 18, 2023
6302b90
Add menu icon
cdedreuille Sep 18, 2023
246fbe4
Merge branch 'charles-sb-mobile' into charles-mobile-menu-drawers
cdedreuille Sep 18, 2023
3fbb783
Improve icons in mobile navigation
cdedreuille Sep 18, 2023
216c208
Merge branch 'charles-sb-mobile' into charles-mobile-menu-drawers
cdedreuille Sep 18, 2023
ca9c691
Merge branch 'charles-mobile-1' into charles-mobile-2
cdedreuille Sep 19, 2023
294b1b5
Merge branch 'new-layout' into charles-mobile-2
cdedreuille Sep 19, 2023
b8f57dc
Merge branch 'new-layout' into charles-mobile-2
cdedreuille Sep 19, 2023
5e9e94c
Add the About for mobile
cdedreuille Sep 19, 2023
471dd5c
About is now working
cdedreuille Sep 20, 2023
86fd48c
Replace mobile components
cdedreuille Sep 20, 2023
cab08c5
Fix dark mode on mobile
cdedreuille Sep 20, 2023
3f5c5fb
Improved upgradeBlock
cdedreuille Sep 20, 2023
07f423c
Update About page to take the new upgrade block
cdedreuille Sep 20, 2023
faa5fb1
Update MobileAbout.tsx
cdedreuille Sep 20, 2023
56feda8
Fix issue when clicking on search
cdedreuille Sep 20, 2023
7a105d2
Fix CI errors
cdedreuille Sep 20, 2023
5218bab
Update Panel.tsx
cdedreuille Sep 20, 2023
d41bc20
Move sidebar and panel outside of each components
cdedreuille Sep 20, 2023
82a98d8
use context for layout states
JReinhold Sep 20, 2023
65127d9
improve closing about page
JReinhold Sep 20, 2023
568215d
add mobile stories
JReinhold Sep 20, 2023
d599393
cleanup
JReinhold Sep 20, 2023
1eec03b
disable mobile panel button in docs mode
JReinhold Sep 20, 2023
efa3aed
improve mobile stories
JReinhold Sep 20, 2023
1fd83dd
viewport in mobile about
JReinhold Sep 20, 2023
da1fbd5
fix types
JReinhold Sep 20, 2023
c950a98
fix drag handles when existing mobile
JReinhold Sep 20, 2023
ff336a1
fix about stories
JReinhold Sep 20, 2023
7bb3e8b
Merge branch 'new-layout' of github.com:storybookjs/storybook into ch…
JReinhold Sep 20, 2023
3ab7738
remove fullscreen button in mobile view
JReinhold Sep 20, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion code/builders/builder-manager/templates/template.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="utf-8" />

<title><%= typeof title !== 'undefined'? title : 'Storybook'%></title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />

<% if (favicon.endsWith('.svg')) {%>
<link rel="icon" type="image/svg+xml" href="./<%= favicon %>" />
Expand Down
2 changes: 2 additions & 0 deletions code/ui/manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"@storybook/theming": "workspace:*",
"@storybook/types": "workspace:*",
"@testing-library/react": "^11.2.2",
"@types/react-transition-group": "^4",
"@types/semver": "^7.3.4",
"browser-dtector": "^3.4.0",
"copy-to-clipboard": "^3.3.1",
Expand All @@ -80,6 +81,7 @@
"react-draggable": "^4.4.3",
"react-helmet-async": "^1.0.7",
"react-resize-detector": "^7.1.2",
"react-transition-group": "^4.4.5",
"resolve-from": "^5.0.0",
"semver": "^7.3.7",
"store2": "^2.14.2",
Expand Down
7 changes: 6 additions & 1 deletion code/ui/manager/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Preview from './container/Preview';
import Panel from './container/Panel';

import { Layout } from './components/layout/Layout';
import { useMobileLayoutContext } from './components/mobile/MobileLayoutProvider';

type Props = {
managerLayoutState: ComponentProps<typeof Layout>['managerLayoutState'];
Expand All @@ -19,6 +20,8 @@ type Props = {
};

export const App = ({ managerLayoutState, setManagerLayoutState, pages }: Props) => {
const mobileLayoutContext = useMobileLayoutContext();

return (
<>
<Global styles={createGlobal} />
Expand All @@ -31,7 +34,9 @@ export const App = ({ managerLayoutState, setManagerLayoutState, pages }: Props)
<Preview id="main" withLoader />
</Route>
}
slotSidebar={<Sidebar />}
slotSidebar={
<Sidebar onMenuClick={() => mobileLayoutContext.setMobileAboutOpen((state) => !state)} />
}
slotPanel={<Panel />}
slotPages={pages.map(({ id, render: Content }) => (
<Content key={id} />
Expand Down
18 changes: 18 additions & 0 deletions code/ui/manager/src/components/layout/Layout.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React, { useState } from 'react';

import { styled } from '@storybook/theming';
import type { Meta, StoryObj } from '@storybook/react';
import { ManagerContext } from '@storybook/manager-api';
import { Layout } from './Layout';

const PlaceholderBlock = styled.div({
Expand Down Expand Up @@ -61,6 +62,23 @@ const meta = {
theme: 'light',
layout: 'fullscreen',
},
decorators: [
(storyFn) => (
<ManagerContext.Provider
value={
{
api: {
getCurrentStoryData: () => ({
title: 'Some Story Title',
}),
},
} as any
}
>
{storyFn()}
</ManagerContext.Provider>
),
],
render: (args) => {
const [managerLayoutState, setManagerLayoutState] = useState(args.managerLayoutState);

Expand Down
12 changes: 9 additions & 3 deletions code/ui/manager/src/components/layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { styled } from '@storybook/theming';
import type { API_Layout, API_ViewMode } from '@storybook/types';
import { useDragging } from './useDragging';
import { useMediaQuery } from '../hooks/useMedia';
import { MobileNavigation } from '../mobile-navigation/MobileNavigation';
import { MobileNavigation } from '../mobile/navigation/MobileNavigation';
import { BREAKPOINT_MIN_600 } from '../../constants';

interface InternalLayoutState {
Expand All @@ -24,6 +24,7 @@ interface Props {
slotSidebar?: React.ReactNode;
slotPanel?: React.ReactNode;
slotPages?: React.ReactNode;
storyTitle?: string | null | undefined;
}
const MINIMUM_CONTENT_WIDTH_PX = 100;

Expand Down Expand Up @@ -112,7 +113,12 @@ const useLayoutSyncingState = (
};
};

export const Layout = ({ managerLayoutState, setManagerLayoutState, ...slots }: Props) => {
export const Layout = ({
managerLayoutState,
setManagerLayoutState,
storyTitle,
...slots
}: Props) => {
const isDesktop = useMediaQuery(BREAKPOINT_MIN_600);
const isMobile = !isDesktop;

Expand Down Expand Up @@ -157,7 +163,7 @@ export const Layout = ({ managerLayoutState, setManagerLayoutState, ...slots }:
)}
</>
)}
{isMobile && <MobileNavigation />}
{isMobile && <MobileNavigation sidebar={slots.slotSidebar} panel={slots.slotPanel} />}
</LayoutContainer>
);
};
Expand Down

This file was deleted.

31 changes: 31 additions & 0 deletions code/ui/manager/src/components/mobile/MobileLayoutProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { FC } from 'react';
import React, { createContext, useContext, useState } from 'react';

type MobileLayoutContextType = {
isMobileAboutOpen: boolean;
setMobileAboutOpen: React.Dispatch<React.SetStateAction<boolean>>;
isMobilePanelOpen: boolean;
setMobilePanelOpen: React.Dispatch<React.SetStateAction<boolean>>;
};

const MobileLayoutContext = createContext<MobileLayoutContextType>({
isMobileAboutOpen: false,
setMobileAboutOpen: () => {},
isMobilePanelOpen: false,
setMobilePanelOpen: () => {},
});

export const MobileLayoutProvider: FC = ({ children }) => {
const [isMobileAboutOpen, setMobileAboutOpen] = useState(false);
const [isMobilePanelOpen, setMobilePanelOpen] = useState(false);

return (
<MobileLayoutContext.Provider
value={{ isMobileAboutOpen, setMobileAboutOpen, isMobilePanelOpen, setMobilePanelOpen }}
>
{children}
</MobileLayoutContext.Provider>
);
};

export const useMobileLayoutContext = () => useContext(MobileLayoutContext);
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { Meta, StoryObj } from '@storybook/react';
import { ManagerContext } from '@storybook/manager-api';
import React, { useEffect } from 'react';
import { within } from '@storybook/testing-library';
import { MobileAbout } from './MobileAbout';
import { MobileLayoutProvider, useMobileLayoutContext } from '../MobileLayoutProvider';

const MockOpenAbout = ({ children }: { children: any }) => {
const { setMobileAboutOpen } = useMobileLayoutContext();
useEffect(() => {
setMobileAboutOpen(true);
}, [setMobileAboutOpen]);
return children;
};

const meta = {
component: MobileAbout,
title: 'Mobile/About',
decorators: [
(storyFn) => {
const { setMobileAboutOpen } = useMobileLayoutContext();
useEffect(() => {
setMobileAboutOpen(true);
}, [setMobileAboutOpen]);
return storyFn();
},
(storyFn) => {
return (
<ManagerContext.Provider
value={
{
api: {
getCurrentVersion: () => ({
version: '7.2.2-alpha.0',
}),
},
} as any
}
>
<MobileLayoutProvider>
<MockOpenAbout>{storyFn()}</MockOpenAbout>
</MobileLayoutProvider>
</ManagerContext.Provider>
);
},
],
} satisfies Meta<typeof MobileAbout>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {};

export const Closed: Story = {
play: async ({ canvasElement }) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
const closeButton = await within(canvasElement).getByTitle('Close about section');
await closeButton.click();
},
};
138 changes: 138 additions & 0 deletions code/ui/manager/src/components/mobile/about/MobileAbout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import type { FC } from 'react';
import React, { useRef } from 'react';
import { Transition, type TransitionStatus } from 'react-transition-group';
import { styled } from '@storybook/theming';
import { Icons, Link } from '@storybook/components';
import { UpgradeBlock } from '../../upgrade/UpgradeBlock';
import { MOBILE_TRANSITION_DURATION } from '../../../constants';
import { useMobileLayoutContext } from '../MobileLayoutProvider';

export const MobileAbout: FC = () => {
const { isMobileAboutOpen, setMobileAboutOpen } = useMobileLayoutContext();
const aboutRef = useRef(null);
console.log('LOG :', isMobileAboutOpen, setMobileAboutOpen);

return (
<Transition
nodeRef={aboutRef}
in={isMobileAboutOpen}
timeout={MOBILE_TRANSITION_DURATION}
appear
mountOnEnter
unmountOnExit
>
{(state) => (
<Container ref={aboutRef} state={state} transitionDuration={MOBILE_TRANSITION_DURATION}>
<Button onClick={() => setMobileAboutOpen(false)} title="Close about section">
<Icons icon="arrowleftalt" />
Back
</Button>
<LinkContainer>
<LinkLine href="https://github.com/storybookjs/storybook" target="_blank">
<LinkLeft>
<Icons icon="github" />
<span>Github</span>
</LinkLeft>
<Icons icon="sharealt" width={12} />
</LinkLine>
<LinkLine
href="https://storybook.js.org/docs/react/get-started/install/"
target="_blank"
>
<LinkLeft>
<Icons icon="storybook" />
<span>Documentation</span>
</LinkLeft>
<Icons icon="sharealt" width={12} />
</LinkLine>
</LinkContainer>
<UpgradeBlock />
<BottomText>
Open source software maintained by{' '}
<Link href="https://chromatic.com" target="_blank">
Chromatic
</Link>{' '}
and the{' '}
<Link href="https://github.com/storybookjs/storybook/graphs/contributors">
Storybook Community
</Link>
</BottomText>
</Container>
)}
</Transition>
);
};

const Container = styled.div<{ state: TransitionStatus; transitionDuration: number }>(
({ theme, state, transitionDuration }) => ({
position: 'absolute',
width: '100%',
height: '100%',
top: 0,
left: 0,
zIndex: 11,
JReinhold marked this conversation as resolved.
Show resolved Hide resolved
transition: `all ${transitionDuration}ms ease-in-out`,
overflow: 'scroll',
padding: '20px',
color: theme.color.defaultText,
background: theme.background.content,
opacity: `${(() => {
if (state === 'entering') return 1;
if (state === 'entered') return 1;
if (state === 'exiting') return 0;
if (state === 'exited') return 0;
return 0;
})()}`,
transform: `${(() => {
if (state === 'entering') return 'translateX(0)';
if (state === 'entered') return 'translateX(0)';
if (state === 'exiting') return 'translateX(20px)';
if (state === 'exited') return 'translateX(20px)';
return 'translateX(0)';
})()}`,
})
);

const LinkContainer = styled.div({
marginTop: 20,
marginBottom: 20,
});

const LinkLine = styled.a(({ theme }) => ({
all: 'unset',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
fontSize: theme.typography.size.s2 - 1,
height: 52,
borderBottom: `1px solid ${theme.appBorderColor}`,
cursor: 'pointer',
padding: '0 10px',

'&:last-child': {
borderBottom: 'none',
},
}));

const LinkLeft = styled.div(({ theme }) => ({
display: 'flex',
alignItems: 'center',
fontSize: theme.typography.size.s2 - 1,
height: 40,
gap: 5,
}));

const BottomText = styled.div(({ theme }) => ({
fontSize: theme.typography.size.s2 - 1,
marginTop: 30,
}));

const Button = styled.button(({ theme }) => ({
all: 'unset',
display: 'flex',
alignItems: 'center',
gap: 10,
color: 'currentColor',
fontSize: theme.typography.size.s2 - 1,
padding: '0 10px',
}));
Loading