fix: collection field support x-use-component-props (#4264)

* fix: collection field support x-use-component-props

* fix: bug

* fix: bug
This commit is contained in:
jack zhang 2024-05-09 09:21:58 +08:00 committed by GitHub
parent 1e5e015b73
commit 2014729e01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 80 additions and 36 deletions

View File

@ -12,7 +12,47 @@ import React, { ComponentType, useMemo } from 'react';
import { useDesignable } from '../../schema-component'; import { useDesignable } from '../../schema-component';
import _ from 'lodash'; import _ from 'lodash';
const useDefaultSchemaProps = () => undefined; const useDefaultDynamicComponentProps = () => undefined;
const getHook = (str: string, scope: Record<string, any>, allText: string) => {
let res = undefined;
if (_.isFunction(str)) {
res = str;
} else {
res = scope[str];
if (!res) {
console.error(`${allText} is not registered`);
}
}
return res || useDefaultDynamicComponentProps;
};
export function useDynamicComponentProps(useComponentPropsStr?: string, props?: any) {
const scope = useExpressionScope();
const useDynamicProps = useMemo(() => {
if (!useComponentPropsStr) {
return useDefaultDynamicComponentProps;
}
if (_.isFunction(useComponentPropsStr)) {
return useComponentPropsStr;
}
const pathList = useComponentPropsStr.split('.');
let result;
for (const item of pathList) {
result = getHook(item, result || scope, useComponentPropsStr);
}
return result;
}, [useComponentPropsStr]);
const res = useDynamicProps(props);
return res;
}
interface WithSchemaHookOptions { interface WithSchemaHookOptions {
displayName?: string; displayName?: string;
@ -25,7 +65,6 @@ export function withDynamicSchemaProps<T = any>(
const displayName = options.displayName || Component.displayName || Component.name; const displayName = options.displayName || Component.displayName || Component.name;
const ComponentWithProps: ComponentType<T> = (props) => { const ComponentWithProps: ComponentType<T> = (props) => {
const { dn, findComponent } = useDesignable(); const { dn, findComponent } = useDesignable();
const scope = useExpressionScope();
const useComponentPropsStr = useMemo(() => { const useComponentPropsStr = useMemo(() => {
const xComponent = dn.getSchemaAttribute('x-component'); const xComponent = dn.getSchemaAttribute('x-component');
const xDecorator = dn.getSchemaAttribute('x-decorator'); const xDecorator = dn.getSchemaAttribute('x-decorator');
@ -40,38 +79,7 @@ export function withDynamicSchemaProps<T = any>(
return xUseDecoratorProps; return xUseDecoratorProps;
} }
}, [dn]); }, [dn]);
const useSchemaProps = useMemo(() => { const schemaProps = useDynamicComponentProps(useComponentPropsStr, props);
const getHook = (str: string, scope: Record<string, any>, allText: string) => {
let res = undefined;
if (_.isFunction(str)) {
res = str;
} else {
res = scope[str];
if (!res) {
console.error(`${allText} is not registered`);
}
}
return res || useDefaultSchemaProps;
};
if (!useComponentPropsStr) {
return useDefaultSchemaProps;
}
if (_.isFunction(useComponentPropsStr)) {
return useComponentPropsStr;
}
const pathList = useComponentPropsStr.split('.');
let result;
for (const item of pathList) {
result = getHook(item, result || scope, useComponentPropsStr);
}
return result;
}, [scope, useComponentPropsStr]);
const schemaProps = useSchemaProps(props);
const memoProps = useMemo(() => { const memoProps = useMemo(() => {
return { ...props, ...schemaProps }; return { ...props, ...schemaProps };

View File

@ -34,8 +34,34 @@ function renderApp(fieldName: string, components = {}) {
reverseKey: null, reverseKey: null,
}; };
const useCustomDynamicProps = () => {
return {
addonBefore: 'addonBefore',
};
};
const dynamicPropsSchema = {
key: 'dynamic-props',
name: 'dynamic-props',
type: 'string',
interface: 'select',
description: null,
collectionName: 't_vwpds9fs4xs',
parentKey: null,
reverseKey: null,
uiSchema: {
type: 'string',
'x-component': 'Input',
'x-component-props': {
placeholder: 'placeholder',
},
'x-use-component-props': 'useCustomDynamicProps',
},
};
const usersCollection: any = collections[0]; const usersCollection: any = collections[0];
usersCollection.fields.push(noUiSchema); usersCollection.fields.push(noUiSchema);
usersCollection.fields.push(dynamicPropsSchema);
const app = new Application({ const app = new Application({
dataSourceManager: { dataSourceManager: {
@ -61,6 +87,7 @@ function renderApp(fieldName: string, components = {}) {
<CollectionProvider name="users"> <CollectionProvider name="users">
<SchemaComponent <SchemaComponent
schema={schema} schema={schema}
scope={{ useCustomDynamicProps }}
components={{ CollectionField: CollectionField, FormItem, Input, ...components }} components={{ CollectionField: CollectionField, FormItem, Input, ...components }}
/> />
</CollectionProvider> </CollectionProvider>
@ -86,6 +113,12 @@ describe('CollectionField', () => {
expect(document.querySelector('.input-test-1')).toHaveTextContent('nickname'); expect(document.querySelector('.input-test-1')).toHaveTextContent('nickname');
}); });
it('useComponentProps', () => {
renderApp('dynamic-props');
expect(document.querySelector('.ant-input')).toHaveAttribute('placeholder', 'placeholder');
expect(screen.queryByText('addonBefore')).toBeInTheDocument();
});
it('no schema', () => { it('no schema', () => {
renderApp('no-ui-schema'); renderApp('no-ui-schema');
expect(document.querySelector('.ant-formily-item-control-content-component')).toHaveTextContent(''); expect(document.querySelector('.ant-formily-item-control-content-component')).toHaveTextContent('');

View File

@ -16,6 +16,7 @@ import { useFormBlockContext } from '../../block-provider/FormBlockProvider';
import { useCompile, useComponent } from '../../schema-component'; import { useCompile, useComponent } from '../../schema-component';
import { useIsAllowToSetDefaultValue } from '../../schema-settings/hooks/useIsAllowToSetDefaultValue'; import { useIsAllowToSetDefaultValue } from '../../schema-settings/hooks/useIsAllowToSetDefaultValue';
import { CollectionFieldProvider, useCollectionField } from './CollectionFieldProvider'; import { CollectionFieldProvider, useCollectionField } from './CollectionFieldProvider';
import { useDynamicComponentProps } from '../../application/hoc';
type Props = { type Props = {
component: any; component: any;
@ -49,6 +50,8 @@ export const CollectionFieldInternalField: React.FC = (props: Props) => {
}, [fieldSchema, uiSchema]); }, [fieldSchema, uiSchema]);
const ctx = useFormBlockContext(); const ctx = useFormBlockContext();
const dynamicProps = useDynamicComponentProps(uiSchema?.['x-use-component-props'], props);
useEffect(() => { useEffect(() => {
if (ctx?.field) { if (ctx?.field) {
ctx.field.added = ctx.field.added || new Set(); ctx.field.added = ctx.field.added || new Set();
@ -82,12 +85,12 @@ export const CollectionFieldInternalField: React.FC = (props: Props) => {
// @ts-ignore // @ts-ignore
field.dataSource = uiSchema.enum; field.dataSource = uiSchema.enum;
const originalProps = compile(uiSchema['x-component-props']) || {}; const originalProps = compile(uiSchema['x-component-props']) || {};
field.componentProps = merge(originalProps, field.componentProps || {}); field.componentProps = merge(originalProps, field.componentProps || {}, dynamicProps || {});
}, [uiSchema]); }, [uiSchema]);
if (!uiSchema) return null; if (!uiSchema) return null;
return <Component {...props} />; return <Component {...props} {...dynamicProps} />;
}; };
export const CollectionField = connect((props) => { export const CollectionField = connect((props) => {