fix: action modal icon and Initializer menu close when click (#4664)

* fix: action modal icon and Initializer menu close when click

* fix: bug

* fix: bug

* feat: bug

* fix: e2e bug
This commit is contained in:
jack zhang 2024-06-15 18:03:33 +08:00 committed by GitHub
parent 6c431caec4
commit c66744d4a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 160 additions and 36 deletions

View File

@ -16,6 +16,7 @@ import {
} from '@nocobase/client';
import React from 'react';
import { appOptions } from './schema-initializer-common';
import { MenuOutlined } from '@ant-design/icons';
const myInitializer = new SchemaInitializer({
name: 'myInitializer',
@ -31,6 +32,7 @@ const myInitializer = new SchemaInitializer({
title="Add Card"
buttonText="Add Card"
isItem
icon={<MenuOutlined />}
onSubmit={({ title }) => {
insert({
type: 'void',
@ -52,6 +54,36 @@ const myInitializer = new SchemaInitializer({
);
},
},
{
name: 'b',
type: 'actionModal',
useComponentProps() {
const { insert } = useSchemaInitializer();
return {
isItem: true,
icon: <MenuOutlined />,
buttonText: 'Add Card 2',
title: 'Add Card Form 2',
schema: {
title: {
type: 'string',
title: 'Title',
required: true,
'x-component': 'Input',
'x-decorator': 'FormItem',
},
},
onSubmit({ title }) {
insert({
type: 'void',
title,
'x-decorator': 'CardItem',
'x-component': 'Hello',
});
},
}
},
}
],
});

View File

@ -16,6 +16,7 @@ import {
} from '@nocobase/client';
import React from 'react';
import { appOptions } from './schema-initializer-common';
import { MenuOutlined } from '@ant-design/icons';
const myInitializer = new SchemaInitializer({
name: 'myInitializer',
@ -31,6 +32,7 @@ const myInitializer = new SchemaInitializer({
title="Add Card"
buttonText="Add Card"
isItem
icon={<MenuOutlined />}
onSubmit={({ title }) => {
insert({
type: 'void',
@ -52,6 +54,36 @@ const myInitializer = new SchemaInitializer({
);
},
},
{
name: 'b',
type: 'actionModal',
useComponentProps() {
const { insert } = useSchemaInitializer();
return {
isItem: true,
icon: <MenuOutlined />,
buttonText: 'Add Card 2',
title: 'Add Card Form 2',
schema: {
title: {
type: 'string',
title: 'Title',
required: true,
'x-component': 'Input',
'x-decorator': 'FormItem',
},
},
onSubmit({ title }) {
insert({
type: 'void',
title,
'x-decorator': 'CardItem',
'x-component': 'Hello',
});
},
}
},
}
],
});

View File

@ -12,9 +12,11 @@ import React, { FC, useCallback, useMemo } from 'react';
import { useActionContext, SchemaComponent } from '../../../schema-component';
import { useSchemaInitializerItem } from '../context';
import { SchemaInitializerItem } from './SchemaInitializerItem';
import { uid } from '@formily/shared';
export interface SchemaInitializerActionModalProps {
title: string;
icon?: string | React.ReactNode;
schema: any;
onCancel?: () => void;
onSubmit?: (values: any) => void;
@ -23,21 +25,13 @@ export interface SchemaInitializerActionModalProps {
isItem?: boolean;
}
const SchemaInitializerActionModalItemComponent = React.forwardRef((props: any, ref) => {
const { onClick, title, ...others } = props;
return (
<SchemaInitializerItem
ref={ref}
{...others}
onClick={(e) => {
onClick?.(e.event);
}}
></SchemaInitializerItem>
);
const SchemaInitializerActionModalItemComponent = React.forwardRef((props: any, ref: any) => {
const { onClick, ...others } = props;
return <SchemaInitializerItem ref={ref} {...others} onClick={(e) => onClick?.(e.event, false)} />;
});
export const SchemaInitializerActionModal: FC<SchemaInitializerActionModalProps> = (props) => {
const { title, schema, buttonText, isItem, component, onCancel, onSubmit } = props;
const { title, icon, schema, buttonText, isItem, component, onCancel, onSubmit } = props;
const useCancelAction = useCallback(() => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const form = useForm();
@ -66,24 +60,35 @@ export const SchemaInitializerActionModal: FC<SchemaInitializerActionModalProps>
},
};
}, [onSubmit]);
const ItemComponent = useMemo(
() =>
React.forwardRef(({ onClick }: any, ref) => {
return <SchemaInitializerActionModalItemComponent onClick={onClick} ref={ref} title={buttonText} icon={icon} />;
}),
[buttonText, icon],
);
const schemaId = useMemo(() => uid(), []);
const defaultSchema = useMemo(() => {
return {
type: 'void',
properties: {
action1: {
[schemaId]: {
type: 'void',
'x-component': 'Action',
'x-component-props': component
? {
component,
icon,
}
: isItem
? {
title: buttonText,
component: SchemaInitializerActionModalItemComponent,
component: ItemComponent,
}
: {
icon: 'PlusOutlined',
icon: icon || 'PlusOutlined',
style: {
borderColor: 'var(--colorSettings)',
color: 'var(--colorSettings)',
@ -132,7 +137,7 @@ export const SchemaInitializerActionModal: FC<SchemaInitializerActionModalProps>
},
},
};
}, [buttonText, component, schema, title, useCancelAction, useSubmitAction]);
}, [buttonText, component, schemaId, schema, title, useCancelAction, useSubmitAction]);
return <SchemaComponent schema={defaultSchema as any} />;
};

View File

@ -12,7 +12,7 @@ import classNames from 'classnames';
import React, { ReactNode, memo, useMemo } from 'react';
import { Icon } from '../../../icon';
import { useCompile } from '../../../schema-component';
import { useSchemaInitializerItem } from '../context';
import { useSchemaInitializer, useSchemaInitializerItem } from '../context';
import { useAriaAttributeOfMenuItem, useSchemaInitializerMenuItems } from '../hooks';
import { SchemaInitializerMenu } from './SchemaInitializerSubMenu';
import { useSchemaInitializerStyles } from './style';
@ -27,16 +27,31 @@ export interface SchemaInitializerItemProps {
onClick?: (args?: any) => any;
applyMenuStyle?: boolean;
children?: ReactNode;
/**
* @internal
*/
closeInitializerMenuWhenClick?: boolean;
}
export const SchemaInitializerItem = memo(
React.forwardRef<any, SchemaInitializerItemProps>((props, ref) => {
const { style, name = uid(), applyMenuStyle = true, className, items, icon, title, onClick, children } = props;
const {
style,
closeInitializerMenuWhenClick = true,
name = uid(),
applyMenuStyle = true,
className,
items,
icon,
title,
onClick,
children,
} = props;
const compile = useCompile();
const childrenItems = useSchemaInitializerMenuItems(items, name, onClick);
const { componentCls, hashId } = useSchemaInitializerStyles();
const { attribute } = useAriaAttributeOfMenuItem();
const { setVisible } = useSchemaInitializer();
const menuItems = useMemo(() => {
if (!(items && items.length > 0)) return undefined;
return [
@ -47,6 +62,9 @@ export const SchemaInitializerItem = memo(
label: children || compile(title),
onClick: (info) => {
if (info.key !== name) return;
if (closeInitializerMenuWhenClick) {
setVisible?.(false);
}
onClick?.({ ...info, item: props });
},
icon: typeof icon === 'string' ? <Icon type={icon as string} /> : icon,
@ -58,12 +76,14 @@ export const SchemaInitializerItem = memo(
if (items && items.length > 0) {
return <SchemaInitializerMenu items={menuItems}></SchemaInitializerMenu>;
}
return (
<div
ref={ref}
onClick={(event) => {
event.stopPropagation();
if (closeInitializerMenuWhenClick) {
setVisible?.(false);
}
onClick?.({ event, item: props });
}}
>
@ -74,8 +94,10 @@ export const SchemaInitializerItem = memo(
>
{children || (
<>
{icon && typeof icon === 'string' ? <Icon type={icon as string} /> : icon}
<span className={classNames({ [`${hashId} ${componentCls}-item-content`]: icon })}>{compile(title)}</span>
{typeof icon === 'string' ? <Icon type={icon} /> : icon}
<span className={classNames({ [`${hashId} ${componentCls}-item-content`]: !!icon })}>
{compile(title)}
</span>
</>
)}
</div>

View File

@ -22,7 +22,7 @@ export const SchemaInitializerSwitch: FC<SchemaInitializerSwitchItemProps> = (pr
const { title, checked, ...resets } = props;
const compile = useCompile();
return (
<SchemaInitializerItem {...resets}>
<SchemaInitializerItem {...resets} closeInitializerMenuWhenClick={false}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
{compile(title)} <Switch style={{ marginLeft: 20 }} size={'small'} checked={checked} />
</div>

View File

@ -32,7 +32,7 @@ type SchemaInitializerItemBuiltInType<T = {}> = T & {
export interface SchemaInitializerItemComponentType<T = {}> {
name: string;
Component: ComponentType<T> | string;
Component?: ComponentType<T> | string;
sort?: number;
componentProps?: Omit<T, 'children'>;
useComponentProps?: () => Omit<T, 'children'>;

View File

@ -114,6 +114,7 @@ test.describe('configure actions', () => {
await page.getByLabel('schema-initializer-ActionBar-detailsWithPaging:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Edit' }).click();
await page.getByLabel('schema-initializer-ActionBar-detailsWithPaging:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.mouse.move(300, 0);

View File

@ -68,6 +68,7 @@ test.describe('actions schema settings', () => {
// 创建 Edit & Delete 两个按钮
await page.getByLabel('schema-initializer-ActionBar-detailsWithPaging:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Edit' }).click();
await page.getByLabel('schema-initializer-ActionBar-detailsWithPaging:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.mouse.move(0, 300);

View File

@ -67,7 +67,9 @@ test.describe('configure global actions', () => {
await page.getByLabel('schema-initializer-ActionBar-gridCard:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Filter' }).click();
await page.getByLabel('schema-initializer-ActionBar-gridCard:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByLabel('schema-initializer-ActionBar-gridCard:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Refresh' }).click();
await page.mouse.move(300, 0);
@ -95,7 +97,9 @@ test.describe('configure item actions', () => {
await page.getByLabel('schema-initializer-ActionBar-gridCard:configureItemActions-general').first().hover();
await page.getByRole('menuitem', { name: 'View' }).click();
await page.getByLabel('schema-initializer-ActionBar-gridCard:configureItemActions-general').first().hover();
await page.getByRole('menuitem', { name: 'Edit' }).click();
await page.getByLabel('schema-initializer-ActionBar-gridCard:configureItemActions-general').first().hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.mouse.move(300, 0);
@ -121,6 +125,7 @@ test.describe('configure item actions', () => {
await page.getByLabel('schema-initializer-ActionBar-gridCard:configureItemActions-general').first().hover();
await page.getByRole('menuitem', { name: 'Popup' }).click();
await page.getByLabel('schema-initializer-ActionBar-gridCard:configureItemActions-general').first().hover();
await page.getByRole('menuitem', { name: 'Update record' }).click();
await page.mouse.move(300, 0);

View File

@ -64,7 +64,9 @@ test.describe('configure global actions', () => {
await page.getByLabel('schema-initializer-ActionBar-list:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Filter' }).click();
await page.getByLabel('schema-initializer-ActionBar-list:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByLabel('schema-initializer-ActionBar-list:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Refresh' }).click();
await page.mouse.move(300, 0);
@ -92,7 +94,9 @@ test.describe('configure item actions', () => {
await page.getByLabel('schema-initializer-ActionBar-list:configureItemActions-general').first().hover();
await page.getByRole('menuitem', { name: 'View' }).click();
await page.getByLabel('schema-initializer-ActionBar-list:configureItemActions-general').first().hover();
await page.getByRole('menuitem', { name: 'Edit' }).click();
await page.getByLabel('schema-initializer-ActionBar-list:configureItemActions-general').first().hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.mouse.move(300, 0);
@ -118,6 +122,7 @@ test.describe('configure item actions', () => {
await page.getByLabel('schema-initializer-ActionBar-list:configureItemActions-general').first().hover();
await page.getByRole('menuitem', { name: 'Popup' }).click();
await page.getByLabel('schema-initializer-ActionBar-list:configureItemActions-general').first().hover();
await page.getByRole('menuitem', { name: 'Update record' }).click();
await page.mouse.move(300, 0);

View File

@ -38,8 +38,11 @@ test.describe('configure actions', () => {
// add buttons
await page.getByLabel('schema-initializer-ActionBar-table:configureActions-users').hover();
await page.getByRole('menuitem', { name: 'Filter' }).click();
await page.getByLabel('schema-initializer-ActionBar-table:configureActions-users').hover();
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByLabel('schema-initializer-ActionBar-table:configureActions-users').hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByLabel('schema-initializer-ActionBar-table:configureActions-users').hover();
await page.getByRole('menuitem', { name: 'Refresh' }).click();
await page.mouse.move(300, 0);

View File

@ -32,12 +32,10 @@ export const Resizable = () => {
title={t('Column width')}
component={React.forwardRef<any, any>((props, ref) => {
const { children, onClick, ...others } = props;
const { setVisible } = useSchemaInitializer();
return (
<SchemaInitializerItem
ref={ref}
onClick={({ event }) => {
setVisible(false);
onClick(event);
}}
{...others}

View File

@ -138,7 +138,13 @@ test.describe('configure actions column', () => {
await page.getByText('Actions', { exact: true }).hover();
await page.getByLabel('designer-schema-initializer-TableV2.Column-TableV2.ActionColumnDesigner-').hover();
await page.getByRole('menuitem', { name: 'View' }).click();
await page.getByText('Actions', { exact: true }).hover();
await page.getByLabel('designer-schema-initializer-TableV2.Column-TableV2.ActionColumnDesigner-').hover();
await page.getByRole('menuitem', { name: 'Edit' }).click();
await page.getByText('Actions', { exact: true }).hover();
await page.getByLabel('designer-schema-initializer-TableV2.Column-TableV2.ActionColumnDesigner-').hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('menuitem', { name: 'Duplicate' }).click();
@ -163,9 +169,10 @@ test.describe('configure actions column', () => {
// add custom action ------------------------------------------------------------
await page.getByText('Actions', { exact: true }).hover();
await page.getByLabel('designer-schema-initializer-TableV2.Column-TableV2.ActionColumnDesigner-').hover();
await page.getByRole('menuitem', { name: 'Popup' }).click();
// 此时二级菜单,不应该关闭,可以继续点击?
await page.getByText('Actions', { exact: true }).hover();
await page.getByLabel('designer-schema-initializer-TableV2.Column-TableV2.ActionColumnDesigner-').hover();
await page.getByRole('menuitem', { name: 'Update record' }).click();
await page.mouse.move(300, 0);

View File

@ -119,8 +119,11 @@ test.describe('configure actions', () => {
// add buttons
await page.getByLabel('schema-initializer-ActionBar-table:configureActions-t_unp4scqamw9').hover();
await page.getByRole('menuitem', { name: 'Filter' }).click();
await page.getByLabel('schema-initializer-ActionBar-table:configureActions-t_unp4scqamw9').hover();
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByLabel('schema-initializer-ActionBar-table:configureActions-t_unp4scqamw9').hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByLabel('schema-initializer-ActionBar-table:configureActions-t_unp4scqamw9').hover();
await page.getByRole('menuitem', { name: 'Refresh' }).click();
await page.mouse.move(300, 0);

View File

@ -37,6 +37,9 @@ const addSomeCustomActions = async (page: Page) => {
await page.getByRole('button', { name: 'Actions', exact: true }).hover();
await page.getByLabel('designer-schema-initializer-TableV2.Column-TableV2.ActionColumnDesigner-general').hover();
await page.getByRole('menuitem', { name: 'Popup' }).click();
await page.getByRole('button', { name: 'Actions', exact: true }).hover();
await page.getByLabel('designer-schema-initializer-TableV2.Column-TableV2.ActionColumnDesigner-general').hover();
await page.getByRole('menuitem', { name: 'Update record' }).click();
};

View File

@ -34,6 +34,7 @@ test.describe('where to open a popup and what can be added to it', () => {
// add blocks
await page.getByLabel('schema-initializer-Grid-popup:addNew:addBlock-general').hover();
await page.getByText('Markdown').click();
await page.getByLabel('schema-initializer-Grid-popup:addNew:addBlock-general').hover();
await page.getByText('Form').hover();
await page.getByRole('menuitem', { name: 'Current collection' }).click();
@ -146,6 +147,7 @@ test.describe('where to open a popup and what can be added to it', () => {
// add blocks
await page.getByLabel('schema-initializer-Grid-popup:bulkEdit:addBlock-general').hover();
await page.getByText('Form').click();
await page.getByLabel('schema-initializer-Grid-popup:bulkEdit:addBlock-general').hover();
await page.getByRole('menuitem', { name: 'Markdown' }).click();
await page.mouse.move(300, 0);

View File

@ -127,8 +127,8 @@ export const Action: ComposedAction = withDynamicSchemaProps(
}, [field, linkageRules, localVariables, variables]);
const handleButtonClick = useCallback(
(e: React.MouseEvent) => {
if (isPortalInBody(e.target as Element)) {
(e: React.MouseEvent, checkPortal = true) => {
if (checkPortal && isPortalInBody(e.target as Element)) {
return;
}
e.preventDefault();
@ -186,7 +186,7 @@ export const Action: ComposedAction = withDynamicSchemaProps(
{...others}
onMouseEnter={handleMouseEnter}
loading={field?.data?.loading || loading}
icon={icon ? <Icon type={icon} /> : null}
icon={typeof icon === 'string' ? <Icon type={icon} /> : icon}
disabled={disabled}
style={buttonStyle}
onClick={handleButtonClick}

View File

@ -348,9 +348,10 @@ export const DataBlockInitializer: FC<DataBlockInitializerProps> = (props) => {
onCreateBlockSchema({ item, fromOthersInPopup });
}
}
setVisible(false);
},
[getTemplateSchemaByMode, insert, onCreateBlockSchema, propsOnClick, setVisible, templateWrap],
[getTemplateSchemaByMode, insert, setVisible, onCreateBlockSchema, propsOnClick, templateWrap],
);
const items =
itemsFromProps ||

View File

@ -65,7 +65,7 @@ export const CalendarFormActionInitializers = new SchemaInitializer({
'x-component': 'Action',
'x-decorator': 'ACLActionProvider',
},
visible: function useVisible() {
useVisible: function useVisible() {
const collection = useCollection_deprecated();
return (collection.template !== 'view' || collection?.writableView) && collection.template !== 'sql';
},

View File

@ -86,10 +86,15 @@ test.describe('configure actions', () => {
await mockPage(oneEmptyGantt).goto();
await page.getByLabel('schema-initializer-ActionBar-gantt:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Filter' }).click();
await page.getByLabel('schema-initializer-ActionBar-gantt:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByLabel('schema-initializer-ActionBar-gantt:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByLabel('schema-initializer-ActionBar-gantt:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Refresh' }).click();
await page.getByLabel('schema-initializer-ActionBar-gantt:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Bulk update' }).click();
await page.getByLabel('schema-initializer-ActionBar-gantt:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Bulk edit' }).click();
await expect(page.getByLabel('action-Filter.Action-Filter-filter-general-table')).toBeVisible();
await expect(page.getByLabel('action-Action-Add new-create-general-table')).toBeVisible();

View File

@ -382,7 +382,7 @@ export const addCustomFormField = new CompatibleSchemaInitializer(
function CustomFormFieldInitializer() {
const itemConfig = useSchemaInitializerItem();
const { insert, setVisible } = useSchemaInitializer();
const { insert } = useSchemaInitializer();
const { onAddField, setCallback } = useContext(AddCustomFormFieldButtonContext);
const { getInterface } = useCollectionManager_deprecated();
@ -394,7 +394,6 @@ function CustomFormFieldInitializer() {
onClick={() => {
setCallback(() => insert);
onAddField(interfaceOptions);
setVisible(false);
}}
{...itemConfig}
/>