mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +00:00
test(core): add tests for page info ui (#5769)

This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import { ConfirmModal } from '@affine/component';
|
||||
import { WorkspacePropertiesAdapter } from '@affine/core/modules/workspace';
|
||||
import type { PageInfoCustomPropertyMeta } from '@affine/core/modules/workspace/properties/schema';
|
||||
import { Trans } from '@affine/i18n';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { useService } from '@toeverything/infra/di';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { PagePropertiesMetaManager } from './page-properties-manager';
|
||||
|
||||
export const ConfirmDeletePropertyModal = ({
|
||||
onConfirm,
|
||||
onCancel,
|
||||
property,
|
||||
show,
|
||||
}: {
|
||||
property: PageInfoCustomPropertyMeta;
|
||||
show: boolean;
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
}) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const adapter = useService(WorkspacePropertiesAdapter);
|
||||
const count = useMemo(() => {
|
||||
const manager = new PagePropertiesMetaManager(adapter);
|
||||
return manager.getPropertyRelatedPages(property.id)?.size || 0;
|
||||
}, [adapter, property.id]);
|
||||
|
||||
return (
|
||||
<ConfirmModal
|
||||
open={show}
|
||||
closeButtonOptions={{
|
||||
onClick: onCancel,
|
||||
}}
|
||||
title={t['com.affine.settings.workspace.properties.delete-property']()}
|
||||
description={
|
||||
<Trans
|
||||
values={{
|
||||
name: property.name,
|
||||
count,
|
||||
}}
|
||||
i18nKey="com.affine.settings.workspace.properties.delete-property-prompt"
|
||||
>
|
||||
The <strong>{{ name: property.name } as any}</strong> property will be
|
||||
removed from count doc(s). This action cannot be undone.
|
||||
</Trans>
|
||||
}
|
||||
onConfirm={onConfirm}
|
||||
cancelButtonOptions={{
|
||||
onClick: onCancel,
|
||||
}}
|
||||
confirmButtonOptions={{
|
||||
type: 'error',
|
||||
children: t['Confirm'](),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -34,7 +34,7 @@ export const IconsSelectorPanel = ({
|
||||
const t = useAFFiNEI18N();
|
||||
return (
|
||||
<Scrollable.Root>
|
||||
<div className={styles.menuHeader}>
|
||||
<div role="heading" className={styles.menuHeader}>
|
||||
{t['com.affine.page-properties.icons']()}
|
||||
</div>
|
||||
<Scrollable.Viewport className={styles.iconsContainerScrollable}>
|
||||
|
||||
@@ -90,7 +90,9 @@ export const EditPropertyNameMenuItem = ({
|
||||
const iconName = getSafeIconName(property.icon, property.type);
|
||||
const onKeyDown: KeyboardEventHandler<HTMLInputElement> = useCallback(
|
||||
e => {
|
||||
e.stopPropagation();
|
||||
if (e.key !== 'Escape') {
|
||||
e.stopPropagation();
|
||||
}
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
onBlur(e.currentTarget.value);
|
||||
|
||||
@@ -132,6 +132,10 @@ export class PagePropertiesMetaManager {
|
||||
}
|
||||
return mapping;
|
||||
}
|
||||
|
||||
getPropertyRelatedPages(id: string) {
|
||||
return this.getPropertyStatistics().get(id);
|
||||
}
|
||||
}
|
||||
|
||||
export class PagePropertiesManager {
|
||||
|
||||
@@ -212,7 +212,7 @@ export const propertyRowCell = style({
|
||||
position: 'relative',
|
||||
borderRadius: 4,
|
||||
fontSize: cssVar('fontSm'),
|
||||
lineHeight: '20px',
|
||||
lineHeight: '22px',
|
||||
userSelect: 'none',
|
||||
':focus-visible': {
|
||||
outline: 'none',
|
||||
|
||||
@@ -60,6 +60,7 @@ import {
|
||||
|
||||
import { AffinePageReference } from '../reference-link';
|
||||
import { managerContext, pageInfoCollapsedAtom } from './common';
|
||||
import { ConfirmDeletePropertyModal } from './confirm-delete-property-modal';
|
||||
import {
|
||||
getDefaultIconName,
|
||||
nameToIcon,
|
||||
@@ -281,7 +282,11 @@ const VisibilityModeSelector = ({
|
||||
},
|
||||
}}
|
||||
>
|
||||
<div data-required={required} className={styles.selectorButton}>
|
||||
<div
|
||||
role="button"
|
||||
data-required={required}
|
||||
className={styles.selectorButton}
|
||||
>
|
||||
{required ? (
|
||||
t['com.affine.page-properties.property.required']()
|
||||
) : (
|
||||
@@ -305,7 +310,11 @@ export const PagePropertiesSettingsPopup = ({
|
||||
const menuItems = useMemo(() => {
|
||||
const options: MenuItemOption[] = [];
|
||||
options.push(
|
||||
<div className={styles.menuHeader} style={{ minWidth: 320 }}>
|
||||
<div
|
||||
role="heading"
|
||||
className={styles.menuHeader}
|
||||
style={{ minWidth: 320 }}
|
||||
>
|
||||
{t['com.affine.page-properties.settings.title']()}
|
||||
</div>
|
||||
);
|
||||
@@ -327,7 +336,12 @@ export const PagePropertiesSettingsPopup = ({
|
||||
<MenuIcon>
|
||||
<Icon />
|
||||
</MenuIcon>
|
||||
<div className={styles.propertyRowName}>{name}</div>
|
||||
<div
|
||||
data-testid="page-property-setting-row-name"
|
||||
className={styles.propertyRowName}
|
||||
>
|
||||
{name}
|
||||
</div>
|
||||
<VisibilityModeSelector property={property} />
|
||||
</SortablePropertyRow>
|
||||
);
|
||||
@@ -407,6 +421,8 @@ export const PagePropertyRowNameMenu = ({
|
||||
const [localProperty, setLocalProperty] = useState(() => ({ ...property }));
|
||||
const nextVisibility = rotateVisibility(localProperty.visibility);
|
||||
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||
|
||||
const handleFinishEditing = useCallback(() => {
|
||||
onFinishEditing();
|
||||
manager.updateCustomPropertyMeta(meta.id, localPropertyMeta);
|
||||
@@ -445,14 +461,9 @@ export const PagePropertyRowNameMenu = ({
|
||||
},
|
||||
[nextVisibility]
|
||||
);
|
||||
const handleDelete = useCallback(
|
||||
(e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
manager.removeCustomProperty(property.id);
|
||||
},
|
||||
[manager, property.id]
|
||||
);
|
||||
const handleDelete = useCallback(() => {
|
||||
manager.removeCustomProperty(property.id);
|
||||
}, [manager, property.id]);
|
||||
|
||||
const handleIconChange = useCallback(
|
||||
(icon: PagePropertyIcon) => {
|
||||
@@ -496,12 +507,11 @@ export const PagePropertyRowNameMenu = ({
|
||||
type: 'danger',
|
||||
icon: <DeleteIcon />,
|
||||
text: t['com.affine.page-properties.property.remove-property'](),
|
||||
onClick: handleDelete,
|
||||
onClick: () => setShowDeleteModal(true),
|
||||
});
|
||||
}
|
||||
return renderMenuItemOptions(options);
|
||||
}, [
|
||||
handleDelete,
|
||||
handleIconChange,
|
||||
handleNameBlur,
|
||||
handleNameChange,
|
||||
@@ -513,20 +523,36 @@ export const PagePropertyRowNameMenu = ({
|
||||
]);
|
||||
|
||||
return (
|
||||
<Menu
|
||||
rootOptions={{
|
||||
open: editing,
|
||||
}}
|
||||
contentOptions={{
|
||||
onInteractOutside: handleFinishEditing,
|
||||
onClick(e) {
|
||||
e.stopPropagation();
|
||||
},
|
||||
}}
|
||||
items={menuItems}
|
||||
>
|
||||
{children}
|
||||
</Menu>
|
||||
<>
|
||||
<Menu
|
||||
rootOptions={{
|
||||
open: editing,
|
||||
}}
|
||||
contentOptions={{
|
||||
onInteractOutside: handleFinishEditing,
|
||||
onClick(e) {
|
||||
e.stopPropagation();
|
||||
},
|
||||
onKeyDown(e) {
|
||||
if (e.key === 'Escape') {
|
||||
handleFinishEditing();
|
||||
}
|
||||
},
|
||||
}}
|
||||
items={menuItems}
|
||||
>
|
||||
{children}
|
||||
</Menu>
|
||||
<ConfirmDeletePropertyModal
|
||||
onConfirm={() => {
|
||||
setShowDeleteModal(false);
|
||||
handleDelete();
|
||||
}}
|
||||
onCancel={() => setShowDeleteModal(false)}
|
||||
show={showDeleteModal}
|
||||
property={meta}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -607,7 +633,11 @@ export const PagePropertiesTableHeader = ({
|
||||
</div>
|
||||
{properties.length === 0 || manager.readonly ? null : (
|
||||
<PagePropertiesSettingsPopup>
|
||||
<IconButton type="plain" icon={<MoreHorizontalIcon />} />
|
||||
<IconButton
|
||||
data-testid="page-info-show-more"
|
||||
type="plain"
|
||||
icon={<MoreHorizontalIcon />}
|
||||
/>
|
||||
</PagePropertiesSettingsPopup>
|
||||
)}
|
||||
<div className={styles.spacer} />
|
||||
@@ -802,7 +832,7 @@ export const PagePropertiesCreatePropertyMenuItems = ({
|
||||
return useMemo(() => {
|
||||
const options: MenuItemOption[] = [];
|
||||
options.push(
|
||||
<div className={styles.menuHeader}>
|
||||
<div role="heading" className={styles.menuHeader}>
|
||||
{t['com.affine.page-properties.create-property.menu.header']()}
|
||||
</div>
|
||||
);
|
||||
@@ -864,7 +894,7 @@ const PagePropertiesAddPropertyMenuItems = ({
|
||||
const menuItems = useMemo(() => {
|
||||
const options: MenuItemOption[] = [];
|
||||
options.push(
|
||||
<div className={styles.menuHeader}>
|
||||
<div role="heading" className={styles.menuHeader}>
|
||||
{t['com.affine.page-properties.add-property.menu.header']()}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -47,7 +47,7 @@ const InlineTagsList = ({
|
||||
children,
|
||||
}: PropsWithChildren<InlineTagsListProps>) => {
|
||||
return (
|
||||
<div className={styles.inlineTagsContainer}>
|
||||
<div className={styles.inlineTagsContainer} data-testid="inline-tags-list">
|
||||
{value.map((tagId, idx) => {
|
||||
const tag = options.find(t => t.id === tagId);
|
||||
if (!tag) {
|
||||
@@ -251,7 +251,7 @@ export const TagsEditor = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.tagsEditorRoot}>
|
||||
<div data-testid="tags-editor-popup" className={styles.tagsEditorRoot}>
|
||||
<div className={styles.tagsEditorSelectedTags}>
|
||||
<InlineTagsList
|
||||
options={options}
|
||||
@@ -282,6 +282,9 @@ export const TagsEditor = ({
|
||||
<div
|
||||
key={tag.id}
|
||||
className={styles.tagSelectorItem}
|
||||
data-testid="tag-selector-item"
|
||||
data-tag-id={tag.id}
|
||||
data-tag-value={tag.value}
|
||||
onClick={() => {
|
||||
onAddTag(tag.id);
|
||||
}}
|
||||
@@ -296,6 +299,7 @@ export const TagsEditor = ({
|
||||
})}
|
||||
{exactMatch || !inputValue ? null : (
|
||||
<div
|
||||
data-testid="tag-selector-item"
|
||||
className={styles.tagSelectorItem}
|
||||
onClick={() => {
|
||||
setInputValue('');
|
||||
|
||||
@@ -269,6 +269,7 @@ const WorkspaceListItem = ({
|
||||
.map(({ key, title }) => {
|
||||
return (
|
||||
<div
|
||||
data-testid={`workspace-list-item-${key}`}
|
||||
onClick={() => {
|
||||
onClick(key);
|
||||
}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button, ConfirmModal, IconButton, Menu } from '@affine/component';
|
||||
import { Button, IconButton, Menu } from '@affine/component';
|
||||
import { SettingHeader } from '@affine/component/setting-components';
|
||||
import { useWorkspacePropertiesAdapter } from '@affine/core/hooks/use-affine-adapter';
|
||||
import { useWorkspace } from '@affine/core/hooks/use-workspace';
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
PagePropertiesMetaManager,
|
||||
type PagePropertyIcon,
|
||||
} from '../../../page-properties';
|
||||
import { ConfirmDeletePropertyModal } from '../../../page-properties/confirm-delete-property-modal';
|
||||
import {
|
||||
EditPropertyNameMenuItem,
|
||||
type MenuItemOption,
|
||||
@@ -54,57 +55,9 @@ const Divider = () => {
|
||||
return <div className={styles.divider} />;
|
||||
};
|
||||
|
||||
const ConfirmDeletePropertyModal = ({
|
||||
onConfirm,
|
||||
onCancel,
|
||||
property,
|
||||
count,
|
||||
show,
|
||||
}: {
|
||||
property: PageInfoCustomPropertyMeta;
|
||||
count: number;
|
||||
show: boolean;
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
}) => {
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
return (
|
||||
<ConfirmModal
|
||||
open={show}
|
||||
closeButtonOptions={{
|
||||
onClick: onCancel,
|
||||
}}
|
||||
title={t['com.affine.settings.workspace.properties.delete-property']()}
|
||||
description={
|
||||
<Trans
|
||||
values={{
|
||||
name: property.name,
|
||||
count,
|
||||
}}
|
||||
i18nKey="com.affine.settings.workspace.properties.delete-property-prompt"
|
||||
>
|
||||
The <strong>{{ name: property.name } as any}</strong> property will be
|
||||
removed from count doc(s). This action cannot be undone.
|
||||
</Trans>
|
||||
}
|
||||
onConfirm={onConfirm}
|
||||
cancelButtonOptions={{
|
||||
onClick: onCancel,
|
||||
}}
|
||||
confirmButtonOptions={{
|
||||
type: 'error',
|
||||
children: t['Confirm'](),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const EditPropertyButton = ({
|
||||
property,
|
||||
count,
|
||||
}: {
|
||||
count: number;
|
||||
property: PageInfoCustomPropertyMeta;
|
||||
}) => {
|
||||
const t = useAFFiNEI18N();
|
||||
@@ -234,7 +187,6 @@ const EditPropertyButton = ({
|
||||
onCancel={() => setShowDeleteModal(false)}
|
||||
show={showDeleteModal}
|
||||
property={property}
|
||||
count={count}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
@@ -281,7 +233,7 @@ const CustomPropertyRow = ({
|
||||
{t['com.affine.page-properties.property.required']()}
|
||||
</div>
|
||||
) : null}
|
||||
<EditPropertyButton property={property} count={relatedPages.length} />
|
||||
<EditPropertyButton property={property} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -44,6 +44,8 @@ export const TagItem = ({
|
||||
data-testid="page-tag"
|
||||
className={styles.tag}
|
||||
data-idx={idx}
|
||||
data-tag-id={tag.id}
|
||||
data-tag-value={tag.value}
|
||||
title={tag.value}
|
||||
style={style}
|
||||
>
|
||||
@@ -59,7 +61,11 @@ export const TagItem = ({
|
||||
/>
|
||||
<div className={styles.tagLabel}>{tag.value}</div>
|
||||
{onRemoved ? (
|
||||
<div className={styles.tagRemove} onClick={handleRemove}>
|
||||
<div
|
||||
data-testid="remove-tag-button"
|
||||
className={styles.tagRemove}
|
||||
onClick={handleRemove}
|
||||
>
|
||||
<CloseIcon />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
Reference in New Issue
Block a user