fix: sidebar regression (#2195)

This commit is contained in:
Himself65
2023-04-28 15:02:47 -05:00
committed by GitHub
parent 73a7c01580
commit 31cccafb40
8 changed files with 50 additions and 30 deletions
@@ -17,7 +17,7 @@ import {
import type { Page } from '@blocksuite/store'; import type { Page } from '@blocksuite/store';
import { useAtomValue } from 'jotai'; import { useAtomValue } from 'jotai';
import type { ReactElement, UIEvent } from 'react'; import type { ReactElement, UIEvent } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import type { AllWorkspace } from '../../shared'; import type { AllWorkspace } from '../../shared';
import ChangeLog from '../pure/workspace-slider-bar/changeLog'; import ChangeLog from '../pure/workspace-slider-bar/changeLog';
@@ -75,15 +75,15 @@ export const RootAppSidebar = ({
}, [createPage, openPage]); }, [createPage, openPage]);
const sidebarOpen = useAtomValue(appSidebarOpenAtom); const sidebarOpen = useAtomValue(appSidebarOpenAtom);
useEffect(() => { useEffect(() => {
if (environment.isDesktop) { if (environment.isDesktop && typeof sidebarOpen === 'boolean') {
window.apis?.onSidebarVisibilityChange(sidebarOpen); window.apis?.onSidebarVisibilityChange(sidebarOpen);
} }
}, [sidebarOpen]); }, [sidebarOpen]);
const ref = useRef<HTMLElement>(null); const [ref, setRef] = useState<HTMLElement | null>(null);
return ( return (
<> <>
<AppSidebar <AppSidebar
ref={ref} ref={setRef}
footer={ footer={
<StyledNewPageButton <StyledNewPageButton
data-testid="new-page-button" data-testid="new-page-button"
@@ -13,7 +13,7 @@ export const navStyle = style({
height: '100%', height: '100%',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
transition: 'margin-left .3s', transition: 'margin-left .3s, width .3s',
zIndex: parseInt(baseTheme.zIndexModal), zIndex: parseInt(baseTheme.zIndexModal),
borderRight: '1px solid var(--affine-border-color)', borderRight: '1px solid var(--affine-border-color)',
'@media': { '@media': {
@@ -61,6 +61,7 @@ export const navHeaderStyle = style({
}, },
selectors: { selectors: {
'&[data-is-macos-electron="true"]': { '&[data-is-macos-electron="true"]': {
WebkitAppRegion: 'drag',
justifyContent: 'flex-end', justifyContent: 'flex-end',
}, },
}, },
@@ -1,6 +1,9 @@
import { atomWithStorage } from 'jotai/utils'; import { atomWithStorage } from 'jotai/utils';
export const appSidebarOpenAtom = atomWithStorage('app-sidebar-open', true); export const appSidebarOpenAtom = atomWithStorage(
'app-sidebar-open',
undefined as boolean | undefined
);
export const appSidebarWidthAtom = atomWithStorage( export const appSidebarWidthAtom = atomWithStorage(
'app-sidebar-width', 'app-sidebar-width',
256 /* px */ 256 /* px */
@@ -2,7 +2,7 @@ import { IconButton } from '@affine/component';
import { SidebarIcon } from '@blocksuite/icons'; import { SidebarIcon } from '@blocksuite/icons';
import type { Meta, StoryFn } from '@storybook/react'; import type { Meta, StoryFn } from '@storybook/react';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import { useRef } from 'react'; import { useState } from 'react';
import { AppSidebar, appSidebarOpenAtom, ResizeIndicator } from '.'; import { AppSidebar, appSidebarOpenAtom, ResizeIndicator } from '.';
import { navHeaderStyle, sidebarButtonStyle } from './index.css'; import { navHeaderStyle, sidebarButtonStyle } from './index.css';
@@ -16,7 +16,7 @@ const Footer = () => <div>Add Page</div>;
export const Default: StoryFn = () => { export const Default: StoryFn = () => {
const [open, setOpen] = useAtom(appSidebarOpenAtom); const [open, setOpen] = useAtom(appSidebarOpenAtom);
const ref = useRef<HTMLElement>(null); const [ref, setRef] = useState<HTMLElement | null>(null);
return ( return (
<> <>
<main <main
@@ -29,7 +29,7 @@ export const Default: StoryFn = () => {
flexDirection: 'row', flexDirection: 'row',
}} }}
> >
<AppSidebar footer={<Footer />} ref={ref}> <AppSidebar footer={<Footer />} ref={setRef}>
Test Test
</AppSidebar> </AppSidebar>
<ResizeIndicator targetElement={ref} /> <ResizeIndicator targetElement={ref} />
@@ -8,10 +8,11 @@ import { assignInlineVars } from '@vanilla-extract/dynamic';
import { useAtom, useAtomValue } from 'jotai'; import { useAtom, useAtomValue } from 'jotai';
import type { PropsWithChildren, ReactElement } from 'react'; import type { PropsWithChildren, ReactElement } from 'react';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { forwardRef, useCallback, useImperativeHandle, useRef } from 'react'; import { forwardRef, useCallback, useEffect } from 'react';
import { IconButton } from '../../ui/button/IconButton'; import { IconButton } from '../../ui/button/IconButton';
import { import {
floatingMaxWidth,
navBodyStyle, navBodyStyle,
navFooterStyle, navFooterStyle,
navHeaderStyle, navHeaderStyle,
@@ -30,24 +31,38 @@ export type AppSidebarProps = PropsWithChildren<{
export const AppSidebar = forwardRef<HTMLElement, AppSidebarProps>( export const AppSidebar = forwardRef<HTMLElement, AppSidebarProps>(
function AppSidebar(props, forwardedRef): ReactElement { function AppSidebar(props, forwardedRef): ReactElement {
const ref = useRef<HTMLElement>(null);
const [open, setOpen] = useAtom(appSidebarOpenAtom); const [open, setOpen] = useAtom(appSidebarOpenAtom);
const appSidebarWidth = useAtomValue(appSidebarWidthAtom); const appSidebarWidth = useAtomValue(appSidebarWidthAtom);
const initialRender = open === undefined;
const handleSidebarOpen = useCallback(() => { const handleSidebarOpen = useCallback(() => {
setOpen(open => !open); setOpen(open => !open);
}, [setOpen]); }, [setOpen]);
useImperativeHandle(forwardedRef, () => ref.current as HTMLElement); useEffect(() => {
if (open === undefined) {
// give the initial value,
// so that the sidebar can be closed on mobile by default
const { matches } = window.matchMedia(
`(min-width: ${floatingMaxWidth}px)`
);
setOpen(matches);
}
}, [open, setOpen]);
const environment = getEnvironment(); const environment = getEnvironment();
const isMacosDesktop = environment.isDesktop && environment.isMacOs; const isMacosDesktop = environment.isDesktop && environment.isMacOs;
if (initialRender) {
// avoid the UI flash
return <div />;
}
return ( return (
<> <>
<nav <nav
className={navStyle} className={navStyle}
ref={ref} ref={forwardedRef}
style={assignInlineVars({ style={assignInlineVars({
[navWidthVar]: `${appSidebarWidth}px`, [navWidthVar]: `${appSidebarWidth}px`,
})} })}
@@ -96,9 +111,7 @@ export const AppSidebar = forwardRef<HTMLElement, AppSidebarProps>(
data-testid="app-sidebar-float-mask" data-testid="app-sidebar-float-mask"
data-open={open} data-open={open}
className={sidebarFloatMaskStyle} className={sidebarFloatMaskStyle}
onClick={useCallback(() => { onClick={() => setOpen(false)}
setOpen(false);
}, [setOpen])}
/> />
</> </>
); );
@@ -4,13 +4,14 @@ import { navWidthVar } from '../index.css';
export const spacerStyle = style({ export const spacerStyle = style({
position: 'absolute', position: 'absolute',
width: '1px',
left: navWidthVar, left: navWidthVar,
top: 0, top: 0,
bottom: 0, bottom: 0,
width: '7px',
height: '100%', height: '100%',
borderLeft: '1px solid var(--affine-border-color)',
zIndex: 'calc(var(--affine-z-index-modal) - 1)', zIndex: 'calc(var(--affine-z-index-modal) - 1)',
backgroundColor: 'var(--affine-border-color)', backgroundColor: 'transparent',
opacity: 0, opacity: 0,
cursor: 'col-resize', cursor: 'col-resize',
'@media': { '@media': {
@@ -1,7 +1,7 @@
import type { Instance } from '@popperjs/core'; import type { Instance } from '@popperjs/core';
import { createPopper } from '@popperjs/core'; import { createPopper } from '@popperjs/core';
import { useAtom, useAtomValue, useSetAtom } from 'jotai'; import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import type { ReactElement, RefObject } from 'react'; import type { ReactElement } from 'react';
import { import {
useCallback, useCallback,
useDeferredValue, useDeferredValue,
@@ -14,7 +14,7 @@ import { appSidebarOpenAtom, appSidebarWidthAtom } from '../index.jotai';
import { spacerStyle } from './index.css'; import { spacerStyle } from './index.css';
export type ResizeIndicatorProps = { export type ResizeIndicatorProps = {
targetElement: RefObject<HTMLElement>; targetElement: HTMLElement | null;
}; };
export const ResizeIndicator = (props: ResizeIndicatorProps): ReactElement => { export const ResizeIndicator = (props: ResizeIndicatorProps): ReactElement => {
@@ -25,14 +25,15 @@ export const ResizeIndicator = (props: ResizeIndicatorProps): ReactElement => {
const [isResizing, setIsResizing] = useState(false); const [isResizing, setIsResizing] = useState(false);
useEffect(() => { useEffect(() => {
if (ref.current) { if (ref.current) {
if (props.targetElement.current) { if (props.targetElement) {
popperRef.current = createPopper( const popper = createPopper(props.targetElement, ref.current, {
props.targetElement.current, placement: 'right',
ref.current, });
{ popperRef.current = popper;
placement: 'right', return () => {
} popper.destroy();
); popperRef.current = null;
};
} }
} }
}, [props.targetElement]); }, [props.targetElement]);
+3 -2
View File
@@ -179,8 +179,9 @@ test('When opening the website for the first time, the first folding sidebar wil
const quickSearchTips = page.locator('[data-testid=quick-search-tips]'); const quickSearchTips = page.locator('[data-testid=quick-search-tips]');
await expect(quickSearchTips).not.toBeVisible(); await expect(quickSearchTips).not.toBeVisible();
await page.getByTestId('app-sidebar-arrow-button-collapse').click(); await page.getByTestId('app-sidebar-arrow-button-collapse').click();
await page.waitForTimeout(200); // fixme: when first close, the tooltip will not show
await page.getByTestId('sliderBar-arrowButton-expand').click(); await page.getByTestId('sliderBar-arrowButton-expand').click();
await page.getByTestId('app-sidebar-arrow-button-collapse').click();
const sliderBarArea = page.getByTestId('sliderBar-inner'); const sliderBarArea = page.getByTestId('sliderBar-inner');
await expect(sliderBarArea).not.toBeInViewport(); await expect(sliderBarArea).not.toBeInViewport();
await expect(quickSearchTips).toBeVisible(); await expect(quickSearchTips).toBeVisible();
@@ -195,8 +196,8 @@ test('After appearing once, it will not appear a second time', async ({
const quickSearchTips = page.locator('[data-testid=quick-search-tips]'); const quickSearchTips = page.locator('[data-testid=quick-search-tips]');
await expect(quickSearchTips).not.toBeVisible(); await expect(quickSearchTips).not.toBeVisible();
await page.getByTestId('app-sidebar-arrow-button-collapse').click(); await page.getByTestId('app-sidebar-arrow-button-collapse').click();
await page.waitForTimeout(200);
await page.getByTestId('sliderBar-arrowButton-expand').click(); await page.getByTestId('sliderBar-arrowButton-expand').click();
await page.getByTestId('app-sidebar-arrow-button-collapse').click();
const sliderBarArea = page.getByTestId('sliderBar'); const sliderBarArea = page.getByTestId('sliderBar');
await expect(sliderBarArea).not.toBeVisible(); await expect(sliderBarArea).not.toBeVisible();
await expect(quickSearchTips).toBeVisible(); await expect(quickSearchTips).toBeVisible();