mirror of
https://github.com/toeverything/AFFiNE.git
synced 2025-01-04 20:37:54 +03:00
fix(core): update outline viewer style (#7641)
## What changes - Update responsive style and fix some bug of outline viewer (https://github.com/toeverything/blocksuite/pull/7759) - Change left and right padding of full-width editor from `15px` to `72px` - Hide outline viewer when side outline panel is opened ([BS-987](https://linear.app/affine-design/issue/BS-987/逻辑-bug-toc-入口和-toc-侧边栏共存)) - Add entries of outline panel and frame panel in more menu of detail page header ( [BS-996](https://linear.app/affine-design/issue/BS-996/page-mode-下的-page-option-缺少-view-table-of-contents-的入口) , [BS-1006](https://linear.app/affine-design/issue/BS-1006/edgeless-mode-的-page-options-里缺少-view-all-frames)) - Add outline viewer to dock peek preview ( [BS-995](https://linear.app/affine-design/issue/BS-995/center-peek-里缺少-quick-toc-的入口) ) - Add more e2e tests for outline viewer
This commit is contained in:
parent
545bd032a7
commit
bd31c8388c
2
packages/common/env/package.json
vendored
2
packages/common/env/package.json
vendored
@ -27,4 +27,4 @@
|
|||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
"version": "0.15.0"
|
"version": "0.15.0"
|
||||||
}
|
}
|
@ -74,4 +74,4 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"version": "0.15.0"
|
"version": "0.15.0"
|
||||||
}
|
}
|
@ -111,4 +111,4 @@
|
|||||||
"vitest": "1.6.0"
|
"vitest": "1.6.0"
|
||||||
},
|
},
|
||||||
"version": "0.15.0"
|
"version": "0.15.0"
|
||||||
}
|
}
|
@ -116,4 +116,4 @@
|
|||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"vitest": "1.6.0"
|
"vitest": "1.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -20,6 +20,7 @@ import { useTrashModalHelper } from '@affine/core/hooks/affine/use-trash-modal-h
|
|||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { mixpanel } from '@affine/core/mixpanel';
|
||||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
|
import { ViewService } from '@affine/core/modules/workbench/services/view';
|
||||||
import { useDetailPageHeaderResponsive } from '@affine/core/pages/workspace/detail-page/use-header-responsive';
|
import { useDetailPageHeaderResponsive } from '@affine/core/pages/workspace/detail-page/use-header-responsive';
|
||||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
@ -29,6 +30,7 @@ import {
|
|||||||
EditIcon,
|
EditIcon,
|
||||||
FavoritedIcon,
|
FavoritedIcon,
|
||||||
FavoriteIcon,
|
FavoriteIcon,
|
||||||
|
FrameIcon,
|
||||||
HistoryIcon,
|
HistoryIcon,
|
||||||
ImportIcon,
|
ImportIcon,
|
||||||
InformationIcon,
|
InformationIcon,
|
||||||
@ -36,6 +38,7 @@ import {
|
|||||||
PageIcon,
|
PageIcon,
|
||||||
ShareIcon,
|
ShareIcon,
|
||||||
SplitViewIcon,
|
SplitViewIcon,
|
||||||
|
TocIcon,
|
||||||
} from '@blocksuite/icons/rc';
|
} from '@blocksuite/icons/rc';
|
||||||
import type { Doc } from '@blocksuite/store';
|
import type { Doc } from '@blocksuite/store';
|
||||||
import {
|
import {
|
||||||
@ -84,6 +87,24 @@ export const PageHeaderMenuButton = ({
|
|||||||
const { importFile } = usePageHelper(docCollection);
|
const { importFile } = usePageHelper(docCollection);
|
||||||
const { setTrashModal } = useTrashModalHelper(docCollection);
|
const { setTrashModal } = useTrashModalHelper(docCollection);
|
||||||
|
|
||||||
|
const view = useService(ViewService).view;
|
||||||
|
|
||||||
|
const openSidePanel = useCallback(
|
||||||
|
(id: string) => {
|
||||||
|
workbench.openSidebar();
|
||||||
|
view.activeSidebarTab(id);
|
||||||
|
},
|
||||||
|
[workbench, view]
|
||||||
|
);
|
||||||
|
|
||||||
|
const openAllFrames = useCallback(() => {
|
||||||
|
openSidePanel('frame');
|
||||||
|
}, [openSidePanel]);
|
||||||
|
|
||||||
|
const openOutlinePanel = useCallback(() => {
|
||||||
|
openSidePanel('outline');
|
||||||
|
}, [openSidePanel]);
|
||||||
|
|
||||||
const [historyModalOpen, setHistoryModalOpen] = useState(false);
|
const [historyModalOpen, setHistoryModalOpen] = useState(false);
|
||||||
const setOpenHistoryTipsModal = useSetAtom(openHistoryTipsModalAtom);
|
const setOpenHistoryTipsModal = useSetAtom(openHistoryTipsModalAtom);
|
||||||
|
|
||||||
@ -297,6 +318,33 @@ export const PageHeaderMenuButton = ({
|
|||||||
{t['com.affine.page-properties.page-info.view']()}
|
{t['com.affine.page-properties.page-info.view']()}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
|
{currentMode === 'page' ? (
|
||||||
|
<MenuItem
|
||||||
|
preFix={
|
||||||
|
<MenuIcon>
|
||||||
|
<TocIcon />
|
||||||
|
</MenuIcon>
|
||||||
|
}
|
||||||
|
data-testid="editor-option-toc"
|
||||||
|
onSelect={openOutlinePanel}
|
||||||
|
style={menuItemStyle}
|
||||||
|
>
|
||||||
|
{t['com.affine.header.option.view-toc']()}
|
||||||
|
</MenuItem>
|
||||||
|
) : (
|
||||||
|
<MenuItem
|
||||||
|
preFix={
|
||||||
|
<MenuIcon>
|
||||||
|
<FrameIcon />
|
||||||
|
</MenuIcon>
|
||||||
|
}
|
||||||
|
data-testid="editor-option-frame"
|
||||||
|
onSelect={openAllFrames}
|
||||||
|
style={menuItemStyle}
|
||||||
|
>
|
||||||
|
{t['com.affine.header.option.view-frame']()}
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
<MenuItem
|
<MenuItem
|
||||||
preFix={
|
preFix={
|
||||||
<MenuIcon>
|
<MenuIcon>
|
||||||
|
@ -6,10 +6,12 @@ import * as styles from './outline-viewer.css';
|
|||||||
|
|
||||||
export const EditorOutlineViewer = ({
|
export const EditorOutlineViewer = ({
|
||||||
editor,
|
editor,
|
||||||
toggleOutlinePanel,
|
show,
|
||||||
|
openOutlinePanel,
|
||||||
}: {
|
}: {
|
||||||
editor: AffineEditorContainer | null;
|
editor: AffineEditorContainer | null;
|
||||||
toggleOutlinePanel: () => void;
|
show: boolean;
|
||||||
|
openOutlinePanel: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const outlineViewerRef = useRef<OutlineViewer | null>(null);
|
const outlineViewerRef = useRef<OutlineViewer | null>(null);
|
||||||
|
|
||||||
@ -24,15 +26,16 @@ export const EditorOutlineViewer = ({
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!editor) {
|
if (!editor || !show) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!outlineViewerRef.current) {
|
if (!outlineViewerRef.current) {
|
||||||
outlineViewerRef.current = new OutlineViewer();
|
outlineViewerRef.current = new OutlineViewer();
|
||||||
(outlineViewerRef.current as OutlineViewer).editor = editor;
|
}
|
||||||
(outlineViewerRef.current as OutlineViewer).toggleOutlinePanel =
|
if (outlineViewerRef.current.editor !== editor) {
|
||||||
toggleOutlinePanel;
|
outlineViewerRef.current.editor = editor;
|
||||||
|
}
|
||||||
|
if (outlineViewerRef.current.toggleOutlinePanel !== openOutlinePanel) {
|
||||||
|
outlineViewerRef.current.toggleOutlinePanel = openOutlinePanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className={styles.root} ref={onRefChange} />;
|
return <div className={styles.root} ref={onRefChange} />;
|
@ -0,0 +1,17 @@
|
|||||||
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
const top = 256 - 52; // 52 is the height of the header
|
||||||
|
const bottom = 76;
|
||||||
|
|
||||||
|
export const root = style({
|
||||||
|
position: 'absolute',
|
||||||
|
top,
|
||||||
|
right: 22,
|
||||||
|
maxHeight: `calc(100% - ${top}px - ${bottom}px)`,
|
||||||
|
display: 'flex',
|
||||||
|
'@container': {
|
||||||
|
'(width <= 640px)': {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
@ -5,7 +5,7 @@ export const editor = style({
|
|||||||
'&.full-screen': {
|
'&.full-screen': {
|
||||||
vars: {
|
vars: {
|
||||||
'--affine-editor-width': '100%',
|
'--affine-editor-width': '100%',
|
||||||
'--affine-editor-side-padding': '15px',
|
'--affine-editor-side-padding': '72px',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import { cssVar } from '@toeverything/theme';
|
import { cssVar } from '@toeverything/theme';
|
||||||
import { style } from '@vanilla-extract/css';
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
export const root = style({
|
||||||
|
containerType: 'inline-size',
|
||||||
|
});
|
||||||
|
|
||||||
export const editor = style({
|
export const editor = style({
|
||||||
vars: {
|
vars: {
|
||||||
'--affine-editor-width': '100%',
|
'--affine-editor-width': '100%',
|
||||||
|
@ -3,6 +3,8 @@ import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton';
|
|||||||
import { AIProvider } from '@affine/core/blocksuite/presets/ai';
|
import { AIProvider } from '@affine/core/blocksuite/presets/ai';
|
||||||
import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary';
|
import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary';
|
||||||
import { BlockSuiteEditor } from '@affine/core/components/blocksuite/block-suite-editor';
|
import { BlockSuiteEditor } from '@affine/core/components/blocksuite/block-suite-editor';
|
||||||
|
import { EditorOutlineViewer } from '@affine/core/components/blocksuite/outline-viewer';
|
||||||
|
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
||||||
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
|
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
|
||||||
import { PageNotFound } from '@affine/core/pages/404';
|
import { PageNotFound } from '@affine/core/pages/404';
|
||||||
import { DebugLogger } from '@affine/debug';
|
import { DebugLogger } from '@affine/debug';
|
||||||
@ -12,7 +14,7 @@ import type { AffineEditorContainer } from '@blocksuite/presets';
|
|||||||
import type { DocMode } from '@toeverything/infra';
|
import type { DocMode } from '@toeverything/infra';
|
||||||
import { DocsService, FrameworkScope, useService } from '@toeverything/infra';
|
import { DocsService, FrameworkScope, useService } from '@toeverything/infra';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { WorkbenchService } from '../../../workbench';
|
import { WorkbenchService } from '../../../workbench';
|
||||||
import { PeekViewService } from '../../services/peek-view';
|
import { PeekViewService } from '../../services/peek-view';
|
||||||
@ -73,6 +75,7 @@ export function DocPeekPreview({
|
|||||||
const workbench = useService(WorkbenchService).workbench;
|
const workbench = useService(WorkbenchService).workbench;
|
||||||
const peekView = useService(PeekViewService).peekView;
|
const peekView = useService(PeekViewService).peekView;
|
||||||
const [editor, setEditor] = useState<AffineEditorContainer | null>(null);
|
const [editor, setEditor] = useState<AffineEditorContainer | null>(null);
|
||||||
|
const { appSettings } = useAppSettingHelper();
|
||||||
|
|
||||||
const onRef = (editor: AffineEditorContainer) => {
|
const onRef = (editor: AffineEditorContainer) => {
|
||||||
setEditor(editor);
|
setEditor(editor);
|
||||||
@ -143,6 +146,13 @@ export function DocPeekPreview({
|
|||||||
};
|
};
|
||||||
}, [editor, jumpToTag, peekView, workspace.id]);
|
}, [editor, jumpToTag, peekView, workspace.id]);
|
||||||
|
|
||||||
|
const openOutlinePanel = useCallback(() => {
|
||||||
|
workbench.openDoc(docId);
|
||||||
|
workbench.openSidebar();
|
||||||
|
workbench.activeView$.value.activeSidebarTab('outline');
|
||||||
|
peekView.close();
|
||||||
|
}, [docId, peekView, workbench]);
|
||||||
|
|
||||||
// if sync engine has been synced and the page is null, show 404 page.
|
// if sync engine has been synced and the page is null, show 404 page.
|
||||||
if (!doc || !resolvedMode) {
|
if (!doc || !resolvedMode) {
|
||||||
return loading || !resolvedMode ? (
|
return loading || !resolvedMode ? (
|
||||||
@ -167,7 +177,15 @@ export function DocPeekPreview({
|
|||||||
page={doc.blockSuiteDoc}
|
page={doc.blockSuiteDoc}
|
||||||
/>
|
/>
|
||||||
</FrameworkScope>
|
</FrameworkScope>
|
||||||
|
{appSettings.enableOutlineViewer && (
|
||||||
|
<EditorOutlineViewer
|
||||||
|
editor={editor}
|
||||||
|
show={resolvedMode === 'page'}
|
||||||
|
openOutlinePanel={openOutlinePanel}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Scrollable.Viewport>
|
</Scrollable.Viewport>
|
||||||
|
|
||||||
<Scrollable.Scrollbar />
|
<Scrollable.Scrollbar />
|
||||||
</Scrollable.Root>
|
</Scrollable.Root>
|
||||||
</AffineErrorBoundary>
|
</AffineErrorBoundary>
|
||||||
|
@ -2,6 +2,7 @@ import { cssVar } from '@toeverything/theme';
|
|||||||
import { style } from '@vanilla-extract/css';
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
export const mainContainer = style({
|
export const mainContainer = style({
|
||||||
|
containerType: 'inline-size',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
@ -3,6 +3,7 @@ import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton';
|
|||||||
import type { ChatPanel } from '@affine/core/blocksuite/presets/ai';
|
import type { ChatPanel } from '@affine/core/blocksuite/presets/ai';
|
||||||
import { AIProvider } from '@affine/core/blocksuite/presets/ai';
|
import { AIProvider } from '@affine/core/blocksuite/presets/ai';
|
||||||
import { PageAIOnboarding } from '@affine/core/components/affine/ai-onboarding';
|
import { PageAIOnboarding } from '@affine/core/components/affine/ai-onboarding';
|
||||||
|
import { EditorOutlineViewer } from '@affine/core/components/blocksuite/outline-viewer';
|
||||||
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
||||||
import { useDocMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta';
|
import { useDocMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta';
|
||||||
import { RecentDocsService } from '@affine/core/modules/quicksearch';
|
import { RecentDocsService } from '@affine/core/modules/quicksearch';
|
||||||
@ -64,7 +65,6 @@ import { performanceRenderLogger } from '../../../shared';
|
|||||||
import { PageNotFound } from '../../404';
|
import { PageNotFound } from '../../404';
|
||||||
import * as styles from './detail-page.css';
|
import * as styles from './detail-page.css';
|
||||||
import { DetailPageHeader } from './detail-page-header';
|
import { DetailPageHeader } from './detail-page-header';
|
||||||
import { EditorOutlineViewer } from './outline-viewer';
|
|
||||||
import { EditorChatPanel } from './tabs/chat';
|
import { EditorChatPanel } from './tabs/chat';
|
||||||
import { EditorFramePanel } from './tabs/frame';
|
import { EditorFramePanel } from './tabs/frame';
|
||||||
import { EditorJournalPanel } from './tabs/journal';
|
import { EditorJournalPanel } from './tabs/journal';
|
||||||
@ -83,6 +83,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
const globalContext = useService(GlobalContextService).globalContext;
|
const globalContext = useService(GlobalContextService).globalContext;
|
||||||
const docCollection = workspace.docCollection;
|
const docCollection = workspace.docCollection;
|
||||||
const mode = useLiveData(doc.mode$);
|
const mode = useLiveData(doc.mode$);
|
||||||
|
const isSideBarOpen = useLiveData(workbench.sidebarOpen$);
|
||||||
const { appSettings } = useAppSettingHelper();
|
const { appSettings } = useAppSettingHelper();
|
||||||
const chatPanelRef = useRef<ChatPanel | null>(null);
|
const chatPanelRef = useRef<ChatPanel | null>(null);
|
||||||
const { setDocReadonly } = useDocMetaHelper(workspace.docCollection);
|
const { setDocReadonly } = useDocMetaHelper(workspace.docCollection);
|
||||||
@ -226,14 +227,14 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
[jumpToPageBlock, docCollection.id, openPage, jumpToTag, workspace.id]
|
[jumpToPageBlock, docCollection.id, openPage, jumpToTag, workspace.id]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [refCallback, hasScrollTop] = useHasScrollTop();
|
||||||
|
const dynamicTopBorder = environment.isDesktop;
|
||||||
|
|
||||||
const openOutlinePanel = useCallback(() => {
|
const openOutlinePanel = useCallback(() => {
|
||||||
workbench.openSidebar();
|
workbench.openSidebar();
|
||||||
view.activeSidebarTab('outline');
|
view.activeSidebarTab('outline');
|
||||||
}, [workbench, view]);
|
}, [workbench, view]);
|
||||||
|
|
||||||
const [refCallback, hasScrollTop] = useHasScrollTop();
|
|
||||||
const dynamicTopBorder = environment.isDesktop;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewHeader>
|
<ViewHeader>
|
||||||
@ -269,15 +270,16 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</Scrollable.Root>
|
</Scrollable.Root>
|
||||||
|
{appSettings.enableOutlineViewer && (
|
||||||
|
<EditorOutlineViewer
|
||||||
|
editor={editor}
|
||||||
|
show={mode === 'page' && !isSideBarOpen}
|
||||||
|
openOutlinePanel={openOutlinePanel}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</AffineErrorBoundary>
|
</AffineErrorBoundary>
|
||||||
{isInTrash ? <TrashPageFooter /> : null}
|
{isInTrash ? <TrashPageFooter /> : null}
|
||||||
</div>
|
</div>
|
||||||
{appSettings.enableOutlineViewer && (
|
|
||||||
<EditorOutlineViewer
|
|
||||||
editor={editor}
|
|
||||||
toggleOutlinePanel={openOutlinePanel}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ViewBody>
|
</ViewBody>
|
||||||
|
|
||||||
<ViewSidebarTab tabId="chat" icon={<AiIcon />} unmountOnInactive={false}>
|
<ViewSidebarTab tabId="chat" icon={<AiIcon />} unmountOnInactive={false}>
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
import { style } from '@vanilla-extract/css';
|
|
||||||
|
|
||||||
export const root = style({
|
|
||||||
position: 'fixed',
|
|
||||||
top: 256,
|
|
||||||
right: 22,
|
|
||||||
maxHeight: 'calc(100% - 16px)',
|
|
||||||
});
|
|
@ -95,4 +95,4 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"ts-node": "*"
|
"ts-node": "*"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -719,6 +719,8 @@
|
|||||||
"com.affine.filterList.button.add": "Add Filter",
|
"com.affine.filterList.button.add": "Add Filter",
|
||||||
"com.affine.header.option.add-tag": "Add Tag",
|
"com.affine.header.option.add-tag": "Add Tag",
|
||||||
"com.affine.header.option.duplicate": "Duplicate",
|
"com.affine.header.option.duplicate": "Duplicate",
|
||||||
|
"com.affine.header.option.view-toc": "View table of contents",
|
||||||
|
"com.affine.header.option.view-frame": "View all frames",
|
||||||
"com.affine.helpIsland.contactUs": "Contact us",
|
"com.affine.helpIsland.contactUs": "Contact us",
|
||||||
"com.affine.helpIsland.gettingStarted": "Getting started",
|
"com.affine.helpIsland.gettingStarted": "Getting started",
|
||||||
"com.affine.helpIsland.helpAndFeedback": "Help and Feedback",
|
"com.affine.helpIsland.helpAndFeedback": "Help and Feedback",
|
||||||
|
@ -5,11 +5,6 @@ import {
|
|||||||
getBlockSuiteEditorTitle,
|
getBlockSuiteEditorTitle,
|
||||||
waitForEditorLoad,
|
waitForEditorLoad,
|
||||||
} from '@affine-test/kit/utils/page-logic';
|
} from '@affine-test/kit/utils/page-logic';
|
||||||
import {
|
|
||||||
confirmExperimentalPrompt,
|
|
||||||
openExperimentalFeaturesPanel,
|
|
||||||
openSettingModal,
|
|
||||||
} from '@affine-test/kit/utils/setting';
|
|
||||||
import type { Page } from '@playwright/test';
|
import type { Page } from '@playwright/test';
|
||||||
import { expect } from '@playwright/test';
|
import { expect } from '@playwright/test';
|
||||||
|
|
||||||
@ -78,45 +73,3 @@ test('link page is useable', async ({ page }) => {
|
|||||||
page.locator('.doc-title-container:has-text("page1")')
|
page.locator('.doc-title-container:has-text("page1")')
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('outline viewer is useable', async ({ page }) => {
|
|
||||||
await openHomePage(page);
|
|
||||||
await waitForEditorLoad(page);
|
|
||||||
await clickNewPageButton(page);
|
|
||||||
await waitForEditorLoad(page);
|
|
||||||
|
|
||||||
await openSettingModal(page);
|
|
||||||
await openExperimentalFeaturesPanel(page);
|
|
||||||
const prompt = page.getByTestId('experimental-prompt');
|
|
||||||
await expect(prompt).toBeVisible();
|
|
||||||
await confirmExperimentalPrompt(page);
|
|
||||||
const settings = page.getByTestId('experimental-settings');
|
|
||||||
const enableOutlineViewerSetting = settings.getByTestId(
|
|
||||||
'outline-viewer-switch'
|
|
||||||
);
|
|
||||||
await expect(enableOutlineViewerSetting).toBeVisible();
|
|
||||||
await enableOutlineViewerSetting.click();
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await page.getByTestId('modal-close-button').click();
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
|
|
||||||
const title = getBlockSuiteEditorTitle(page);
|
|
||||||
await title.pressSequentially('Title');
|
|
||||||
await page.keyboard.press('Enter');
|
|
||||||
expect(await title.innerText()).toBe('Title');
|
|
||||||
await page.keyboard.type('# ');
|
|
||||||
await page.keyboard.type('Heading 1');
|
|
||||||
await page.keyboard.press('Enter');
|
|
||||||
await page.keyboard.type('## ');
|
|
||||||
await page.keyboard.type('Heading 2');
|
|
||||||
await page.keyboard.press('Enter');
|
|
||||||
|
|
||||||
const indicators = page.locator('.outline-heading-indicator');
|
|
||||||
await expect(indicators).toHaveCount(2);
|
|
||||||
await expect(indicators.nth(0)).toBeVisible();
|
|
||||||
await expect(indicators.nth(1)).toBeVisible();
|
|
||||||
|
|
||||||
const viewer = page.locator('affine-outline-panel-body');
|
|
||||||
await indicators.first().hover({ force: true });
|
|
||||||
await expect(viewer).toBeVisible();
|
|
||||||
});
|
|
||||||
|
143
tests/affine-local/e2e/blocksuite/outline.spec.ts
Normal file
143
tests/affine-local/e2e/blocksuite/outline.spec.ts
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
import { test } from '@affine-test/kit/playwright';
|
||||||
|
import {
|
||||||
|
clickEdgelessModeButton,
|
||||||
|
clickPageModeButton,
|
||||||
|
} from '@affine-test/kit/utils/editor';
|
||||||
|
import { openHomePage } from '@affine-test/kit/utils/load-page';
|
||||||
|
import {
|
||||||
|
clickNewPageButton,
|
||||||
|
createLinkedPage,
|
||||||
|
getBlockSuiteEditorTitle,
|
||||||
|
waitForEditorLoad,
|
||||||
|
waitForEmptyEditor,
|
||||||
|
} from '@affine-test/kit/utils/page-logic';
|
||||||
|
import {
|
||||||
|
confirmExperimentalPrompt,
|
||||||
|
openExperimentalFeaturesPanel,
|
||||||
|
openSettingModal,
|
||||||
|
} from '@affine-test/kit/utils/setting';
|
||||||
|
import { expect, type Page } from '@playwright/test';
|
||||||
|
|
||||||
|
async function enableOutlineViewer(page: Page) {
|
||||||
|
await openSettingModal(page);
|
||||||
|
await openExperimentalFeaturesPanel(page);
|
||||||
|
const prompt = page.getByTestId('experimental-prompt');
|
||||||
|
await expect(prompt).toBeVisible();
|
||||||
|
await confirmExperimentalPrompt(page);
|
||||||
|
const settings = page.getByTestId('experimental-settings');
|
||||||
|
const enableOutlineViewerSetting = settings.getByTestId(
|
||||||
|
'outline-viewer-switch'
|
||||||
|
);
|
||||||
|
await expect(enableOutlineViewerSetting).toBeVisible();
|
||||||
|
await enableOutlineViewerSetting.click();
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
await page.getByTestId('modal-close-button').click();
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
test('outline viewer is useable', async ({ page }) => {
|
||||||
|
await openHomePage(page);
|
||||||
|
await waitForEditorLoad(page);
|
||||||
|
await clickNewPageButton(page);
|
||||||
|
await waitForEditorLoad(page);
|
||||||
|
await enableOutlineViewer(page);
|
||||||
|
|
||||||
|
const title = getBlockSuiteEditorTitle(page);
|
||||||
|
await title.click();
|
||||||
|
await title.pressSequentially('Title');
|
||||||
|
await expect(title).toContainText('Title');
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
await page.keyboard.type('# ');
|
||||||
|
await page.keyboard.type('Heading 1');
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
await page.keyboard.type('## ');
|
||||||
|
await page.keyboard.type('Heading 2');
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
|
||||||
|
const indicators = page.locator('.outline-heading-indicator');
|
||||||
|
await expect(indicators).toHaveCount(2);
|
||||||
|
await expect(indicators.nth(0)).toBeVisible();
|
||||||
|
await expect(indicators.nth(1)).toBeVisible();
|
||||||
|
|
||||||
|
const viewer = page.locator('affine-outline-panel-body');
|
||||||
|
await indicators.first().hover({ force: true });
|
||||||
|
await expect(viewer).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('outline viewer should hide in edgeless mode', async ({ page }) => {
|
||||||
|
await openHomePage(page);
|
||||||
|
await waitForEditorLoad(page);
|
||||||
|
await clickNewPageButton(page);
|
||||||
|
await waitForEditorLoad(page);
|
||||||
|
await enableOutlineViewer(page);
|
||||||
|
|
||||||
|
const title = getBlockSuiteEditorTitle(page);
|
||||||
|
await title.click();
|
||||||
|
await title.pressSequentially('Title');
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
await expect(title).toHaveText('Title');
|
||||||
|
await page.keyboard.type('# ');
|
||||||
|
await page.keyboard.type('Heading 1');
|
||||||
|
|
||||||
|
const indicators = page.locator('.outline-heading-indicator');
|
||||||
|
await expect(indicators).toHaveCount(1);
|
||||||
|
|
||||||
|
await clickEdgelessModeButton(page);
|
||||||
|
await expect(indicators).toHaveCount(0);
|
||||||
|
|
||||||
|
await clickPageModeButton(page);
|
||||||
|
await expect(indicators).toHaveCount(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('outline viewer should be useable in doc peek preview', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await openHomePage(page);
|
||||||
|
await waitForEditorLoad(page);
|
||||||
|
await enableOutlineViewer(page);
|
||||||
|
await clickNewPageButton(page);
|
||||||
|
await waitForEmptyEditor(page);
|
||||||
|
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
await createLinkedPage(page, 'Test Page');
|
||||||
|
|
||||||
|
await page.locator('affine-reference').hover();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.locator('.affine-reference-popover-container')
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
await page
|
||||||
|
.locator('editor-menu-button editor-icon-button[aria-label="Open doc"]')
|
||||||
|
.click();
|
||||||
|
await page
|
||||||
|
.locator('editor-menu-action:has-text("Open in center peek")')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
const peekView = page.getByTestId('peek-view-modal');
|
||||||
|
await expect(peekView).toBeVisible();
|
||||||
|
|
||||||
|
const title = peekView.locator('doc-title .inline-editor');
|
||||||
|
await title.click();
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
|
||||||
|
await page.keyboard.type('# Heading 1');
|
||||||
|
|
||||||
|
const indicators = peekView.locator('.outline-heading-indicator');
|
||||||
|
await expect(indicators).toHaveCount(1);
|
||||||
|
await expect(indicators).toBeVisible();
|
||||||
|
|
||||||
|
await indicators.first().hover({ force: true });
|
||||||
|
const viewer = peekView.locator('affine-outline-panel-body');
|
||||||
|
await expect(viewer).toBeVisible();
|
||||||
|
|
||||||
|
const toggleButton = peekView.locator(
|
||||||
|
'.outline-viewer-header-container edgeless-tool-icon-button'
|
||||||
|
);
|
||||||
|
await toggleButton.click();
|
||||||
|
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
await expect(peekView).toBeHidden();
|
||||||
|
await expect(viewer).toBeHidden();
|
||||||
|
await expect(page.locator('affine-outline-panel')).toBeVisible();
|
||||||
|
});
|
@ -46,4 +46,4 @@
|
|||||||
"dev": "node --loader ts-node/esm/transpile-only.mjs ./src/bin/dev.ts"
|
"dev": "node --loader ts-node/esm/transpile-only.mjs ./src/bin/dev.ts"
|
||||||
},
|
},
|
||||||
"version": "0.15.0"
|
"version": "0.15.0"
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user