Skip to content

Commit

Permalink
Merge branch 'new-layout' into charles-mobile-2
Browse files Browse the repository at this point in the history
  • Loading branch information
cdedreuille committed Sep 19, 2023
2 parents ca9c691 + 385a406 commit 294b1b5
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 61 deletions.
167 changes: 130 additions & 37 deletions code/e2e-tests/manager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,90 +4,183 @@ import { SbPage } from './util';

const storybookUrl = process.env.STORYBOOK_URL || 'http://localhost:8001';

test.describe('manager', () => {
test.describe('Manager UI', () => {
// TODO: test dragging and resizing

test.beforeEach(async ({ page }) => {
await page.goto(storybookUrl);

await new SbPage(page).waitUntilLoaded();
});

test('shortcuts sidebar', async ({ page }) => {
test('Sidebar toggling', async ({ page }) => {
const sbPage = new SbPage(page);

await expect(sbPage.page.locator('.sidebar-container')).toBeVisible();

// toggle with keyboard shortcut
await sbPage.page.locator('html').press('s');
await expect(sbPage.page.locator('.sidebar-container')).not.toBeVisible();

await sbPage.page.locator('[aria-label="Show sidebar"]').click();
await sbPage.page.locator('html').press('s');
await expect(sbPage.page.locator('.sidebar-container')).toBeVisible();

// toggle with menu item
await sbPage.page.locator('[aria-label="Shortcuts"]').click();
await sbPage.page.locator('#list-item-S').click();

await expect(sbPage.page.locator('.sidebar-container')).not.toBeVisible();

await sbPage.page.locator('html').press('s');
// toggle with "show sidebar" button
await sbPage.page.locator('[aria-label="Show sidebar"]').click();
await expect(sbPage.page.locator('.sidebar-container')).toBeVisible();
});

test('shortcuts toolbar', async ({ page }) => {
test('Toolbar toggling', async ({ page }) => {
const sbPage = new SbPage(page);
const isToolbarShown = async () => {
const canvas = await sbPage.page.locator('div', {
has: sbPage.page.locator('> #storybook-preview-wrapper'),
});

return (await canvas.getAttribute('offset')) === '40';
const expectToolbarVisibility = async (visible: boolean) => {
expect(async () => {
const toolbar = await sbPage.page.locator(`[data-test-id="sb-preview-toolbar"]`);
const marginTop = await toolbar.evaluate(
(element) => window.getComputedStyle(element).marginTop
);
expect(marginTop).toBe(visible ? '0px' : '-40px');
}).toPass({ intervals: [400] });
};

await expect(await isToolbarShown()).toBeTruthy();
await expectToolbarVisibility(true);

// toggle with keyboard shortcut
await sbPage.page.locator('html').press('t');
await expectToolbarVisibility(false);
await sbPage.page.locator('html').press('t');
await expect(await isToolbarShown()).toBeFalsy();
await expectToolbarVisibility(true);

// toggle with menu item
await sbPage.page.locator('[aria-label="Shortcuts"]').click();
await sbPage.page.locator('#list-item-T').click();
await expect(await isToolbarShown()).toBeTruthy();
await expectToolbarVisibility(false);
await sbPage.page.locator('[aria-label="Shortcuts"]').click();
await sbPage.page.locator('#list-item-T').click();
await expectToolbarVisibility(true);
});

test('shortcuts panel', async ({ page }) => {
const sbPage = new SbPage(page);
const isPanelsShown = async () => {
const main = await sbPage.page.locator('div', {
has: sbPage.page.locator('> * > #storybook-preview-wrapper'),
});
test.describe('Panel', () => {
test('Hidden in docs view', async ({ page }) => {
const sbPage = new SbPage(page);

const style = await main.getAttribute('style');
return style;
};
// navigate to docs to hide panel
await sbPage.navigateToStory('example/button', 'docs');

await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible();

// toggle with keyboard shortcut
await sbPage.page.locator('html').press('a');
await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible();
await sbPage.page.locator('html').press('a');
await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible();
});

test('Toggling', async ({ page }) => {
const sbPage = new SbPage(page);

// navigate to story to show panel
await sbPage.navigateToStory('example/button', 'primary');

await expect(sbPage.page.locator('#storybook-panel-root')).toBeVisible();

// toggle with keyboard shortcut
await sbPage.page.locator('html').press('a');
await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible();
await sbPage.page.locator('html').press('a');
await expect(sbPage.page.locator('#storybook-panel-root')).toBeVisible();

// toggle with menu item
await sbPage.page.locator('[aria-label="Shortcuts"]').click();
await sbPage.page.locator('#list-item-A').click();
await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible();

// toggle with "show addons" button
await sbPage.page.locator('[aria-label="Show addons"]').click();
await expect(sbPage.page.locator('#storybook-panel-root')).toBeVisible();
});

test('Positioning', async ({ page }) => {
const sbPage = new SbPage(page);

// navigate to story to show panel
await sbPage.navigateToStory('example/button', 'primary');

await expect(sbPage.page.locator('#storybook-panel-root')).toBeVisible();

// toggle position with keyboard shortcut
await sbPage.page.locator('html').press('d');
await expect(sbPage.page.locator('#storybook-panel-root')).toBeVisible();
// TODO: how to assert panel position?

// hide with keyboard shortcut
await sbPage.page.locator('html').press('a');
await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible();

// toggling position should also show the panel again
await sbPage.page.locator('html').press('d');
await expect(sbPage.page.locator('#storybook-panel-root')).toBeVisible();
});
});

test('Fullscreen toggling', async ({ page }) => {
const sbPage = new SbPage(page);

// navigate to story to show panel
await sbPage.navigateToStory('example/button', 'primary');
await expect(await isPanelsShown()).toBeTruthy();

await sbPage.page.locator('html').press('a');
await expect(await isPanelsShown()).toBeFalsy();
await expect(sbPage.page.locator('#storybook-panel-root')).toBeVisible();
await expect(sbPage.page.locator('.sidebar-container')).toBeVisible();

await sbPage.page.locator('[aria-label="Shortcuts"]').click();
await sbPage.page.locator('#list-item-A').click();
await expect(await isPanelsShown()).toBeTruthy();
// toggle with keyboard shortcut
await sbPage.page.locator('html').press('f');
await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible();
await expect(sbPage.page.locator('.sidebar-container')).not.toBeVisible();

await sbPage.page.locator('html').press('a');
await expect(await isPanelsShown()).toBeFalsy();
await sbPage.page.locator('html').press('f');
await expect(sbPage.page.locator('#storybook-panel-root')).toBeVisible();
await expect(sbPage.page.locator('.sidebar-container')).toBeVisible();

await sbPage.page.locator('html').press('a');
// toggle with menu item
await sbPage.page.locator('[aria-label="Shortcuts"]').click();
await sbPage.page.locator('#list-item-D').click();
await expect(await isPanelsShown()).toBeTruthy();
await sbPage.page.locator('#list-item-F').click();
await expect(sbPage.page.locator('.sidebar-container')).not.toBeVisible();
await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible();

// toggle with "go/exit fullscreen" button
await sbPage.page.locator('[aria-label="Exit full screen"]').click();
await expect(sbPage.page.locator('#storybook-panel-root')).toBeVisible();
await expect(sbPage.page.locator('.sidebar-container')).toBeVisible();

await sbPage.page.locator('[aria-label="Go full screen"]').click();
await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible();
await expect(sbPage.page.locator('.sidebar-container')).not.toBeVisible();

// go fullscreen when sidebar is shown but panel is hidden
await sbPage.page.locator('[aria-label="Show sidebar"]').click();
await sbPage.page.locator('[aria-label="Go full screen"]').click();
await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible();
await expect(sbPage.page.locator('.sidebar-container')).not.toBeVisible();

// go fullscreen when panel is shown but sidebar is hidden
await sbPage.page.locator('[aria-label="Show addons"]').click();
await sbPage.page.locator('[aria-label="Go full screen"]').click();
await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible();
await expect(sbPage.page.locator('.sidebar-container')).not.toBeVisible();
});

test('settings page', async ({ page }) => {
test('Settings page', async ({ page }) => {
const sbPage = new SbPage(page);
await sbPage.page.locator('[aria-label="Shortcuts"]').click();
await sbPage.page.locator('#list-item-about').click();

await expect(sbPage.page.url()).toContain('/settings/about');

await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible();

await sbPage.page.locator('[title="Close settings page"]').click();
await expect(sbPage.page.url()).not.toContain('/settings/about');
});
Expand Down
4 changes: 4 additions & 0 deletions code/ui/manager/src/components/hooks/useMedia.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { useEffect, useState } from 'react';

// The hook is taken from this library:
// https://usehooks-ts.com/react-hook/use-media-query
// The good thing about it is that is uses window.matchMedia

export function useMediaQuery(query: string): boolean {
const getMatches = (queryMatch: string): boolean => {
// Prevents SSR issues
Expand Down
1 change: 0 additions & 1 deletion code/ui/manager/src/components/layout/Layout.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ const meta = {
theme: 'light',
layout: 'fullscreen',
},
decorators: [(Story) => <Story />],
render: (args) => {
const [managerLayoutState, setManagerLayoutState] = useState(args.managerLayoutState);

Expand Down
30 changes: 10 additions & 20 deletions code/ui/manager/src/components/layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ 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 { breakpointMin600 } from '../../constants';
import { BREAKPOINT_MIN_600 } from '../../constants';

interface InternalLayoutState {
isDragging: boolean;
Expand All @@ -25,7 +25,7 @@ interface Props {
slotPanel?: React.ReactNode;
slotPages?: React.ReactNode;
}
const MINIMUM_CONTENT_WIDTH_PX = 30;
const MINIMUM_CONTENT_WIDTH_PX = 100;

const layoutStateIsEqual = (state: ManagerLayoutState, other: ManagerLayoutState) =>
state.navSize === other.navSize &&
Expand Down Expand Up @@ -113,7 +113,7 @@ const useLayoutSyncingState = (
};

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

const {
Expand All @@ -136,10 +136,9 @@ export const Layout = ({ managerLayoutState, setManagerLayoutState, ...slots }:
panelPosition={managerLayoutState.panelPosition}
isDragging={isDragging}
viewMode={managerLayoutState.viewMode}
breakpoint={breakpointMin600}
>
{showPages && <PagesContainer>{slots.slotPages}</PagesContainer>}
<ContentContainer breakpoint={breakpointMin600}>{slots.slotMain}</ContentContainer>
<ContentContainer>{slots.slotMain}</ContentContainer>
{isDesktop && (
<>
<SidebarContainer>
Expand All @@ -163,27 +162,18 @@ export const Layout = ({ managerLayoutState, setManagerLayoutState, ...slots }:
);
};

const LayoutContainer = styled.div<LayoutState & { breakpoint: string }>(
({
navSize,
rightPanelWidth,
bottomPanelHeight,
viewMode,
panelPosition,
isDragging,
breakpoint,
}) => {
const LayoutContainer = styled.div<LayoutState>(
({ navSize, rightPanelWidth, bottomPanelHeight, viewMode, panelPosition }) => {
return {
width: '100%',
height: '100svh',
height: '100svh', // We are using svh to use the minimum space on mobile
overflow: 'hidden',
display: 'flex',
flexDirection: 'column',

[`@media ${breakpoint}`]: {
[`@media ${BREAKPOINT_MIN_600}`]: {
display: 'grid',
gap: 0,
transition: isDragging ? null : 'all 0.2s ease-in-out', // transition when toggling panels, but not when dragging
gridTemplateColumns: `minmax(0, ${navSize}px) minmax(${MINIMUM_CONTENT_WIDTH_PX}px, 1fr) minmax(0, ${rightPanelWidth}px)`,
gridTemplateRows: `1fr ${bottomPanelHeight}px`,
gridTemplateAreas: (() => {
Expand Down Expand Up @@ -211,13 +201,13 @@ const SidebarContainer = styled.div(({ theme }) => ({
borderRight: `1px solid ${theme.color.border}`,
}));

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

[`@media ${breakpoint}`]: {
[`@media ${BREAKPOINT_MIN_600}`]: {
flex: 'auto',
gridArea: 'content',
},
Expand Down
4 changes: 2 additions & 2 deletions code/ui/manager/src/components/preview/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const fullScreenTool: Addon_BaseType = {
key="full"
onClick={toggle as any}
title={`${isFullscreen ? 'Exit full screen' : 'Go full screen'} [${shortcut}]`}
aria-label={isFullscreen ? 'Exit full screen' : 'Go full screen'}
>
<Icons icon={isFullscreen ? 'close' : 'expand'} />
</IconButton>
Expand Down Expand Up @@ -157,7 +158,7 @@ export const ToolRes: FunctionComponent<ToolData & RenderData> = React.memo<Tool
const { left, right } = useTools(api.getElements, tabs, viewMode, entry, location, path);

return left || right ? (
<Toolbar key="toolbar" shown={isShown}>
<Toolbar key="toolbar" shown={isShown} data-test-id="sb-preview-toolbar">
<ToolbarInner>
<ToolbarLeft>
<Tools key="left" list={left} />
Expand Down Expand Up @@ -249,7 +250,6 @@ const Toolbar = styled.div<{ shown: boolean }>(({ theme, shown }) => ({
flexShrink: 0,
overflowX: 'auto',
overflowY: 'hidden',
transition: 'all .2s linear',
marginTop: shown ? 0 : -40,
boxShadow: `${theme.appBorderColor} 0 -1px 0 0 inset`,
background: theme.barBg,
Expand Down
2 changes: 1 addition & 1 deletion code/ui/manager/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const breakpointMin600 = '(min-width: 600px)';
export const BREAKPOINT_MIN_600 = '(min-width: 600px)';

0 comments on commit 294b1b5

Please sign in to comment.