feat(core): make some tabs in sidebar persistent for all docs page (#10014)

close AF-2164

![CleanShot 2025-02-07 at 14.23.36.gif](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/LakojjjzZNf6ogjOVwKE/deb15a06-32e9-40bf-8a15-07a3c2fa8227.gif)
This commit is contained in:
CatsJuice
2025-02-10 14:58:26 +00:00
parent c78d6b81c6
commit f774868f0e
11 changed files with 68 additions and 16 deletions
@@ -49,7 +49,11 @@ export const DatePicker = (props: DatePickerProps) => {
}, []);
return (
<div className={styles.calendarRoot} style={variables}>
<div
className={styles.calendarRoot}
style={variables}
data-testid="date-picker-calendar"
>
<Component
cursor={cursor}
{...finalProps}
@@ -18,6 +18,7 @@ import { useCallback, useMemo, useState } from 'react';
import { CollectionService } from '../../../../modules/collection';
import { ViewBody, ViewHeader } from '../../../../modules/workbench';
import { AllDocSidebarTabs } from '../layouts/all-doc-sidebar-tabs';
import { EmptyCollectionList } from '../page-list-empty';
import { AllCollectionHeader } from './header';
import * as styles from './index.css';
@@ -102,6 +103,7 @@ export const AllCollection = () => {
)}
</div>
</ViewBody>
<AllDocSidebarTabs />
</>
);
};
@@ -19,6 +19,7 @@ import {
ViewIcon,
ViewTitle,
} from '../../../../modules/workbench';
import { AllDocSidebarTabs } from '../layouts/all-doc-sidebar-tabs';
import { EmptyPageList } from '../page-list-empty';
import * as styles from './all-page.css';
import { FilterContainer } from './all-page-filter';
@@ -78,6 +79,7 @@ export const AllPage = () => {
)}
</div>
</ViewBody>
<AllDocSidebarTabs />
</>
);
};
@@ -16,6 +16,7 @@ import {
ViewIcon,
ViewTitle,
} from '../../../../modules/workbench';
import { AllDocSidebarTabs } from '../layouts/all-doc-sidebar-tabs';
import { EmptyTagList } from '../page-list-empty';
import * as styles from './all-tag.css';
import { AllTagHeader } from './header';
@@ -72,6 +73,7 @@ export const AllTag = () => {
)}
</div>
</ViewBody>
<AllDocSidebarTabs />
</>
);
};
@@ -21,6 +21,7 @@ import {
ViewIcon,
ViewTitle,
} from '../../../../modules/workbench';
import { AllDocSidebarTabs } from '../layouts/all-doc-sidebar-tabs';
import { CollectionDetailHeader } from './header';
export const CollectionDetail = ({
@@ -123,6 +124,7 @@ export const Component = function CollectionPage() {
<>
<ViewIcon icon="collection" />
<ViewTitle title={collection.name} />
<AllDocSidebarTabs />
{inner}
</>
);
@@ -21,7 +21,11 @@ import { JournalService } from '@affine/core/modules/journal';
import { WorkbenchLink } from '@affine/core/modules/workbench';
import { useI18n } from '@affine/i18n';
import { CalendarXmarkIcon, EditIcon } from '@blocksuite/icons/rc';
import { useLiveData, useService } from '@toeverything/infra';
import {
useLiveData,
useService,
useServiceOptional,
} from '@toeverything/infra';
import { assignInlineVars } from '@vanilla-extract/dynamic';
import clsx from 'clsx';
import dayjs from 'dayjs';
@@ -99,9 +103,11 @@ interface JournalBlockProps {
const mobile = environment.isMobile;
export const EditorJournalPanel = () => {
const t = useI18n();
const doc = useService(DocService).doc;
const doc = useServiceOptional(DocService)?.doc;
const journalService = useService(JournalService);
const journalDateStr = useLiveData(journalService.journalDate$(doc.id));
const journalDateStr = useLiveData(
doc ? journalService.journalDate$(doc.id) : null
);
const journalDate = journalDateStr ? dayjs(journalDateStr) : null;
const isJournal = !!journalDate;
const { openJournal } = useJournalRouteHelper();
@@ -0,0 +1,19 @@
import { Scrollable } from '@affine/component';
import { ViewSidebarTab } from '@affine/core/modules/workbench';
import { TodayIcon } from '@blocksuite/icons/rc';
import { sidebarScrollArea } from '../detail-page/detail-page.css';
import { EditorJournalPanel } from '../detail-page/tabs/journal';
export const AllDocSidebarTabs = () => {
return (
<ViewSidebarTab tabId="all-docs-journal" icon={<TodayIcon />}>
<Scrollable.Root className={sidebarScrollArea}>
<Scrollable.Viewport>
<EditorJournalPanel />
</Scrollable.Viewport>
<Scrollable.Scrollbar />
</Scrollable.Root>
</ViewSidebarTab>
);
};
@@ -19,6 +19,7 @@ import { useEffect, useMemo } from 'react';
import { useParams } from 'react-router-dom';
import { PageNotFound } from '../../404';
import { AllDocSidebarTabs } from '../layouts/all-doc-sidebar-tabs';
import { EmptyPageList } from '../page-list-empty';
import { TagDetailHeader } from './header';
import * as styles from './index.css';
@@ -99,5 +100,10 @@ export const TagDetail = ({ tagId }: { tagId?: string }) => {
export const Component = () => {
const params = useParams();
return <TagDetail tagId={params.tagId} />;
return (
<>
<AllDocSidebarTabs />
<TagDetail tagId={params.tagId} />;
</>
);
};
@@ -73,7 +73,7 @@ const ViewIsland = ({
);
}
const [island] = useState<Island>(createIsland());
const [island] = useState<Island>(createIsland);
useEffect(() => {
setter(prev => ({ ...prev, [id]: island }));
+8 -5
View File
@@ -1,4 +1,4 @@
/* eslint-disable unicorn/prefer-dom-node-dataset */
/* oxlint-disable unicorn/prefer-dom-node-dataset */
import { test } from '@affine-test/kit/playwright';
import {
changeFilter,
@@ -80,13 +80,16 @@ test('use monthpicker to modify the month of datepicker', async ({ page }) => {
await clickDatePicker(page);
const lastMonth = new Date();
lastMonth.setMonth(lastMonth.getMonth() - 1);
await selectMonthFromMonthPicker(page, lastMonth);
await checkDatePickerMonth(page, lastMonth);
const datePicker = page.locator(
'[role="dialog"] [data-testid="date-picker-calendar"]'
);
await selectMonthFromMonthPicker(datePicker, lastMonth);
await checkDatePickerMonth(datePicker, lastMonth);
// change month
const nextMonth = new Date();
nextMonth.setMonth(nextMonth.getMonth() + 1);
await selectMonthFromMonthPicker(page, nextMonth);
await checkDatePickerMonth(page, nextMonth);
await selectMonthFromMonthPicker(datePicker, nextMonth);
await checkDatePickerMonth(datePicker, nextMonth);
});
test('allow creation of filters by tags', async ({ page }) => {
+11 -5
View File
@@ -1,4 +1,4 @@
import type { Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { expect } from '@playwright/test';
import { clickNewPageButton, getBlockSuiteEditorTitle } from './page-logic';
@@ -49,7 +49,7 @@ export const getPagesCount = async (page: Page) => {
}
// locator is not a HTMLElement, so we can't use dataset
// eslint-disable-next-line unicorn/prefer-dom-node-dataset
// oxlint-disable-next-line unicorn/prefer-dom-node-dataset
const count = await locator.getAttribute('data-total-count');
return count ? parseInt(count) : 0;
};
@@ -67,7 +67,7 @@ export const clickDatePicker = async (page: Page) => {
await page.locator('[data-testid="filter-arg"]').locator('input').click();
};
const clickMonthPicker = async (page: Page) => {
const clickMonthPicker = async (page: Page | Locator) => {
await page.locator('[data-testid="month-picker-button"]').click();
};
@@ -78,7 +78,10 @@ export const fillDatePicker = async (page: Page, date: Date) => {
.fill(dateFormat(date));
};
export const selectMonthFromMonthPicker = async (page: Page, date: Date) => {
export const selectMonthFromMonthPicker = async (
page: Page | Locator,
date: Date
) => {
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const year = date.getFullYear();
// Open the month picker popup
@@ -103,7 +106,10 @@ export const selectMonthFromMonthPicker = async (page: Page, date: Date) => {
await selectMonth();
};
export const checkDatePickerMonth = async (page: Page, date: Date) => {
export const checkDatePickerMonth = async (
page: Page | Locator,
date: Date
) => {
expect(
await page.getByTestId('month-picker-button').evaluate(e => e.dataset.month)
).toBe(date.getMonth().toString());