mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-23 16:15:05 +03:00
feat(core): use new print pdf api (#7932)
This commit is contained in:
parent
cf086e4018
commit
3db95bafa2
@ -49,7 +49,7 @@
|
||||
"@radix-ui/react-toolbar": "^1.0.4",
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
"@radix-ui/react-visually-hidden": "^1.1.0",
|
||||
"@toeverything/theme": "^1.0.5",
|
||||
"@toeverything/theme": "^1.0.7",
|
||||
"@vanilla-extract/dynamic": "^2.1.0",
|
||||
"bytes": "^3.1.2",
|
||||
"check-password-strength": "^2.0.10",
|
||||
|
@ -48,7 +48,7 @@
|
||||
"@sentry/integrations": "^7.109.0",
|
||||
"@sentry/react": "^8.0.0",
|
||||
"@sgtpooki/file-type": "^1.0.1",
|
||||
"@toeverything/theme": "^1.0.5",
|
||||
"@toeverything/theme": "^1.0.7",
|
||||
"@vanilla-extract/dynamic": "^2.1.0",
|
||||
"animejs": "^3.2.2",
|
||||
"async-call-rpc": "^6.4.2",
|
||||
|
@ -42,6 +42,11 @@ export const tableHeaderInfoRow = style({
|
||||
fontSize: cssVar('fontSm'),
|
||||
fontWeight: 500,
|
||||
minHeight: 34,
|
||||
'@media': {
|
||||
print: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const tableHeaderSecondaryRow = style({
|
||||
@ -54,6 +59,11 @@ export const tableHeaderSecondaryRow = style({
|
||||
padding: '0 6px',
|
||||
gap: '8px',
|
||||
height: 24,
|
||||
'@media': {
|
||||
print: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const tableHeaderCollapseButtonWrapper = style({
|
||||
@ -101,12 +111,26 @@ export const tableHeaderDivider = style({
|
||||
borderTop: `0.5px solid ${cssVar('borderColor')}`,
|
||||
width: '100%',
|
||||
margin: '8px 0',
|
||||
'@media': {
|
||||
print: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const tableBodyRoot = style({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 8,
|
||||
'@media': {
|
||||
print: {
|
||||
selectors: {
|
||||
'&[data-state="open"]': {
|
||||
marginBottom: 32,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const tableBodySortable = style({
|
||||
@ -124,6 +148,11 @@ export const addPropertyButton = style({
|
||||
height: 36,
|
||||
fontWeight: 400,
|
||||
gap: 6,
|
||||
'@media': {
|
||||
print: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
});
|
||||
globalStyle(`${addPropertyButton} svg`, {
|
||||
fontSize: 16,
|
||||
|
@ -148,6 +148,11 @@ export const rowContainerStyle = style({
|
||||
alignItems: 'center',
|
||||
padding: '4px',
|
||||
});
|
||||
export const exportContainerStyle = style({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '8px',
|
||||
});
|
||||
export const labelStyle = style({
|
||||
fontSize: cssVar('fontSm'),
|
||||
fontWeight: 500,
|
||||
|
@ -1,20 +1,22 @@
|
||||
import { ExportMenuItems } from '@affine/core/components/page-list';
|
||||
import {
|
||||
ExportMenuItems,
|
||||
PrintMenuItems,
|
||||
} from '@affine/core/components/page-list';
|
||||
import { useExportPage } from '@affine/core/hooks/affine/use-export-page';
|
||||
import { EditorService } from '@affine/core/modules/editor';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
|
||||
import * as styles from './index.css';
|
||||
import type { ShareMenuProps } from './share-menu';
|
||||
|
||||
export const ShareExport = ({ currentPage }: ShareMenuProps) => {
|
||||
export const ShareExport = () => {
|
||||
const t = useI18n();
|
||||
const editor = useService(EditorService).editor;
|
||||
const exportHandler = useExportPage(currentPage);
|
||||
const exportHandler = useExportPage();
|
||||
const currentMode = useLiveData(editor.mode$);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.exportContainerStyle}>
|
||||
<div className={styles.descriptionStyle}>
|
||||
{t['com.affine.share-menu.ShareViaExportDescription']()}
|
||||
</div>
|
||||
@ -25,6 +27,19 @@ export const ShareExport = ({ currentPage }: ShareMenuProps) => {
|
||||
pageMode={currentMode}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
{currentMode === 'page' && (
|
||||
<>
|
||||
<div className={styles.descriptionStyle}>
|
||||
{t['com.affine.share-menu.ShareViaPrintDescription']()}
|
||||
</div>
|
||||
<div>
|
||||
<PrintMenuItems
|
||||
exportHandler={exportHandler}
|
||||
className={styles.exportItemStyle}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -39,7 +39,7 @@ export const ShareMenuContent = (props: ShareMenuProps) => {
|
||||
<SharePage {...props} />
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="export">
|
||||
<ShareExport {...props} />
|
||||
<ShareExport />
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
</div>
|
||||
|
@ -14,6 +14,11 @@ export const container = style({
|
||||
padding: '0 24px',
|
||||
},
|
||||
},
|
||||
'@media': {
|
||||
print: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const dividerContainer = style({
|
||||
|
@ -168,6 +168,9 @@ export const BlocksuiteEditorContainer = forwardRef<
|
||||
get mode() {
|
||||
return mode;
|
||||
},
|
||||
get origin() {
|
||||
return rootRef.current;
|
||||
},
|
||||
};
|
||||
|
||||
const proxy = new Proxy(api, {
|
||||
|
@ -162,7 +162,7 @@ export const PageHeaderMenuButton = ({
|
||||
}
|
||||
}, []);
|
||||
|
||||
const exportHandler = useExportPage(editorService.editor.doc.blockSuiteDoc);
|
||||
const exportHandler = useExportPage();
|
||||
|
||||
const handleDuplicate = useCallback(() => {
|
||||
duplicate(pageId);
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { MenuItem, MenuSub } from '@affine/component';
|
||||
import { MenuItem, MenuSeparator, MenuSub } from '@affine/component';
|
||||
import { track } from '@affine/core/mixpanel';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import {
|
||||
ExportIcon,
|
||||
ExportToHtmlIcon,
|
||||
ExportToMarkdownIcon,
|
||||
ExportToPdfIcon,
|
||||
ExportToPngIcon,
|
||||
FileIcon,
|
||||
} from '@blocksuite/icons/rc';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { transitionStyle } from './index.css';
|
||||
|
||||
@ -22,7 +22,7 @@ interface ExportMenuItemProps<T> {
|
||||
}
|
||||
|
||||
interface ExportProps {
|
||||
exportHandler: (type: 'pdf' | 'html' | 'png' | 'markdown') => Promise<void>;
|
||||
exportHandler: (type: 'pdf' | 'html' | 'png' | 'markdown') => void;
|
||||
pageMode?: 'page' | 'edgeless';
|
||||
className?: string;
|
||||
}
|
||||
@ -47,74 +47,73 @@ export function ExportMenuItem<T>({
|
||||
);
|
||||
}
|
||||
|
||||
export const PrintMenuItems = ({
|
||||
exportHandler,
|
||||
className = transitionStyle,
|
||||
}: ExportProps) => {
|
||||
const t = useI18n();
|
||||
return (
|
||||
<ExportMenuItem
|
||||
onSelect={() => exportHandler('pdf')}
|
||||
className={className}
|
||||
type="pdf"
|
||||
icon={<FileIcon />}
|
||||
label={t['com.affine.export.print']()}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const ExportMenuItems = ({
|
||||
exportHandler,
|
||||
className = transitionStyle,
|
||||
pageMode = 'page',
|
||||
}: ExportProps) => {
|
||||
const t = useI18n();
|
||||
const itemMap = useMemo(
|
||||
() => [
|
||||
{
|
||||
component: ExportMenuItem,
|
||||
props: {
|
||||
onSelect: () => exportHandler('pdf'),
|
||||
className: className,
|
||||
type: 'pdf',
|
||||
icon: <ExportToPdfIcon />,
|
||||
label: t['Export to PDF'](),
|
||||
},
|
||||
},
|
||||
{
|
||||
component: ExportMenuItem,
|
||||
props: {
|
||||
onSelect: () => exportHandler('html'),
|
||||
className: className,
|
||||
type: 'html',
|
||||
icon: <ExportToHtmlIcon />,
|
||||
label: t['Export to HTML'](),
|
||||
},
|
||||
},
|
||||
{
|
||||
component: ExportMenuItem,
|
||||
props: {
|
||||
onSelect: () => exportHandler('png'),
|
||||
className: className,
|
||||
type: 'png',
|
||||
icon: <ExportToPngIcon />,
|
||||
label: t['Export to PNG'](),
|
||||
},
|
||||
},
|
||||
{
|
||||
component: ExportMenuItem,
|
||||
props: {
|
||||
onSelect: () => exportHandler('markdown'),
|
||||
className: className,
|
||||
type: 'markdown',
|
||||
icon: <ExportToMarkdownIcon />,
|
||||
label: t['Export to Markdown'](),
|
||||
},
|
||||
},
|
||||
],
|
||||
[className, exportHandler, t]
|
||||
return (
|
||||
<>
|
||||
<ExportMenuItem
|
||||
onSelect={() => exportHandler('html')}
|
||||
className={className}
|
||||
type="html"
|
||||
icon={<ExportToHtmlIcon />}
|
||||
label={t['Export to HTML']()}
|
||||
/>
|
||||
{pageMode !== 'edgeless' && (
|
||||
<ExportMenuItem
|
||||
onSelect={() => exportHandler('png')}
|
||||
className={className}
|
||||
type="png"
|
||||
icon={<ExportToPngIcon />}
|
||||
label={t['Export to PNG']()}
|
||||
/>
|
||||
)}
|
||||
<ExportMenuItem
|
||||
onSelect={() => exportHandler('markdown')}
|
||||
className={className}
|
||||
type="markdown"
|
||||
icon={<ExportToMarkdownIcon />}
|
||||
label={t['Export to Markdown']()}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
const items = itemMap.map(({ component: Component, props }) =>
|
||||
pageMode === 'edgeless' &&
|
||||
(props.type === 'pdf' || props.type === 'png') ? null : (
|
||||
<Component key={props.label} {...props} />
|
||||
)
|
||||
);
|
||||
return items;
|
||||
};
|
||||
|
||||
export const Export = ({ exportHandler, className, pageMode }: ExportProps) => {
|
||||
const t = useI18n();
|
||||
const items = (
|
||||
<ExportMenuItems
|
||||
exportHandler={exportHandler}
|
||||
className={className}
|
||||
pageMode={pageMode}
|
||||
/>
|
||||
<>
|
||||
<ExportMenuItems
|
||||
exportHandler={exportHandler}
|
||||
className={className}
|
||||
pageMode={pageMode}
|
||||
/>
|
||||
{pageMode !== 'edgeless' && (
|
||||
<>
|
||||
<MenuSeparator />
|
||||
<PrintMenuItems exportHandler={exportHandler} className={className} />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
const handleExportMenuOpenChange = useCallback((open: boolean) => {
|
||||
if (open) {
|
||||
|
@ -4,23 +4,35 @@ import {
|
||||
resolveGlobalLoadingEventAtom,
|
||||
} from '@affine/component/global-loading';
|
||||
import { track } from '@affine/core/mixpanel';
|
||||
import { apis } from '@affine/electron-api';
|
||||
import { EditorService } from '@affine/core/modules/editor';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import type { PageRootService, RootBlockModel } from '@blocksuite/blocks';
|
||||
import { HtmlTransformer, MarkdownTransformer } from '@blocksuite/blocks';
|
||||
import type { PageRootService } from '@blocksuite/blocks';
|
||||
import {
|
||||
HtmlTransformer,
|
||||
MarkdownTransformer,
|
||||
printToPdf,
|
||||
} from '@blocksuite/blocks';
|
||||
import type { AffineEditorContainer } from '@blocksuite/presets';
|
||||
import type { Doc } from '@blocksuite/store';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { useAsyncCallback } from '../affine-async-hooks';
|
||||
|
||||
type ExportType = 'pdf' | 'html' | 'png' | 'markdown';
|
||||
|
||||
interface ExportHandlerOptions {
|
||||
page: Doc;
|
||||
editorContainer: AffineEditorContainer;
|
||||
type: ExportType;
|
||||
}
|
||||
|
||||
async function exportHandler({ page, type }: ExportHandlerOptions) {
|
||||
async function exportHandler({
|
||||
page,
|
||||
type,
|
||||
editorContainer,
|
||||
}: ExportHandlerOptions) {
|
||||
const editorRoot = document.querySelector('editor-host');
|
||||
let pageService: PageRootService | null = null;
|
||||
if (editorRoot) {
|
||||
@ -37,15 +49,8 @@ async function exportHandler({ page, type }: ExportHandlerOptions) {
|
||||
await MarkdownTransformer.exportDoc(page);
|
||||
break;
|
||||
case 'pdf':
|
||||
if (environment.isDesktop && page.meta?.mode === 'page') {
|
||||
await apis?.export.savePDFFileAs(
|
||||
(page.root as RootBlockModel).title.toString()
|
||||
);
|
||||
} else {
|
||||
if (!pageService) return;
|
||||
await pageService.exportManager.exportPdf();
|
||||
}
|
||||
break;
|
||||
await printToPdf(editorContainer);
|
||||
return;
|
||||
case 'png': {
|
||||
if (!pageService) return;
|
||||
await pageService.exportManager.exportPng();
|
||||
@ -54,21 +59,31 @@ async function exportHandler({ page, type }: ExportHandlerOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
export const useExportPage = (page: Doc) => {
|
||||
export const useExportPage = () => {
|
||||
const editor = useService(EditorService).editor;
|
||||
const editorContainer = useLiveData(editor.editorContainer$);
|
||||
const blocksuiteDoc = editor.doc.blockSuiteDoc;
|
||||
const pushGlobalLoadingEvent = useSetAtom(pushGlobalLoadingEventAtom);
|
||||
const resolveGlobalLoadingEvent = useSetAtom(resolveGlobalLoadingEventAtom);
|
||||
const t = useI18n();
|
||||
|
||||
const onClickHandler = useCallback(
|
||||
const onClickHandler = useAsyncCallback(
|
||||
async (type: ExportType) => {
|
||||
if (editorContainer === null) return;
|
||||
|
||||
// editor container is wrapped by a proxy, we need to get the origin
|
||||
const originEditorContainer = (editorContainer as any)
|
||||
.origin as AffineEditorContainer;
|
||||
|
||||
const globalLoadingID = nanoid();
|
||||
pushGlobalLoadingEvent({
|
||||
key: globalLoadingID,
|
||||
});
|
||||
try {
|
||||
await exportHandler({
|
||||
page,
|
||||
page: blocksuiteDoc,
|
||||
type,
|
||||
editorContainer: originEditorContainer,
|
||||
});
|
||||
notify.success({
|
||||
title: t['com.affine.export.success.title'](),
|
||||
@ -84,7 +99,13 @@ export const useExportPage = (page: Doc) => {
|
||||
resolveGlobalLoadingEvent(globalLoadingID);
|
||||
}
|
||||
},
|
||||
[page, pushGlobalLoadingEvent, resolveGlobalLoadingEvent, t]
|
||||
[
|
||||
blocksuiteDoc,
|
||||
editorContainer,
|
||||
pushGlobalLoadingEvent,
|
||||
resolveGlobalLoadingEvent,
|
||||
t,
|
||||
]
|
||||
);
|
||||
|
||||
return onClickHandler;
|
||||
|
@ -51,7 +51,7 @@ export function useRegisterBlocksuiteEditorCommands(editor: Editor) {
|
||||
}, [setInfoModalState]);
|
||||
|
||||
const { duplicate } = useBlockSuiteMetaHelper(docCollection);
|
||||
const exportHandler = useExportPage(doc.blockSuiteDoc);
|
||||
const exportHandler = useExportPage();
|
||||
const { setTrashModal } = useTrashModalHelper(docCollection);
|
||||
const onClickDelete = useCallback(
|
||||
(title: string) => {
|
||||
@ -189,7 +189,7 @@ export function useRegisterBlocksuiteEditorCommands(editor: Editor) {
|
||||
type: 'pdf',
|
||||
});
|
||||
|
||||
await exportHandler('pdf');
|
||||
exportHandler('pdf');
|
||||
},
|
||||
})
|
||||
);
|
||||
@ -206,7 +206,7 @@ export function useRegisterBlocksuiteEditorCommands(editor: Editor) {
|
||||
type: 'html',
|
||||
});
|
||||
|
||||
await exportHandler('html');
|
||||
exportHandler('html');
|
||||
},
|
||||
})
|
||||
);
|
||||
@ -223,7 +223,7 @@ export function useRegisterBlocksuiteEditorCommands(editor: Editor) {
|
||||
type: 'png',
|
||||
});
|
||||
|
||||
await exportHandler('png');
|
||||
exportHandler('png');
|
||||
},
|
||||
})
|
||||
);
|
||||
@ -240,7 +240,7 @@ export function useRegisterBlocksuiteEditorCommands(editor: Editor) {
|
||||
type: 'markdown',
|
||||
});
|
||||
|
||||
await exportHandler('markdown');
|
||||
exportHandler('markdown');
|
||||
},
|
||||
})
|
||||
);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import type { DocMode } from '@blocksuite/blocks';
|
||||
import type { AffineEditorContainer } from '@blocksuite/presets';
|
||||
import type { DocService, WorkspaceService } from '@toeverything/infra';
|
||||
import { Entity, LiveData } from '@toeverything/infra';
|
||||
|
||||
@ -14,6 +15,8 @@ export class Editor extends Entity<{ defaultMode: DocMode }> {
|
||||
readonly isSharedMode =
|
||||
this.workspaceService.workspace.openOptions.isSharedMode;
|
||||
|
||||
readonly editorContainer$ = new LiveData<AffineEditorContainer | null>(null);
|
||||
|
||||
toggleMode() {
|
||||
this.mode$.next(this.mode$.value === 'edgeless' ? 'page' : 'edgeless');
|
||||
}
|
||||
@ -22,6 +25,10 @@ export class Editor extends Entity<{ defaultMode: DocMode }> {
|
||||
this.mode$.next(mode);
|
||||
}
|
||||
|
||||
setEditorContainer(editorContainer: AffineEditorContainer | null) {
|
||||
this.editorContainer$.next(editorContainer);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly docService: DocService,
|
||||
private readonly workspaceService: WorkspaceService
|
||||
|
@ -102,8 +102,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
||||
|
||||
const isInTrash = useLiveData(doc.meta$.map(meta => meta.trash));
|
||||
const { openPage, jumpToPageBlock, jumpToTag } = useNavigateHelper();
|
||||
const [editorContainer, setEditorContainer] =
|
||||
useState<AffineEditorContainer | null>(null);
|
||||
const editorContainer = useLiveData(editor.editorContainer$);
|
||||
|
||||
const isSideBarOpen = useLiveData(workbench.sidebarOpen$);
|
||||
const { appSettings } = useAppSettingHelper();
|
||||
@ -179,7 +178,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
||||
usePageDocumentTitle(title);
|
||||
|
||||
const onLoad = useCallback(
|
||||
(bsPage: BlockSuiteDoc, editor: AffineEditorContainer) => {
|
||||
(bsPage: BlockSuiteDoc, editorContainer: AffineEditorContainer) => {
|
||||
try {
|
||||
// todo(joooye34): improve the following migration code
|
||||
const surfaceBlock = bsPage.getBlockByFlavour('affine:surface')[0];
|
||||
@ -201,7 +200,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
||||
} catch {}
|
||||
|
||||
// blocksuite editor host
|
||||
const editorHost = editor.host;
|
||||
const editorHost = editorContainer.host;
|
||||
|
||||
// provide image proxy endpoint to blocksuite
|
||||
editorHost?.std.clipboard.use(
|
||||
@ -240,13 +239,20 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
||||
);
|
||||
}
|
||||
|
||||
setEditorContainer(editor);
|
||||
editor.setEditorContainer(editorContainer);
|
||||
|
||||
return () => {
|
||||
disposable.dispose();
|
||||
};
|
||||
},
|
||||
[jumpToPageBlock, docCollection.id, openPage, jumpToTag, workspace.id]
|
||||
[
|
||||
editor,
|
||||
jumpToPageBlock,
|
||||
docCollection.id,
|
||||
openPage,
|
||||
jumpToTag,
|
||||
workspace.id,
|
||||
]
|
||||
);
|
||||
|
||||
const [refCallback, hasScrollTop] = useHasScrollTop();
|
||||
|
@ -975,6 +975,7 @@
|
||||
"com.affine.payment.billing-setting.upgrade": "Upgrade",
|
||||
"com.affine.payment.billing-setting.view-invoice": "View invoice",
|
||||
"com.affine.payment.billing-setting.year": "year",
|
||||
"com.affine.export.print": "Print",
|
||||
"com.affine.payment.billing-type-form.description": "Please tell us more about your use case, to make AFFiNE better.",
|
||||
"com.affine.payment.billing-type-form.go": "Go",
|
||||
"com.affine.payment.billing-type-form.title": "Tell us your use case",
|
||||
@ -1276,6 +1277,7 @@
|
||||
"com.affine.share-menu.SharePage": "Share doc",
|
||||
"com.affine.share-menu.ShareViaExport": "Share via export",
|
||||
"com.affine.share-menu.ShareViaExportDescription": "Download a static copy of your doc to share with others.",
|
||||
"com.affine.share-menu.ShareViaPrintDescription": "Print a paper copy.",
|
||||
"com.affine.share-menu.ShareWithLink": "Share with link",
|
||||
"com.affine.share-menu.ShareWithLinkDescription": "Create a link you can easily share with anyone. The visitors will open your doc in the form od a document",
|
||||
"com.affine.share-menu.SharedPage": "Shared doc",
|
||||
|
@ -336,19 +336,6 @@ test('assert the recent browse pages are on the recent list', async ({
|
||||
}
|
||||
});
|
||||
|
||||
test('can use cmdk to export pdf', async ({ page }) => {
|
||||
await openHomePage(page);
|
||||
await waitForEditorLoad(page);
|
||||
await clickNewPageButton(page);
|
||||
await getBlockSuiteEditorTitle(page).click();
|
||||
await getBlockSuiteEditorTitle(page).fill('this is a new page to export');
|
||||
await openQuickSearchByShortcut(page);
|
||||
const [download] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
keyboardDownAndSelect(page, 'Export to PDF'),
|
||||
]);
|
||||
expect(download.suggestedFilename()).toBe('this is a new page to export.pdf');
|
||||
});
|
||||
test('can use cmdk to export png', async ({ page }) => {
|
||||
await openHomePage(page);
|
||||
await waitForEditorLoad(page);
|
||||
|
12
yarn.lock
12
yarn.lock
@ -326,7 +326,7 @@ __metadata:
|
||||
"@storybook/react": "npm:^8.2.9"
|
||||
"@storybook/react-vite": "npm:^8.2.9"
|
||||
"@testing-library/react": "npm:^16.0.0"
|
||||
"@toeverything/theme": "npm:^1.0.5"
|
||||
"@toeverything/theme": "npm:^1.0.7"
|
||||
"@types/bytes": "npm:^3.1.4"
|
||||
"@types/react": "npm:^18.2.75"
|
||||
"@types/react-dnd": "npm:^3.0.2"
|
||||
@ -422,7 +422,7 @@ __metadata:
|
||||
"@sgtpooki/file-type": "npm:^1.0.1"
|
||||
"@swc/core": "npm:^1.4.13"
|
||||
"@testing-library/react": "npm:^16.0.0"
|
||||
"@toeverything/theme": "npm:^1.0.5"
|
||||
"@toeverything/theme": "npm:^1.0.7"
|
||||
"@types/animejs": "npm:^3.1.12"
|
||||
"@types/bytes": "npm:^3.1.4"
|
||||
"@types/image-blob-reduce": "npm:^4.1.4"
|
||||
@ -13858,10 +13858,10 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@toeverything/theme@npm:^1.0.2, @toeverything/theme@npm:^1.0.5":
|
||||
version: 1.0.5
|
||||
resolution: "@toeverything/theme@npm:1.0.5"
|
||||
checksum: 10/26f177192c546b8b6c953a4d75da5520486fa92c872b889052861cd121ef9840b5006e2f1513988117290066812e08015aa99b025d13c01ca50563a9ba26a732
|
||||
"@toeverything/theme@npm:^1.0.2, @toeverything/theme@npm:^1.0.7":
|
||||
version: 1.0.7
|
||||
resolution: "@toeverything/theme@npm:1.0.7"
|
||||
checksum: 10/86b46af255450ab7ea0a20faf41c27793129852759d23736e914876a696c40e6daa15b25cde7353cd56c673c6191d04cabe6b77f6131ba0b0862bb8d482d7a01
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user