Skip to content

Commit

Permalink
feat(client): add new variable named 'URL search params' and support …
Browse files Browse the repository at this point in the history
…link action (#4506)

* feat: support link action

* feat(client): add new variable named 'URL search params'

* chore: add translation

* fix: avoid crashing

* chore: fix failed test

* feat: link action

* feat: link action

* fix: remove filter parameters with undefined values

* feat: link action

* feat: add support for default values in filter form fields

* refactor: code improve

* refactor: locale improve

* refactor: locale improve

* test: add e2e test

* refactor: locale improve

* refactor: locale improve

* fix: resolve operation issues with variables

* refactor: code improve

* chore: enable direct selection of variables as default value

* chore: use qs to parse query string

* fix: menu selectKeys (T-4373)

* refactor: use qs to stringify search params

* refactor: locale improve

* refactor: locale improve

* chore: fix failed tests

* fix: resolve issue where setting Data scope is not work

* chore: fix failed e2e tests

* chore: make e2e tests more stable

* chore: add translation

* chore: make e2e tests more stable

* fix: resolve the issue of error when saving data scope

* feat: trigger variable parsing after context change

* test: add unit tests

* test: add e2e test

* refactor: extract template

* chore: fix failed unit tests

* chore: fix failed e2e test

* fix(Link): hide linkage rules in top link (T-4410)

* fix(permission): remove URL search params variable from data scope

* chore: make more stable

* chore: make e2e test more stable

* fix(Link): reduce size for variable

* fix: clear previous context (T-4449)

* fix(calendar, map): resolve initial data scope setting error (T-4450)

* fix: correct concatenation of query string (T-4453)

---------

Co-authored-by: katherinehhh <katherine_15995@163.com>
Co-authored-by: jack zhang <1098626505@qq.com>
  • Loading branch information
3 people committed Jun 4, 2024
1 parent 0b8f762 commit f66edb5
Show file tree
Hide file tree
Showing 67 changed files with 1,407 additions and 158 deletions.
14 changes: 9 additions & 5 deletions packages/core/client/src/block-provider/DetailsBlockProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,22 +77,26 @@ const InternalDetailsBlockProvider = (props) => {
const useCompatDetailsBlockParams = (props) => {
const fieldSchema = useFieldSchema();

let params;
let params,
parseVariableLoading = false;
// 1. 新版本的 schema 存在 x-use-decorator-props 属性
if (fieldSchema['x-use-decorator-props']) {
params = props?.params;
parseVariableLoading = props?.parseVariableLoading;
} else {
// 2. 旧版本的 schema 不存在 x-use-decorator-props 属性
// 因为 schema 中是否存在 x-use-decorator-props 是固定不变的,所以这里可以使用 hooks
// eslint-disable-next-line react-hooks/rules-of-hooks
params = useDetailsWithPaginationBlockParams(props);
const parsedParams = useDetailsWithPaginationBlockParams(props);
params = parsedParams.params;
parseVariableLoading = parsedParams.parseVariableLoading;
}

return params;
return { params, parseVariableLoading };
};

export const DetailsBlockProvider = withDynamicSchemaProps((props) => {
const params = useCompatDetailsBlockParams(props);
const { params, parseVariableLoading } = useCompatDetailsBlockParams(props);
const record = useCollectionRecordData();
const { association, dataSource } = props;
const { getCollection } = useCollectionManager_deprecated(dataSource);
Expand All @@ -104,7 +108,7 @@ export const DetailsBlockProvider = withDynamicSchemaProps((props) => {
detailFlag = __collection === collection;
}

if (!detailFlag) {
if (!detailFlag || parseVariableLoading) {
return null;
}

Expand Down
21 changes: 15 additions & 6 deletions packages/core/client/src/block-provider/TableBlockProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ const InternalTableBlockProvider = (props: Props) => {
}, [service?.loading, fieldSchema]);

const setExpandFlagValue = useCallback(
(falg) => {
setExpandFlag(falg || !expandFlag);
(flag) => {
setExpandFlag(flag || !expandFlag);
},
[expandFlag],
);
Expand Down Expand Up @@ -106,18 +106,22 @@ const InternalTableBlockProvider = (props: Props) => {
const useTableBlockParamsCompat = (props) => {
const fieldSchema = useFieldSchema();

let params;
let params,
parseVariableLoading = false;
// 1. 新版本的 schema 存在 x-use-decorator-props 属性
if (fieldSchema['x-use-decorator-props']) {
params = props.params;
parseVariableLoading = props.parseVariableLoading;
} else {
// 2. 旧版本的 schema 不存在 x-use-decorator-props 属性
// 因为 schema 中是否存在 x-use-decorator-props 是固定不变的,所以这里可以使用 hooks
// eslint-disable-next-line react-hooks/rules-of-hooks
params = useTableBlockParams(props);
const tableBlockParams = useTableBlockParams(props);
params = tableBlockParams.params;
parseVariableLoading = tableBlockParams.parseVariableLoading;
}

return params;
return { params, parseVariableLoading };
};

export const TableBlockProvider = withDynamicSchemaProps((props) => {
Expand All @@ -127,7 +131,7 @@ export const TableBlockProvider = withDynamicSchemaProps((props) => {
const { getCollection, getCollectionField } = useCollectionManager_deprecated(props.dataSource);
const collection = getCollection(props.collection, props.dataSource);
const { treeTable } = fieldSchema?.['x-decorator-props'] || {};
const params = useTableBlockParamsCompat(props);
const { params, parseVariableLoading } = useTableBlockParamsCompat(props);

let childrenColumnName = 'children';

Expand All @@ -148,6 +152,11 @@ export const TableBlockProvider = withDynamicSchemaProps((props) => {
}
const form = useMemo(() => createForm(), [treeTable]);

// 在解析变量的时候不渲染,避免因为重复请求数据导致的资源浪费
if (parseVariableLoading) {
return null;
}

return (
<SchemaComponentOptions scope={{ treeTable }}>
<FormContext.Provider value={form}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,11 +257,11 @@ export const TableSelectorProvider = withDynamicSchemaProps((props: TableSelecto
console.error(err);
}

const { filter: parsedFilter } = useParsedFilter({
const { filter: parsedFilter, parseVariableLoading } = useParsedFilter({
filterOption: params?.filter,
});

if (!_.isEmpty(params?.filter) && _.isEmpty(parsedFilter)) {
if ((!_.isEmpty(params?.filter) && _.isEmpty(parsedFilter)) || parseVariableLoading) {
return null;
}

Expand Down
152 changes: 152 additions & 0 deletions packages/core/client/src/block-provider/__tests__/hooks/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/

import { appendQueryStringToUrl, parseVariablesAndChangeParamsToQueryString, reduceValueSize } from '../../hooks/index';

describe('parseVariablesAndChangeParamsToQueryString', () => {
it('should parse variables and change params to query string', async () => {
const searchParams = [
{ name: 'param1', value: '{{ $var1.value }}' },
{ name: 'param2', value: 'value2' },
{ name: 'param3', value: 'value3' },
];
const variables: any = {
parseVariable: vi.fn().mockResolvedValue('parsedValue'),
};
const localVariables: any = [
{ name: '$var1', ctx: { value: 'localValue1' } },
{ name: '$var2', ctx: { value: 'localValue2' } },
];
const replaceVariableValue = vi.fn().mockResolvedValue('replacedValue');

const result = await parseVariablesAndChangeParamsToQueryString({
searchParams,
variables,
localVariables,
replaceVariableValue,
});

expect(variables.parseVariable).toHaveBeenCalledTimes(1);
expect(variables.parseVariable).toHaveBeenCalledWith('{{ $var1.value }}', localVariables);
expect(replaceVariableValue).toHaveBeenCalledTimes(2);
expect(replaceVariableValue).toHaveBeenCalledWith('value2', variables, localVariables);
expect(replaceVariableValue).toHaveBeenCalledWith('value3', variables, localVariables);

expect(result).toBe('param1=parsedValue&param2=replacedValue&param3=replacedValue');
});
});

describe('reduceValueSize', () => {
it('should reduce the size of the value', () => {
const value = {
key1: 'value1',
key2: 'value2',
key3: 'value3',
};

const result = reduceValueSize(value);

expect(result).toEqual({
key1: 'value1',
key2: 'value2',
key3: 'value3',
});
});

it('should remove keys with string values longer than 100 characters', () => {
const value = {
key1: 'value1',
key2: 'value2',
key3: 'value3'.repeat(20),
};

const result = reduceValueSize(value);

expect(result).toEqual({
key1: 'value1',
key2: 'value2',
});
});

it('should reduce the size of nested objects', () => {
const value = {
key1: 'value1',
key2: 'value2',
key3: {
nestedKey1: 'nestedValue1',
nestedKey2: 'nestedValue2',
nestedKey3: 'nestedValue3',
},
};

const result = reduceValueSize(value);

expect(result).toEqual({
key1: 'value1',
key2: 'value2',
});
});

it('should reduce the size of nested arrays', () => {
const value = {
key1: 'value1',
key2: 'value2',
key3: ['value1', 'value2', 'value3'],
};

const result = reduceValueSize(value);

expect(result).toEqual({
key1: 'value1',
key2: 'value2',
});
});

it('should reduce the size of arrays', () => {
const value = ['value1', 'value2', 'value3'.repeat(20)];
const result = reduceValueSize(value);
expect(result).toEqual(['value1', 'value2', 'value3'.repeat(20)]);

const value2 = [
'value1',
'value2',
{
key1: 'value1',
key2: 'value2',
key3: {
nestedKey1: 'nestedValue1',
nestedKey2: 'nestedValue2',
nestedKey3: 'nestedValue3',
},
},
];
const result2 = reduceValueSize(value2);
expect(result2).toEqual(['value1', 'value2', { key1: 'value1', key2: 'value2' }]);
});
});

describe('appendQueryStringToUrl', () => {
it('should append query string to the URL', () => {
const url = 'https://example.com';
const queryString = 'param1=value1&param2=value2';

const result = appendQueryStringToUrl(url, queryString);

expect(result).toBe('https://example.com?param1=value1&param2=value2');
});

it('should append query string to the URL with existing query parameters', () => {
const url = 'https://example.com?existingParam=value';
const queryString = 'param1=value1&param2=value2';

const result = appendQueryStringToUrl(url, queryString);

expect(result).toBe('https://example.com?existingParam=value&param1=value1&param2=value2');
});
});
Loading

0 comments on commit f66edb5

Please sign in to comment.