diff --git a/demos/form/QueryFilter/light-filter-collapse.tsx b/demos/form/QueryFilter/light-filter-collapse.tsx index 17dce0e5513b..eccaae0189b5 100644 --- a/demos/form/QueryFilter/light-filter-collapse.tsx +++ b/demos/form/QueryFilter/light-filter-collapse.tsx @@ -1,8 +1,4 @@ -import { - LightFilter, - ProFormDateTimePicker, - ProFormSelect, -} from '@ant-design/pro-components'; +import { LightFilter } from '@ant-design/pro-components'; import { message } from 'antd'; const Demo = () => { @@ -16,7 +12,7 @@ const Demo = () => { message.success('提交成功'); }} > - { woman: '女', }} /> - + ); }; diff --git a/demos/form/QueryFilter/light-filter.tsx b/demos/form/QueryFilter/light-filter.tsx index 4d2bc79f6e7e..98a9d17e55d6 100644 --- a/demos/form/QueryFilter/light-filter.tsx +++ b/demos/form/QueryFilter/light-filter.tsx @@ -1,20 +1,4 @@ -import { - LightFilter, - ProFormCascader, - ProFormCheckbox, - ProFormDatePicker, - ProFormDateRangePicker, - ProFormDateTimePicker, - ProFormDateTimeRangePicker, - ProFormDigit, - ProFormFieldSet, - ProFormSelect, - ProFormSlider, - ProFormSwitch, - ProFormText, - ProFormTimePicker, - ProFormTreeSelect, -} from '@ant-design/pro-components'; +import { LightFilter } from '@ant-design/pro-components'; import { Radio, Space, TreeSelect } from 'antd'; import type { SizeType } from 'antd/lib/config-provider/SizeContext'; import dayjs from 'dayjs'; @@ -210,7 +194,7 @@ const Demo = () => { console.log('筛选提交:', values); }} > - { woman: '女', }} /> - { long: '这是一个很长的用来测试溢出的项目', }} /> - - { placeholder: '请选择', }} /> - { placeholder: '请选择省/市/区', }} /> - - - - - - - + + + + + + - - - - - - - - - - + + + + + + + + + + ); diff --git a/src/form/BaseForm/BaseForm.tsx b/src/form/BaseForm/BaseForm.tsx index 87240520a562..043b3e478e35 100644 --- a/src/form/BaseForm/BaseForm.tsx +++ b/src/form/BaseForm/BaseForm.tsx @@ -219,7 +219,7 @@ export type BaseFormProps, U = Record> = { /** 是否回车提交 */ isKeyPressSubmit?: boolean; /** Form 组件的类型,内部使用 */ - formComponentType?: 'DrawerForm' | 'ModalForm' | 'QueryFilter'; + formComponentType?: 'DrawerForm' | 'ModalForm' | 'QueryFilter' | 'LightFilter'; } & Omit & CommonFormProps; diff --git a/src/form/FieldContext.tsx b/src/form/FieldContext.tsx index 66d0a251964c..8ddb6930ba45 100644 --- a/src/form/FieldContext.tsx +++ b/src/form/FieldContext.tsx @@ -21,7 +21,7 @@ export type FiledContextProps = { }, ) => void; /** Form 组件的类型 */ - formComponentType?: string; + formComponentType?: 'DrawerForm' | 'ModalForm' | 'QueryFilter' | 'LightFilter' | string; /** 获取表单实例计数器 */ formKey?: string; diff --git a/src/form/components/SchemaForm/index.tsx b/src/form/components/SchemaForm/index.tsx index 43c7ce9e88fe..2c0e8f493806 100644 --- a/src/form/components/SchemaForm/index.tsx +++ b/src/form/components/SchemaForm/index.tsx @@ -22,7 +22,7 @@ import { import type { ProFormInstance } from '../../BaseForm'; import type { ProFormProps } from '../../layouts'; import { DrawerForm } from '../../layouts/DrawerForm'; -import { LightFilter } from '../../layouts/LightFilter'; +import LightFilter from '../../layouts/LightFilter'; import { ModalForm } from '../../layouts/ModalForm'; import { ProForm } from '../../layouts/ProForm'; import { QueryFilter } from '../../layouts/QueryFilter'; diff --git a/src/form/layouts/LightFilter/index.tsx b/src/form/layouts/LightFilter/index.tsx index 4f5e4d1bfc86..6d3ec4d5f2a3 100644 --- a/src/form/layouts/LightFilter/index.tsx +++ b/src/form/layouts/LightFilter/index.tsx @@ -5,61 +5,22 @@ import { ConfigProvider } from 'antd'; import type { SizeType } from 'antd/lib/config-provider/SizeContext'; import type { TooltipPlacement } from 'antd/lib/tooltip'; import { clsx } from 'clsx'; -import React, { - useContext, - useEffect, - useImperativeHandle, - useMemo, - useRef, - useState, -} from 'react'; +import React, { useContext, useImperativeHandle, useMemo, useRef, useState } from 'react'; import { useIntl } from '../../../provider'; import { FieldLabel, FilterDropdown } from '../../../utils'; import type { CommonFormProps, ProFormInstance } from '../../BaseForm'; import { BaseForm } from '../../BaseForm'; import type { LightFilterFooterRender } from '../../typing'; +import { lightFilterFieldComponents } from './lightFilterFieldComponents'; import { useStyle } from './style'; -export type LightFilterProps> = { +export type LightFilterLayoutProps> = { collapse?: boolean; - /** - * @name 收起的label dom - * - * @example collapseLabel={"收起"} - */ collapseLabel?: React.ReactNode; - /** - * @name 组件样式变体 - */ variant?: 'outlined' | 'filled' | 'borderless'; - /** - * @name 忽略rules,一般而言 LightFilter 应该不支持rules,默认是 false。 - */ ignoreRules?: boolean; - - /** - * @name 自定义 footerRender - * - * @example 自定义清除 - * footerRender={(onConfirm,onClear)=>{ return 清除 })} - */ footerRender?: LightFilterFooterRender; - - /** - * @name 支持配置弹出的位置 - * @default bottomLeft - */ placement?: TooltipPlacement; - /** - * @name 透传给内部 Popover 的属性(折叠态弹层) - * - * @description - * LightFilter 在折叠态会使用 Popover 将筛选项渲染到 body 下; - * 可通过该属性为弹层根节点添加自定义类名(如 classNames.root)以便做样式覆盖。 - * - * @example - * popoverProps={{ classNames: { root: 'my-lightfilter-popover' } } } - */ popoverProps?: Omit< PopoverProps, 'children' | 'content' | 'trigger' | 'open' | 'onOpenChange' | 'placement' @@ -67,11 +28,6 @@ export type LightFilterProps> = { } & Omit, 'onFinish'> & CommonFormProps; -/** - * 单行的查询表单,一般用于配合 table 或者 list使用 有时也会用于 card 的额外区域 - * - * @param props - */ const LightFilterContainer: React.FC<{ items: React.ReactNode[]; prefixCls: string; @@ -126,15 +82,7 @@ const LightFilterContainer: React.FC<{ label={intl.getMessage('form.lightFilter.more', '更多筛选')} /> ); - }, [ - collapseLabel, - collapse, - lightFilterClassName, - hashId, - variant, - size, - intl, - ]); + }, [collapseLabel, collapse, lightFilterClassName, hashId, variant, size, intl]); const { collapseItems, outsideItems } = useMemo(() => { const collapseItemsArr: React.ReactNode[] = []; @@ -151,7 +99,7 @@ const LightFilterContainer: React.FC<{ collapseItems: collapseItemsArr, outsideItems: outsideItemsArr, }; - }, [props.items]); + }, [props.items, collapse]); return wrapSSR(
{ @@ -246,7 +192,7 @@ const LightFilterContainer: React.FC<{ return false; }, }; - if (moreValues.hasOwnProperty(name)) { + if (Object.prototype.hasOwnProperty.call(moreValues, name)) { newFieldProps[child.props.valuePropName || 'value'] = moreValues[name]; } @@ -276,7 +222,9 @@ const LightFilterContainer: React.FC<{ ); }; -function LightFilter>(props: LightFilterProps) { +function LightFilterComponent>( + props: LightFilterLayoutProps, +) { const { size, collapse, @@ -287,7 +235,6 @@ function LightFilter>(props: LightFilterProps) { placement, formRef: userFormRef, variant, - ignoreRules, footerRender, popoverProps, ...reset @@ -304,6 +251,7 @@ function LightFilter>(props: LightFilterProps) { return ( { @@ -313,7 +261,6 @@ function LightFilter>(props: LightFilterProps) { prefixCls={prefixCls} items={items?.flatMap((item: any) => { if (!item || !item?.type) return item; - /** 如果是 ProFormGroup,直接拼接dom */ if (item?.type?.displayName === 'ProForm-Group') return item.props.children; return item; @@ -361,4 +308,11 @@ function LightFilter>(props: LightFilterProps) { ); } +type LightFilterType = typeof LightFilterComponent & typeof lightFilterFieldComponents; +const LightFilter = LightFilterComponent as LightFilterType; +Object.assign(LightFilter, lightFilterFieldComponents); +(LightFilter as { displayName?: string }).displayName = 'LightFilter'; + export { LightFilter }; +export default LightFilter; +export type { LightFilterLayoutProps as LightFilterProps }; diff --git a/src/form/layouts/LightFilter/lightFilterFieldComponents.tsx b/src/form/layouts/LightFilter/lightFilterFieldComponents.tsx new file mode 100644 index 000000000000..f419fac83669 --- /dev/null +++ b/src/form/layouts/LightFilter/lightFilterFieldComponents.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import ProFormCascader from '../../components/Cascader'; +import ProFormCheckbox from '../../components/Checkbox'; +import ProFormDatePicker from '../../components/DatePicker'; +import ProFormDateTimePicker from '../../components/DatePicker/DateTimePicker'; +import ProFormTimePicker from '../../components/DatePicker/TimePicker'; +import ProFormDateRangePicker from '../../components/DateRangePicker'; +import { ProFormDateMonthRangePicker } from '../../components/DateRangePicker/DateMonthRangePicker'; +import { ProFormDateQuarterRangePicker } from '../../components/DateRangePicker/DateQuarterRangePicker'; +import { ProFormDateTimeRangePicker } from '../../components/DateRangePicker/DateTimeRangePicker'; +import { ProFormDateWeekRangePicker } from '../../components/DateRangePicker/DateWeekRangePicker'; +import { ProFormDateYearRangePicker } from '../../components/DateRangePicker/DateYearRangePicker'; +import { ProFormTimeRangePicker } from '../../components/DateRangePicker/TimeRangePicker'; +import ProFormDigit from '../../components/Digit'; +import ProFormDigitRange from '../../components/Digit/DigitRange'; +import ProFormFieldSet from '../../components/FieldSet'; +import ProFormSelect from '../../components/Select'; +import ProFormSlider from '../../components/Slider'; +import ProFormSwitch from '../../components/Switch'; +import ProFormText from '../../components/Text'; +import ProFormTextArea from '../../components/TextArea'; +import ProFormTreeSelect from '../../components/TreeSelect'; + +const createLightFilterField = (Field: React.ComponentType>) => { + const FieldWrapper = (props: Record) => { + const { proFieldProps, ...rest } = props; + return ; + }; + FieldWrapper.displayName = 'LightFilterField'; + return FieldWrapper; +}; + +/** 挂在 `LightFilter` 上的轻量字段(`LightFilter.input` 等) */ +export const lightFilterFieldComponents = { + input: createLightFilterField(ProFormText), + password: createLightFilterField(ProFormText.Password), + text: createLightFilterField(ProFormText), + textArea: createLightFilterField(ProFormTextArea), + select: createLightFilterField(ProFormSelect), + searchSelect: createLightFilterField(ProFormSelect.SearchSelect), + treeSelect: createLightFilterField(ProFormTreeSelect), + cascader: createLightFilterField(ProFormCascader), + digit: createLightFilterField(ProFormDigit), + digitRange: createLightFilterField(ProFormDigitRange), + slider: createLightFilterField(ProFormSlider), + date: createLightFilterField(ProFormDatePicker), + dateTime: createLightFilterField(ProFormDateTimePicker), + time: createLightFilterField(ProFormTimePicker), + timeRange: createLightFilterField(ProFormTimeRangePicker), + dateRange: createLightFilterField(ProFormDateRangePicker), + dateTimeRange: createLightFilterField(ProFormDateTimeRangePicker), + weekRange: createLightFilterField(ProFormDateWeekRangePicker), + monthRange: createLightFilterField(ProFormDateMonthRangePicker), + quarterRange: createLightFilterField(ProFormDateQuarterRangePicker), + yearRange: createLightFilterField(ProFormDateYearRangePicker), + timePickerRange: createLightFilterField(ProFormTimePicker.RangePicker), + checkboxGroup: createLightFilterField(ProFormCheckbox.Group), + fieldSet: createLightFilterField(ProFormFieldSet), + switch: createLightFilterField(ProFormSwitch), +} as const; diff --git a/src/form/layouts/LightFilter/style.ts b/src/form/layouts/LightFilter/style.ts index becc45d58b90..8bfa4ba58b63 100644 --- a/src/form/layouts/LightFilter/style.ts +++ b/src/form/layouts/LightFilter/style.ts @@ -10,7 +10,6 @@ const genLightFilterStyle: GenerateStyle = (token) => { [token.componentCls]: { boxSizing: 'border-box', lineHeight: '30px', - // @see https://yuque.antfin-inc.com/tech-ui/topics/523 '&::before': { display: 'block', height: 0, @@ -55,11 +54,11 @@ const genLightFilterStyle: GenerateStyle = (token) => { export function useStyle(prefixCls: string) { return useAntdStyle('LightFilter', (token) => { - const proCardToken: LightFilterToken = { + const layoutToken: LightFilterToken = { ...token, componentCls: `.${prefixCls}`, }; - return [genLightFilterStyle(proCardToken)]; + return [genLightFilterStyle(layoutToken)]; }); } diff --git a/src/form/layouts/index.ts b/src/form/layouts/index.ts index 202a35e526d6..69568125d0b2 100644 --- a/src/form/layouts/index.ts +++ b/src/form/layouts/index.ts @@ -2,7 +2,7 @@ import { ProForm } from './ProForm'; export { DrawerForm } from './DrawerForm'; export type { DrawerFormProps } from './DrawerForm'; -export { LightFilter } from './LightFilter'; +export { default as LightFilter } from './LightFilter'; export type { LightFilterProps } from './LightFilter'; export { LoginForm } from './LoginForm'; export type { LoginFormProps } from './LoginForm'; diff --git a/src/table/Table.tsx b/src/table/Table.tsx index a57de6ed05aa..54a393b5d684 100644 --- a/src/table/Table.tsx +++ b/src/table/Table.tsx @@ -723,7 +723,7 @@ const ProTable = < }, }; - /** 是不是 LightFilter, LightFilter 有一些特殊的处理 */ + /** LightFilter(轻量筛选)时工具栏把 search 插到左侧 */ const isLightFilter: boolean = search !== false && search?.filterType === 'light'; diff --git a/src/table/components/Form/FormRender.tsx b/src/table/components/Form/FormRender.tsx index 7d7cb72035af..2932431713ab 100644 --- a/src/table/components/Form/FormRender.tsx +++ b/src/table/components/Form/FormRender.tsx @@ -57,7 +57,7 @@ const getFormCompetent = ( */ const getFromProps = (isForm: boolean, searchConfig: any, name: string) => { if (!isForm && name === 'LightFilter') { - // 传给 lightFilter 的问题 + // 传给轻量筛选表单的配置 return omit( { ...searchConfig, @@ -143,6 +143,14 @@ const FormRender = ({ const { getPrefixCls } = useContext(ConfigProvider.ConfigContext); + const className = getPrefixCls('pro-table-search'); + const formClassName = getPrefixCls('pro-table-form'); + + const competentName = useMemo( + () => getFormCompetent(isForm, searchConfig), + [searchConfig, isForm], + ); + const columnsList = useMemo(() => { return columns .filter((item) => { @@ -174,20 +182,15 @@ const FormRender = ({ : {}), valueType: finalValueType, proFieldProps: { + ...(competentName === 'LightFilter' && item.proFieldProps?.light === undefined + ? { light: true } + : {}), ...item.proFieldProps, proFieldKey: columnKey ? `table-field-${columnKey}` : undefined, }, }; }); - }, [columns, type]); - - const className = getPrefixCls('pro-table-search'); - const formClassName = getPrefixCls('pro-table-form'); - - const competentName = useMemo( - () => getFormCompetent(isForm, searchConfig), - [searchConfig, isForm], - ); + }, [columns, type, competentName]); // 传给每个表单的配置,理论上大家都需要 const loadingProps: any = useMemo( diff --git a/src/utils/components/DropdownFooter/index.tsx b/src/utils/components/DropdownFooter/index.tsx index 65576f3d9257..3e8622167c3b 100644 --- a/src/utils/components/DropdownFooter/index.tsx +++ b/src/utils/components/DropdownFooter/index.tsx @@ -4,12 +4,7 @@ import React, { useContext } from 'react'; import { useIntl } from '../../../provider'; import { useStyle } from './style'; -type LightFilterFooterRender = - | (( - onConfirm?: (e?: React.MouseEvent) => void, - onClear?: (e?: React.MouseEvent) => void, - ) => React.JSX.Element | false) - | false; +import type { LightFilterFooterRender } from '../../../form/typing'; type OnClick = (e?: React.MouseEvent) => void; diff --git a/tests/form/base.test.tsx b/tests/form/base.test.tsx index 8c5941e90b44..41274052f1f9 100644 --- a/tests/form/base.test.tsx +++ b/tests/form/base.test.tsx @@ -1872,7 +1872,7 @@ describe('ProForm', () => { const onRequest = vi.fn(); const wrapper = render( - { const onRequest = vi.fn(); const wrapper = render( - { cleanup(); }); -describe('✔️ ProFormLightFilter', () => { +describe('✔️ LightFilter', () => { afterEach(() => { cleanup(); }); it(' ✔️ clear input values', async () => { const html = render( - { it(' ✔️ lightFilter resize', async () => { const html = render( - { it(' ✔️ lightFilter ProFormCascader support label', async () => { const html = render( - [ { value: 'zhejiang', diff --git a/tests/form/lightFilter.test.tsx b/tests/form/lightFilter.test.tsx index e9c81554e892..7e973e15041e 100644 --- a/tests/form/lightFilter.test.tsx +++ b/tests/form/lightFilter.test.tsx @@ -1,18 +1,4 @@ -import { - LightFilter, - ProFormDatePicker, - ProFormDateQuarterRangePicker, - ProFormDateRangePicker, - ProFormDateTimePicker, - ProFormDateTimeRangePicker, - ProFormDateWeekRangePicker, - ProFormDateYearRangePicker, - ProFormDigitRange, - ProFormSelect, - ProFormSlider, - ProFormText, - ProFormTimePicker, -} from '@ant-design/pro-components'; +import { LightFilter, ProFormText } from '@ant-design/pro-components'; import { fireEvent, render, waitFor } from '@testing-library/react'; import dayjs from 'dayjs'; import advancedFormat from 'dayjs/plugin/advancedFormat'; @@ -24,13 +10,28 @@ dayjs.extend(advancedFormat); dayjs.extend(weekOfYear); describe('LightFilter', () => { - it(' 🪕 should render basic structure', async () => { + it(' 🪕 should not use light field label until using LightFilter field helpers', async () => { const { container } = render( , ); + await waitFor(() => { + expect( + container.querySelector('.ant-pro-form-light-filter'), + ).toBeTruthy(); + }); + expect(container.querySelector('.ant-pro-core-field-label')).toBeFalsy(); + }); + + it(' 🪕 should render basic structure', async () => { + const { container } = render( + + + , + ); + await waitFor(() => { expect( container.querySelector('.ant-pro-form-light-filter'), @@ -55,7 +56,7 @@ describe('LightFilter', () => { }} onValuesChange={onValuesChange} > - + , ); @@ -78,7 +79,7 @@ describe('LightFilter', () => { const { container } = render( - + , ); @@ -100,7 +101,7 @@ describe('LightFilter', () => { const { container } = render( - + , ); @@ -121,7 +122,7 @@ describe('LightFilter', () => { const { container } = render( - { const { container } = render( - + , ); @@ -174,7 +175,7 @@ describe('LightFilter', () => { const { container } = render( - + , ); @@ -197,7 +198,7 @@ describe('LightFilter', () => { const { container } = render( - + , ); @@ -220,7 +221,7 @@ describe('LightFilter', () => { const { container } = render( - + , ); @@ -243,7 +244,7 @@ describe('LightFilter', () => { const { container } = render( - + , ); @@ -266,8 +267,8 @@ describe('LightFilter', () => { const { container } = render( - - + + , ); @@ -297,8 +298,8 @@ describe('LightFilter', () => { collapse collapseLabel="更多筛选" > - - + + , ); @@ -321,8 +322,8 @@ describe('LightFilter', () => { const { container } = render( - - + + , ); @@ -342,7 +343,7 @@ describe('LightFilter', () => { const { container } = render( - + , ); @@ -373,11 +374,11 @@ describe('LightFilter', () => { yearRange: [dayjs('2022-01-01'), dayjs('2023-01-01')], }} > - - - - - + + + + + , ); @@ -423,7 +424,7 @@ describe('LightFilter', () => { digitRange: [12, 34], }} > - { const { container } = render( - + , ); @@ -483,8 +484,8 @@ describe('LightFilter', () => { footerRender={footerRender} collapse > - - + + , ); @@ -513,8 +514,8 @@ describe('LightFilter', () => { classNames: { root: 'my-lightfilter-popover' }, }} > - - + + , ); @@ -542,7 +543,7 @@ describe('LightFilter', () => { it(' 🪕 should default to borderless variant', async () => { const { container } = render( - + , ); @@ -567,7 +568,7 @@ describe('LightFilter', () => { it(' 🪕 should support outlined variant', async () => { const { container } = render( - + , );