Skip to content

Commit

Permalink
fix(filter-form): fix auto-trigger filter action without default value (
Browse files Browse the repository at this point in the history
nocobase#4627)

* test: add e2e test

* test: add unit tests

* fix(reset-button): prevent data clear when default values exist

* fix: resolve known issues and add tests
  • Loading branch information
zhangzhonghe authored Jun 12, 2024
1 parent 5d48b3b commit e684885
Show file tree
Hide file tree
Showing 9 changed files with 1,060 additions and 58 deletions.
124 changes: 80 additions & 44 deletions packages/core/client/src/block-provider/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
import { useAPIClient, useRequest } from '../../api-client';
import { useFormBlockContext } from '../../block-provider/FormBlockProvider';
import { useCollectionManager_deprecated, useCollection_deprecated } from '../../collection-manager';
import { useFilterBlock } from '../../filter-provider/FilterProvider';
import { DataBlock, useFilterBlock } from '../../filter-provider/FilterProvider';
import { mergeFilter, transformToFilter } from '../../filter-provider/utils';
import { useTreeParentRecord } from '../../modules/blocks/data-blocks/table/TreeRecordProvider';
import { useRecord } from '../../record-provider';
Expand Down Expand Up @@ -430,6 +430,10 @@ const useDoFilter = () => {
const { name } = useCollection();
const { targets = [], uid } = useMemo(() => findFilterTargets(fieldSchema), [fieldSchema]);

const getFilterFromCurrentForm = useCallback(() => {
return removeNullCondition(transformToFilter(form.values, getOperators(), getCollectionJoinField, name));
}, [form.values, getCollectionJoinField, getOperators, name]);

const doFilter = useCallback(
async ({ doNothingWhenFilterIsEmpty = false } = {}) => {
try {
Expand All @@ -443,20 +447,19 @@ const useDoFilter = () => {
// 保留原有的 filter
const storedFilter = block.service.params?.[1]?.filters || {};

storedFilter[uid] = removeNullCondition(
transformToFilter(form.values, getOperators(), getCollectionJoinField, name),
);
// 由当前表单转换而来的 filter
storedFilter[uid] = getFilterFromCurrentForm();

const mergedFilter = mergeFilter([
...Object.values(storedFilter).map((filter) => removeNullCondition(filter)),
block.defaultFilter,
]);

if (doNothingWhenFilterIsEmpty && _.isEmpty(mergedFilter)) {
if (doNothingWhenFilterIsEmpty && _.isEmpty(storedFilter[uid])) {
return;
}

if (block.dataLoadingMode === 'manual' && _.isEmpty(mergedFilter)) {
if (block.dataLoadingMode === 'manual' && _.isEmpty(storedFilter[uid])) {
return block.clearData();
}

Expand All @@ -474,7 +477,7 @@ const useDoFilter = () => {
console.error(error);
}
},
[form.values, getCollectionJoinField, getDataBlocks, getOperators, name, targets, uid],
[getDataBlocks, getFilterFromCurrentForm, targets, uid],
);

// 这里的代码是为了实现:筛选表单的筛选操作在首次渲染时自动执行一次
Expand All @@ -487,6 +490,10 @@ const useDoFilter = () => {
* 用于执行筛选表单的筛选操作
*/
doFilter,
/**
* 根据当前表单的值获取 filter
*/
getFilterFromCurrentForm,
};
};

Expand All @@ -504,52 +511,35 @@ export const useFilterBlockActionProps = () => {
};
};

export const useResetBlockActionProps = () => {
const useDoReset = () => {
const form = useForm();
const actionField = useField();
const fieldSchema = useFieldSchema();
const { getDataBlocks } = useFilterBlock();
const { targets, uid } = findFilterTargets(fieldSchema);
const { doFilter, getFilterFromCurrentForm } = useDoFilter();

return {
doReset: async () => {
await form.reset();
if (_.isEmpty(getFilterFromCurrentForm())) {
return doReset({ getDataBlocks, targets, uid });
}
await doFilter();
},
};
};

export const useResetBlockActionProps = () => {
const actionField = useField();
const { doReset } = useDoReset();

actionField.data = actionField.data || {};

return {
async onClick() {
const { targets, uid } = findFilterTargets(fieldSchema);

form.reset();
actionField.data.loading = true;
try {
// 收集 filter 的值
await Promise.all(
getDataBlocks().map(async (block) => {
const target = targets.find((target) => target.uid === block.uid);
if (!target) return;

if (block.dataLoadingMode === 'manual') {
return block.clearData();
}

const param = block.service.params?.[0] || {};
// 保留原有的 filter
const storedFilter = block.service.params?.[1]?.filters || {};

delete storedFilter[uid];
const mergedFilter = mergeFilter([...Object.values(storedFilter), block.defaultFilter]);

return block.doFilter(
{
...param,
page: 1,
filter: mergedFilter,
},
{ filters: storedFilter },
);
}),
);
actionField.data.loading = false;
} catch (error) {
actionField.data.loading = false;
}
await doReset();
actionField.data.loading = false;
},
};
};
Expand Down Expand Up @@ -1347,6 +1337,52 @@ export const useAssociationFilterBlockProps = () => {
labelKey,
};
};
async function doReset({
getDataBlocks,
targets,
uid,
}: {
getDataBlocks: () => DataBlock[];
targets: {
/** field uid */
uid: string;
/** associated field */
field?: string;
}[];
uid: string;
}) {
try {
await Promise.all(
getDataBlocks().map(async (block) => {
const target = targets.find((target) => target.uid === block.uid);
if (!target) return;

if (block.dataLoadingMode === 'manual') {
return block.clearData();
}

const param = block.service.params?.[0] || {};
// 保留原有的 filter
const storedFilter = block.service.params?.[1]?.filters || {};

delete storedFilter[uid];
const mergedFilter = mergeFilter([...Object.values(storedFilter), block.defaultFilter]);

return block.doFilter(
{
...param,
page: 1,
filter: mergedFilter,
},
{ filters: storedFilter },
);
}),
);
} catch (error) {
console.error(error);
}
}

export function getAssociationPath(str) {
const lastIndex = str.lastIndexOf('.');
if (lastIndex !== -1) {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/client/src/data-source/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
*/

import {
Application,
CollectionFieldInterface,
DEFAULT_DATA_SOURCE_KEY,
isTitleField,
Application,
useDataSourceHeaders,
DEFAULT_DATA_SOURCE_KEY,
} from '@nocobase/client';
import { renderHook } from '@nocobase/test/client';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/

import { getSupportFieldsByAssociation, getSupportFieldsByForeignKey } from '../utils';
import { getSupportFieldsByAssociation, getSupportFieldsByForeignKey, transformToFilter } from '../utils';

describe('getSupportFieldsByAssociation', () => {
it('should return all associated fields matching the inherited collections chain', () => {
Expand Down Expand Up @@ -140,3 +140,80 @@ describe('getSupportFieldsByForeignKey', () => {
]);
});
});

describe('transformToFilter', () => {
const values = {
field1: 'value1',
field2: 'value2',
field3: [
{
id: 'value3',
},
{
id: 'value4',
},
],
};

const operators = {
field1: '$eq',
field2: '$ne',
field3: '$in',
};

const collectionName = 'collection';

const getCollectionJoinField = vi.fn((name: string) => {
if (name === `${collectionName}.field1`) return {};
if (name === `${collectionName}.field2`) return {};
if (name === `${collectionName}.field3`) return { target: 'targetCollection', targetKey: 'id' };
return {};
});

it('should transform values to filter', () => {
const expectedFilter = {
$and: [
{
field1: {
$eq: 'value1',
},
},
{
field2: {
$ne: 'value2',
},
},
{
'field3.id': {
$eq: ['value3', 'value4'],
},
},
],
};

const filter = transformToFilter(values, operators, getCollectionJoinField, collectionName);

expect(filter).toEqual(expectedFilter);
});

it('should handle null values', () => {
const valuesWithNull = {
field1: null,
field2: 'value2',
};

const expectedFilter = {
$and: [
{
field2: {
$ne: 'value2',
},
},
],
};

const filter = transformToFilter(valuesWithNull, operators, getCollectionJoinField, collectionName);

expect(filter).toEqual(expectedFilter);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

import { expect, test } from '@nocobase/test/e2e';
import { tableListDetailsGridCardWithUsers } from './templatesOfBug';
import { TableBlockWithDataScope, tableListDetailsGridCardWithUsers } from './templatesOfBug';

test.describe('setDataLoadingModeSettingsItem', () => {
test('basic', async ({ page, mockPage }) => {
Expand Down Expand Up @@ -72,4 +72,30 @@ test.describe('setDataLoadingModeSettingsItem', () => {
await expect(page.getByLabel('block-item-CardItem-users-list').getByText('No data')).toBeVisible();
await expect(page.getByLabel('block-item-BlockItem-users-').getByText('No data')).toBeVisible();
});

test('When the data block has data scope settings and dataLoadingMode is manual, data should not be displayed after the first page load', async ({
page,
mockPage,
}) => {
await mockPage(TableBlockWithDataScope).goto();
await expect(page.getByLabel('block-item-CardItem-users-table').getByText('No data')).toBeVisible();

// 此时点击 filter 按钮,应该还是没数据,因为表单没有值
await page.getByLabel('action-Action-Filter-submit-').click({
position: {
x: 10,
y: 10,
},
});
await expect(page.getByLabel('block-item-CardItem-users-table').getByText('No data')).toBeVisible();

// 点击 Reset 按钮,也是一样
await page.getByLabel('action-Action-Reset-users-').click({
position: {
x: 10,
y: 10,
},
});
await expect(page.getByLabel('block-item-CardItem-users-table').getByText('No data')).toBeVisible();
});
});
Loading

0 comments on commit e684885

Please sign in to comment.