fix: use appends param to load association data (#3282)

* fix: use appends param to load association data

* chore: update yarn.lock

* test: add test

* test: remove the 'BUG:' text

* test: fix 'window is not defined'

* test: increase timeout
This commit is contained in:
Zeke Zhang 2023-12-29 11:34:27 +08:00 committed by GitHub
parent 0c12fbce29
commit c6915c69f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1641 additions and 26 deletions

View File

@ -305,7 +305,8 @@ export const BlockProvider = (props: {
}) => {
const { collection, association, name } = props;
const resource = useResource(props);
const { appends, updateAssociationValues } = useAssociationNames();
const { getAssociationAppends } = useAssociationNames();
const { appends, updateAssociationValues } = getAssociationAppends();
const params = useMemo(() => {
if (!props.params?.['appends']) {
return { ...props.params, appends };

View File

@ -1295,11 +1295,11 @@ export function getAssociationPath(str) {
}
export const useAssociationNames = () => {
let updateAssociationValues = new Set([]);
let appends = new Set([]);
const { getCollectionJoinField, getCollection } = useCollectionManager();
const fieldSchema = useFieldSchema();
const updateAssociationValues = new Set([]);
const appends = new Set([]);
const getAssociationAppends = (schema, str) => {
const _getAssociationAppends = (schema, str) => {
schema.reduceProperties((pre, s) => {
const prefix = pre || str;
const collectionField = s['x-collection-field'] && getCollectionJoinField(s['x-collection-field']);
@ -1338,7 +1338,7 @@ export const useAssociationNames = () => {
if (['Nester', 'SubTable', 'PopoverNester'].includes(s['x-component-props']?.mode)) {
updateAssociationValues.add(path);
const bufPrefix = prefix && prefix !== '' ? prefix + '.' + s.name : s.name;
getAssociationAppends(s, bufPrefix);
_getAssociationAppends(s, bufPrefix);
}
} else if (
![
@ -1354,12 +1354,18 @@ export const useAssociationNames = () => {
'TableField',
].includes(s['x-component'])
) {
getAssociationAppends(s, str);
_getAssociationAppends(s, str);
}
}, str);
};
getAssociationAppends(fieldSchema, '');
return { appends: [...appends], updateAssociationValues: [...updateAssociationValues] };
const getAssociationAppends = () => {
updateAssociationValues = new Set([]);
appends = new Set([]);
_getAssociationAppends(fieldSchema, '');
return { appends: [...appends], updateAssociationValues: [...updateAssociationValues] };
};
return { getAssociationAppends };
};
function getTargetField(obj) {

View File

@ -6,7 +6,7 @@ import {
oneTableSubtableWithMultiLevelAssociationFields,
test,
} from '@nocobase/test/e2e';
import { T2200, T2614, T2615 } from './templatesOfBug';
import { T2200, T2614, T2615, T2845 } from './templatesOfBug';
test.describe('display association fields', () => {
test('form: should display correctly', async ({ page, mockPage, mockRecord }) => {
@ -50,7 +50,7 @@ test.describe('display association fields', () => {
});
// https://nocobase.height.app/T-2615
test('BUG: should load association data', async ({ page, mockPage, mockRecord }) => {
test('should load association data', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2615).waitForInit();
await mockRecord('T2615');
await nocoPage.goto();
@ -87,8 +87,57 @@ test.describe('display association fields', () => {
);
});
// https://nocobase.height.app/T-2845
test('should load association data of subform', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2845).waitForInit();
// 和 T2615 使用一样的数据表结构
const record = await mockRecord('T2615');
await nocoPage.goto();
// 1. 新增表单中应该显示关系字段的数据
await page.getByRole('button', { name: 'Add new' }).click();
await page
.getByLabel('block-item-CollectionField-T2615-form-T2615.m2o-m2o')
.getByTestId('select-object-single')
.click();
await page.getByRole('option', { name: String(record.m2o.id) }).click();
await expect(page.getByLabel('block-item-CollectionField-T2615Target2-form-T2615Target2.id-ID')).toHaveText(
`ID:${record.m2o.m2oOfTarget1.id}`,
);
await expect(page.getByLabel('block-item-CollectionField-T2615Target2-form-T2615Target2.m2oOfTarget2-')).toHaveText(
`m2oOfTarget2:${record.m2o.m2oOfTarget1.m2oOfTarget2.id}`,
);
// 关闭弹窗
await page.getByLabel('drawer-Action.Container-T2615-Add record-mask').click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 2. 编辑表单中应该显示关系字段的数据
await page.getByLabel(`action-Action.Link-Edit record-update-T2615-table-${record.id - 1}`).click();
await expect(page.getByLabel('block-item-CollectionField-T2615Target2-form-T2615Target2.id-ID')).toHaveText(
`ID:${record.m2o.m2oOfTarget1.id}`,
);
await expect(page.getByLabel('block-item-CollectionField-T2615Target2-form-T2615Target2.m2oOfTarget2-')).toHaveText(
`m2oOfTarget2:${record.m2o.m2oOfTarget1.m2oOfTarget2.id}`,
);
await page.getByLabel('drawer-Action.Container-T2615-Edit record-mask').click();
// 3. 详情中应该显示关系字段的数据
await page.getByLabel(`action-Action.Link-View record-view-T2615-table-${record.id - 1}`).click();
await expect(page.getByLabel('block-item-CollectionField-T2615-form-T2615.m2o-m2o')).toHaveText(
`m2o:${record.m2o.id}`,
);
await expect(page.getByLabel('block-item-CollectionField-T2615Target2-form-T2615Target2.id-ID')).toHaveText(
`ID:${record.m2o.m2oOfTarget1.id}`,
);
await expect(page.getByLabel('block-item-CollectionField-T2615Target2-form-T2615Target2.m2oOfTarget2-')).toHaveText(
`m2oOfTarget2:${record.m2o.m2oOfTarget1.m2oOfTarget2.id}`,
);
});
// https://nocobase.height.app/T-2614
test('BUG: should load association data in subform', async ({ page, mockPage, mockRecord }) => {
test('should load association data in subform', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2614).waitForInit();
await mockRecord('T2614');
await nocoPage.goto();
@ -198,7 +247,7 @@ test.describe('association fields', () => {
});
// fix https://nocobase.height.app/T-2200
test('BUG: should be possible to change the value of the association field normally', async ({ page, mockPage }) => {
test('should be possible to change the value of the association field normally', async ({ page, mockPage }) => {
await mockPage(T2200).goto();
await page.getByLabel('action-Action.Link-Edit-update-users-table-0').click();

View File

@ -212,7 +212,7 @@ test.describe('creation form block schema settings', () => {
});
// fix https://nocobase.height.app/T-2165
test('BUG: variable labels should be displayed normally', async ({ page, mockPage }) => {
test('variable labels should be displayed normally', async ({ page, mockPage }) => {
await mockPage(T2165).goto();
await page.getByLabel('block-item-CardItem-users-form').hover();
@ -628,7 +628,7 @@ test.describe('creation form block schema settings', () => {
});
// fix https://nocobase.height.app/T-2174
test('BUG: should show default value option', async ({ page, mockPage, mockRecord }) => {
test('should show default value option', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2174).waitForInit();
await mockRecord('test2174');
await nocoPage.goto();

View File

@ -2,7 +2,7 @@ import { expect, test } from '@nocobase/test/e2e';
import { T2187 } from '../templatesOfBug';
// fix https://nocobase.height.app/T-2187
test('BUG: in the Duplicate mode, the Roles field should not have a value after clicking it because it is not selected', async ({
test('in the Duplicate mode, the Roles field should not have a value after clicking it because it is not selected', async ({
page,
mockPage,
}) => {

View File

@ -2,7 +2,7 @@ import { expect, test } from '@nocobase/test/e2e';
import { T2183, T2186 } from '../templatesOfBug';
// fix https://nocobase.height.app/T-2183
test('BUG: should save conditions', async ({ page, mockPage }) => {
test('should save conditions', async ({ page, mockPage }) => {
await mockPage(T2183).goto();
await page.getByLabel('action-Filter.Action-Filter-filter-users-table').click();
await page.getByText('Add condition', { exact: true }).click();
@ -19,7 +19,7 @@ test('BUG: should save conditions', async ({ page, mockPage }) => {
});
// fix https://nocobase.height.app/T-2186
test('BUG: the input box displayed should correspond to the field type', async ({ page, mockPage }) => {
test('the input box displayed should correspond to the field type', async ({ page, mockPage }) => {
await mockPage(T2186).goto();
await page.getByLabel('action-Filter.Action-Filter-filter-users-table').click();

View File

@ -3,6 +3,7 @@ import { useField, useFieldSchema, useForm } from '@formily/react';
import { nextTick } from '@nocobase/utils/client';
import _ from 'lodash';
import { useEffect, useRef } from 'react';
import { useAssociationNames } from '../../../../block-provider';
import { useCollection, useCollectionManager } from '../../../../collection-manager';
import { useFlag } from '../../../../flag-provider';
import { useVariables } from '../../../../variables';
@ -25,6 +26,7 @@ const useLazyLoadDisplayAssociationFieldsOfForm = () => {
const field = useField<Field>();
const { formValue: subFormValue } = useSubFormValue();
const { isInSubForm, isInSubTable } = useFlag() || {};
const { getAssociationAppends } = useAssociationNames();
const schemaName = fieldSchema.name.toString();
const formValue = _.cloneDeep(isInSubForm || isInSubTable ? subFormValue : form.values);
@ -66,8 +68,10 @@ const useLazyLoadDisplayAssociationFieldsOfForm = () => {
return;
}
const { appends } = getAssociationAppends();
variables
.parseVariable(variableString, formVariable)
.parseVariable(variableString, formVariable, { appends })
.then((value) => {
nextTick(() => {
const result = transformVariableValue(value, { targetCollectionField: collectionFieldRef.current });

View File

@ -51,7 +51,14 @@ const VariablesProvider = ({ children }) => {
* 3. `key` `key`
*/
const getValue = useCallback(
async (variablePath: string, localVariables?: VariableOption[]) => {
async (
variablePath: string,
localVariables?: VariableOption[],
options?: {
/** 第一次请求时,需要包含的关系字段 */
appends?: string[];
},
) => {
const list = variablePath.split('.');
const variableName = list[0];
const _variableToCollectionName = mergeVariableToCollectionNameWithLocalVariables(
@ -85,6 +92,9 @@ const VariablesProvider = ({ children }) => {
const result = api
.request({
url,
params: {
appends: options?.appends,
},
})
.then((data) => {
clearRequested(url);
@ -106,6 +116,9 @@ const VariablesProvider = ({ children }) => {
} else {
const waitForData = api.request({
url,
params: {
appends: options?.appends,
},
});
stashRequested(url, waitForData);
data = await waitForData;
@ -180,7 +193,14 @@ const VariablesProvider = ({ children }) => {
* @param localVariables
* @returns
*/
async (str: string, localVariables?: VariableOption | VariableOption[]) => {
async (
str: string,
localVariables?: VariableOption | VariableOption[],
options?: {
/** 第一次请求时,需要包含的关系字段 */
appends?: string[];
},
) => {
if (!isVariable(str)) {
return str;
}
@ -190,7 +210,7 @@ const VariablesProvider = ({ children }) => {
}
const path = getPath(str);
const value = await getValue(path, localVariables as VariableOption[]);
const value = await getValue(path, localVariables as VariableOption[], options);
return uniq(filterEmptyValues(value));
},

View File

@ -29,7 +29,14 @@ export interface VariablesContextType {
* console.log(value); // test
* ```
*/
parseVariable: (str: string, localVariable?: VariableOption | VariableOption[]) => Promise<any>;
parseVariable: (
str: string,
localVariable?: VariableOption | VariableOption[],
options?: {
/** 第一次请求时,需要包含的关系字段 */
appends?: string[];
},
) => Promise<any>;
/**
*
* @param variableOption

View File

@ -34,6 +34,10 @@ Object.defineProperty(window, 'matchMedia', {
const { getComputedStyle } = window;
window.getComputedStyle = (elt) => getComputedStyle(elt);
// 解决 https://github.com/nocobase/nocobase/actions/runs/7353181446/job/20018831007?pr=3282
// 该错误是发生在测试环境之后的,应该是存在异步代码没有 await 导致的,但是不知道是哪里的问题,所以先这样处理
global.window = window;
/**
* TypeError: range.getBoundingClientRect is not a function
* https://github.com/jsdom/jsdom/issues/3002

View File

@ -71,6 +71,7 @@ test.describe('form item & create form', () => {
});
test('pattern', async ({ page, mockPage }) => {
test.slow();
await testPattern({
page,
gotoPage: () =>

View File

@ -1,4 +1,3 @@
import React, { useRef, useMemo, useContext } from 'react';
import { createForm } from '@formily/core';
import { useField } from '@formily/react';
import {
@ -9,8 +8,9 @@ import {
useAPIClient,
useAssociationNames,
} from '@nocobase/client';
import { parse } from '@nocobase/utils/client';
import { useFlowContext } from '@nocobase/plugin-workflow/client';
import { parse } from '@nocobase/utils/client';
import React, { useContext, useMemo, useRef } from 'react';
function useFlowContextData(dataSource) {
const { execution, nodes } = useFlowContext();
@ -37,7 +37,8 @@ function useFlowContextData(dataSource) {
export function DetailsBlockProvider(props) {
const field = useField();
const formBlockRef = useRef(null);
const { appends, updateAssociationValues } = useAssociationNames();
const { getAssociationAppends } = useAssociationNames();
const { appends, updateAssociationValues } = getAssociationAppends();
const values = useFlowContextData(props.dataSource);
const form = useMemo(

View File

@ -19,7 +19,8 @@ export function FormBlockProvider(props) {
const fieldSchema = useFieldSchema();
const field = useField();
const formBlockRef = useRef(null);
const { appends, updateAssociationValues } = useAssociationNames();
const { getAssociationAppends } = useAssociationNames();
const { appends, updateAssociationValues } = getAssociationAppends();
const [formKey] = Object.keys(fieldSchema.toJSON().properties ?? {});
const values = userJob?.result?.[formKey];

View File

@ -4215,6 +4215,11 @@
dependencies:
eslint-scope "5.1.1"
"@nocobase/plugin-workflow-test@>=0.17.0-alpha.3":
version "0.17.0-alpha.7"
resolved "https://registry.yarnpkg.com/@nocobase/plugin-workflow-test/-/plugin-workflow-test-0.17.0-alpha.7.tgz#219a3a1e91e51bec08b1adbb1c4c5c9c7184ecce"
integrity sha512-krZlo1xDM66spbQG6jAYSraxazuxCuA2rD9TxptFQXjkAMTGkuDilJ+edMj6p866N3nyzvFh8lZ2/XY1HUC7Xw==
"@node-saml/node-saml@^4.0.2":
version "4.0.5"
resolved "https://registry.npmmirror.com/@node-saml/node-saml/-/node-saml-4.0.5.tgz#039e387095b54639b06df62b1b4a6d8941c6d907"