mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
refactor(core): refactor collection to use new filter system (#12228)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a new Collection entity and store with reactive management and real-time updates. - Added reactive favorite and shared filters with expanded filtering options. - **Refactor** - Overhauled collection and filtering logic for better performance and maintainability. - Replaced legacy filtering UI and logic with a streamlined, service-driven rules system. - Updated collection components to use reactive data streams and simplified props. - Simplified collection creation by delegating ID generation and instantiation to the service layer. - Removed deprecated hooks and replaced state-based filtering with observable-driven filtering. - **Bug Fixes** - Improved accuracy and consistency of tag and favorite filtering in collections. - **Chores** - Removed deprecated and unused filter-related files, types, components, and styles to reduce complexity. - Cleaned up imports and removed unused code across multiple components. - **Documentation** - Corrected inline documentation for improved clarity. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -1,17 +1,6 @@
|
||||
/* oxlint-disable unicorn/prefer-dom-node-dataset */
|
||||
import { test } from '@affine-test/kit/playwright';
|
||||
import {
|
||||
changeFilter,
|
||||
checkDatePicker,
|
||||
checkDatePickerMonth,
|
||||
checkFilterName,
|
||||
clickDatePicker,
|
||||
createFirstFilter,
|
||||
createPageWithTag,
|
||||
getPagesCount,
|
||||
selectMonthFromMonthPicker,
|
||||
selectTag,
|
||||
} from '@affine-test/kit/utils/filter';
|
||||
import { getPagesCount } from '@affine-test/kit/utils/filter';
|
||||
import { openHomePage } from '@affine-test/kit/utils/load-page';
|
||||
import {
|
||||
clickNewPageButton,
|
||||
@@ -52,75 +41,6 @@ test('all page can create new edgeless page', async ({ page }) => {
|
||||
await expect(page.locator('affine-edgeless-root')).toBeVisible();
|
||||
});
|
||||
|
||||
test('allow creation of filters by favorite', async ({ page }) => {
|
||||
await openHomePage(page);
|
||||
await waitForEditorLoad(page);
|
||||
await clickSideBarAllPageButton(page);
|
||||
await createFirstFilter(page, 'Favourited');
|
||||
await page
|
||||
.locator('[data-testid="filter-arg"]', { hasText: 'true' })
|
||||
.locator('div')
|
||||
.click();
|
||||
expect(await page.locator('[data-testid="filter-arg"]').textContent()).toBe(
|
||||
'false'
|
||||
);
|
||||
});
|
||||
|
||||
test('use monthpicker to modify the month of datepicker', async ({ page }) => {
|
||||
await openHomePage(page);
|
||||
await waitForEditorLoad(page);
|
||||
await clickSideBarAllPageButton(page);
|
||||
await createFirstFilter(page, 'Created');
|
||||
await checkFilterName(page, 'after');
|
||||
// init date
|
||||
const yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
await checkDatePicker(page, yesterday);
|
||||
// change month
|
||||
await clickDatePicker(page);
|
||||
const lastMonth = new Date();
|
||||
lastMonth.setMonth(lastMonth.getMonth() - 1);
|
||||
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(datePicker, nextMonth);
|
||||
await checkDatePickerMonth(datePicker, nextMonth);
|
||||
});
|
||||
|
||||
test('allow creation of filters by tags', async ({ page }) => {
|
||||
await openHomePage(page);
|
||||
await waitForEditorLoad(page);
|
||||
await clickSideBarAllPageButton(page);
|
||||
await waitForAllPagesLoad(page);
|
||||
const pageCount = await getPagesCount(page);
|
||||
expect(pageCount).not.toBe(0);
|
||||
await createFirstFilter(page, 'Tags');
|
||||
await checkFilterName(page, 'is not empty');
|
||||
const pagesWithTags = await page
|
||||
.locator('[data-testid="page-list-item"]')
|
||||
.all();
|
||||
const pagesWithTagsCount = pagesWithTags.length;
|
||||
expect(pagesWithTagsCount).toBe(0);
|
||||
await createPageWithTag(page, { title: 'Page A', tags: ['Page A'] });
|
||||
await createPageWithTag(page, { title: 'Page B', tags: ['Page B'] });
|
||||
await clickSideBarAllPageButton(page);
|
||||
await createFirstFilter(page, 'Tags');
|
||||
await checkFilterName(page, 'is not empty');
|
||||
expect(await getPagesCount(page)).toBe(pagesWithTagsCount + 2);
|
||||
await changeFilter(page, 'contains all');
|
||||
expect(await getPagesCount(page)).toBe(pageCount + 2);
|
||||
await selectTag(page, 'Page A');
|
||||
expect(await getPagesCount(page)).toBe(1);
|
||||
await changeFilter(page, 'does not contains all');
|
||||
await selectTag(page, 'Page B');
|
||||
expect(await getPagesCount(page)).toBe(pageCount + 1);
|
||||
});
|
||||
|
||||
test('enable selection and use ESC to disable selection', async ({ page }) => {
|
||||
await openHomePage(page);
|
||||
await waitForEditorLoad(page);
|
||||
@@ -155,8 +75,8 @@ test('enable selection and use ESC to disable selection', async ({ page }) => {
|
||||
.count()
|
||||
).toBeGreaterThan(0);
|
||||
|
||||
// wait for 300ms
|
||||
await page.waitForTimeout(300);
|
||||
// wait for 500ms
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// esc again, checkboxes should disappear
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
@@ -34,48 +34,43 @@ const createAndPinCollection = async (
|
||||
collectionName?: string;
|
||||
}
|
||||
) => {
|
||||
await clickNewPageButton(page);
|
||||
await getBlockSuiteEditorTitle(page).click();
|
||||
await getBlockSuiteEditorTitle(page).fill('test page');
|
||||
|
||||
// fixme: remove this timeout. looks like an issue with useBindWorkbenchToBrowserRouter?
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await page.getByTestId('all-pages').click();
|
||||
|
||||
const cell = page.getByTestId('page-list-item-title').getByText('test page');
|
||||
await expect(cell).toBeVisible();
|
||||
await page.getByTestId('create-first-filter').click({
|
||||
delay: 200,
|
||||
});
|
||||
await page
|
||||
.getByTestId('variable-select')
|
||||
.getByTestId(`filler-tag-Created`)
|
||||
.click({
|
||||
delay: 200,
|
||||
});
|
||||
await page.getByTestId('save-as-collection').click({
|
||||
delay: 200,
|
||||
});
|
||||
await page.getByTestId('navigation-panel-bar-add-collection-button').click();
|
||||
const title = page.getByTestId('prompt-modal-input');
|
||||
await expect(title).toBeVisible();
|
||||
await title.fill(options?.collectionName ?? 'test collection');
|
||||
await page.getByTestId('prompt-modal-confirm').click();
|
||||
await page.waitForTimeout(100);
|
||||
await page
|
||||
.locator('[data-testid^="navigation-panel-collection-"]')
|
||||
.first()
|
||||
.click();
|
||||
await page.getByTestId('collection-add-doc-button').click();
|
||||
await page.getByTestId('confirm-modal-confirm').click();
|
||||
|
||||
// fixme: remove this timeout. looks like an issue with useBindWorkbenchToBrowserRouter?
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await getBlockSuiteEditorTitle(page).click();
|
||||
await getBlockSuiteEditorTitle(page).fill('test page');
|
||||
|
||||
await page.getByTestId('all-pages').click();
|
||||
|
||||
const cell = page.getByTestId('page-list-item-title').getByText('test page');
|
||||
await expect(cell).toBeVisible();
|
||||
};
|
||||
|
||||
test('Show collections items in sidebar', async ({ page }) => {
|
||||
await removeOnboardingPages(page);
|
||||
await createAndPinCollection(page);
|
||||
const collections = page.getByTestId('navigation-panel-collections');
|
||||
await collections.getByTestId('category-divider-collapse-button').click();
|
||||
const items = collections.locator(
|
||||
'[data-testid^="navigation-panel-collection-"]'
|
||||
);
|
||||
await expect(items).toHaveCount(1);
|
||||
const first = items.first();
|
||||
expect(await first.textContent()).toBe('test collection');
|
||||
await first.getByTestId('navigation-panel-collapsed-button').click();
|
||||
expect((await first.textContent())!.startsWith('test collection')).toBe(true);
|
||||
const collectionPage = first
|
||||
.locator('[data-testid^="navigation-panel-doc-"]')
|
||||
.nth(0);
|
||||
@@ -118,34 +113,12 @@ test('edit collection', async ({ page }) => {
|
||||
await removeOnboardingPages(page);
|
||||
await createAndPinCollection(page);
|
||||
const collections = page.getByTestId('navigation-panel-collections');
|
||||
await collections.getByTestId('category-divider-collapse-button').click();
|
||||
const items = collections.locator(
|
||||
'[data-testid^="navigation-panel-collection-"]'
|
||||
);
|
||||
await expect(items).toHaveCount(1);
|
||||
const first = items.first();
|
||||
await first.hover();
|
||||
await first
|
||||
.getByTestId('navigation-panel-tree-node-operation-button')
|
||||
.click();
|
||||
const editCollection = page.getByText('Rename');
|
||||
await editCollection.click();
|
||||
await page.getByTestId('rename-modal-input').fill('123');
|
||||
await page.keyboard.press('Enter');
|
||||
await page.waitForTimeout(100);
|
||||
expect(await first.textContent()).toBe('123');
|
||||
});
|
||||
|
||||
test('edit collection and change filter date', async ({ page }) => {
|
||||
await removeOnboardingPages(page);
|
||||
await createAndPinCollection(page);
|
||||
const collections = page.getByTestId('navigation-panel-collections');
|
||||
await collections.getByTestId('category-divider-collapse-button').click();
|
||||
const items = collections.locator(
|
||||
'[data-testid^="navigation-panel-collection-"]'
|
||||
);
|
||||
await expect(items).toHaveCount(1);
|
||||
const first = items.first();
|
||||
await first.getByTestId('navigation-panel-collapsed-button').first().click();
|
||||
await first.hover();
|
||||
await first
|
||||
.getByTestId('navigation-panel-tree-node-operation-button')
|
||||
|
||||
@@ -1,43 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { expect } from '@playwright/test';
|
||||
|
||||
import { clickNewPageButton, getBlockSuiteEditorTitle } from './page-logic';
|
||||
|
||||
const monthNames = [
|
||||
'Jan',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Apr',
|
||||
'May',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Aug',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Dec',
|
||||
];
|
||||
|
||||
export const createFirstFilter = async (page: Page, name: string) => {
|
||||
await page.locator('[data-testid="create-first-filter"]').click();
|
||||
await page
|
||||
.locator('[data-testid="variable-select-item"]', { hasText: name })
|
||||
.click();
|
||||
await page.keyboard.press('Escape');
|
||||
};
|
||||
|
||||
export const checkFilterName = async (page: Page, name: string) => {
|
||||
const filterName = await page
|
||||
.locator('[data-testid="filter-name"]')
|
||||
.textContent();
|
||||
expect(filterName).toBe(name);
|
||||
};
|
||||
|
||||
const dateFormat = (date: Date) => {
|
||||
const month = monthNames[date.getMonth()];
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
return `${month} ${day}`;
|
||||
};
|
||||
import type { Page } from '@playwright/test';
|
||||
|
||||
// fixme: there could be multiple page lists in the Page
|
||||
export const getPagesCount = async (page: Page) => {
|
||||
@@ -54,97 +15,6 @@ export const getPagesCount = async (page: Page) => {
|
||||
return count ? parseInt(count) : 0;
|
||||
};
|
||||
|
||||
export const checkDatePicker = async (page: Page, date: Date) => {
|
||||
expect(
|
||||
await page
|
||||
.locator('[data-testid="filter-arg"]')
|
||||
.locator('input')
|
||||
.inputValue()
|
||||
).toBe(dateFormat(date));
|
||||
};
|
||||
|
||||
export const clickDatePicker = async (page: Page) => {
|
||||
await page.locator('[data-testid="filter-arg"]').locator('input').click();
|
||||
};
|
||||
|
||||
const clickMonthPicker = async (page: Page | Locator) => {
|
||||
await page.locator('[data-testid="month-picker-button"]').click();
|
||||
};
|
||||
|
||||
export const fillDatePicker = async (page: Page, date: Date) => {
|
||||
await page
|
||||
.locator('[data-testid="filter-arg"]')
|
||||
.locator('input')
|
||||
.fill(dateFormat(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
|
||||
await clickMonthPicker(page);
|
||||
const selectMonth = async (): Promise<void> => {
|
||||
const selectedYear = +(await page
|
||||
.getByTestId('month-picker-current-year')
|
||||
.innerText());
|
||||
if (selectedYear > year) {
|
||||
await page.locator('[data-testid="date-picker-nav-prev"]').click();
|
||||
return await selectMonth();
|
||||
} else if (selectedYear < year) {
|
||||
await page.locator('[data-testid="date-picker-nav-next"]').click();
|
||||
return await selectMonth();
|
||||
}
|
||||
// Click on the day cell
|
||||
const monthCell = page.locator(
|
||||
`[data-is-month-cell][aria-label="${year}-${month}"]`
|
||||
);
|
||||
await monthCell.click();
|
||||
};
|
||||
await selectMonth();
|
||||
};
|
||||
|
||||
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());
|
||||
};
|
||||
|
||||
const createTag = async (page: Page, name: string) => {
|
||||
await page.keyboard.type(name);
|
||||
await page.keyboard.press('ArrowUp');
|
||||
await page.keyboard.press('Enter');
|
||||
};
|
||||
|
||||
export const createPageWithTag = async (
|
||||
page: Page,
|
||||
options: {
|
||||
title: string;
|
||||
tags: string[];
|
||||
}
|
||||
) => {
|
||||
await page.getByTestId('all-pages').click();
|
||||
await clickNewPageButton(page);
|
||||
await getBlockSuiteEditorTitle(page).click();
|
||||
await getBlockSuiteEditorTitle(page).fill('test page');
|
||||
await page.getByTestId('page-info-collapse').click();
|
||||
await page.locator('[data-testid="property-tags-value"]').click();
|
||||
for (const name of options.tags) {
|
||||
await createTag(page, name);
|
||||
}
|
||||
await page.keyboard.press('Escape');
|
||||
};
|
||||
|
||||
export const changeFilter = async (page: Page, to: string) => {
|
||||
await page.getByTestId('filter-name').click();
|
||||
await page.getByTestId(`filler-tag-${to}`).click();
|
||||
};
|
||||
|
||||
export async function selectTag(page: Page, name: string | RegExp) {
|
||||
await page.getByTestId('filter-arg').click();
|
||||
await page.getByTestId(`multi-select-${name}`).click();
|
||||
|
||||
@@ -30,10 +30,13 @@ export async function waitForEditorLoad(page: Page) {
|
||||
}
|
||||
|
||||
export async function waitForAllPagesLoad(page: Page) {
|
||||
// if filters tag is rendered, we believe all_pages is ready
|
||||
await page.waitForSelector('[data-testid="create-first-filter"]', {
|
||||
timeout: 20000,
|
||||
});
|
||||
// if page-list-header-selection-checkbox is rendered, we believe all_pages is ready
|
||||
await page.waitForSelector(
|
||||
'[data-testid="page-list-header-selection-checkbox"]',
|
||||
{
|
||||
timeout: 20000,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export async function clickNewPageButton(page: Page, title?: string) {
|
||||
|
||||
Reference in New Issue
Block a user