mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 05:46:00 +00:00
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:
parent
1e5e015b73
commit
2014729e01
@ -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 };
|
||||||
|
@ -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('');
|
||||||
|
@ -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) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user