mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-09 15:39:24 +08:00
chore: deprecate the current record variable from the form (#4063)
* chore: remove the current record variable from the form * chore: fix failed e2e * refactor(VariableInput): support for setting the react node for label * feat: support to show tooltip * chore: stash * chore: add translation * chore: add translation * chore: fix expresion * test: add e2e for deprecated variables * refactor: migrate file * chore: make e2e pass * chore: make e2e pass * chore: make e2e pass * chore: adjust * chore: natch * chore: revert match
This commit is contained in:
parent
785cc525c4
commit
fd4c9cb288
@ -194,7 +194,7 @@ export const RenderChildrenWithAssociationFilter: React.FC<any> = (props) => {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const BlockContext = createContext<{
|
||||
export const BlockContext = createContext<{
|
||||
/** 用以区分区块的标识 */
|
||||
name: string;
|
||||
}>(null);
|
||||
|
@ -822,5 +822,6 @@
|
||||
"Owners": "Owners",
|
||||
"Plugin settings": "Plugin settings",
|
||||
"Menu": "Menu",
|
||||
"Drag and drop sorting field": "Drag and drop sorting field"
|
||||
"Drag and drop sorting field": "Drag and drop sorting field",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "This variable has been deprecated and can be replaced with \"Current form\""
|
||||
}
|
||||
|
@ -755,5 +755,6 @@
|
||||
"DataSource": "Fuente de datos",
|
||||
"Home page": "Página de inicio",
|
||||
"Handbook": "Manual de usuario",
|
||||
"License": "Licencia"
|
||||
"License": "Licencia",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "La variable ha sido obsoleta; \"Formulario actual\" puede ser utilizada como sustituto"
|
||||
}
|
||||
|
@ -775,5 +775,6 @@
|
||||
"Allow selection of existing records":"Permet de sélectionner des données existantes",
|
||||
"Home page": "Page d'accueil",
|
||||
"Handbook": "Manuel de l'utilisateur",
|
||||
"License": "Licence"
|
||||
"License": "Licence",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "La variable a été obsolète ; \"Formulaire actuel\" peut être utilisé comme substitut"
|
||||
}
|
||||
|
@ -694,5 +694,6 @@
|
||||
"The {{type}} \"{{name}}\" may have been deleted. Please remove this {{blockType}}.": "{{type}} \"{{name}}\" は削除されている可能性があります。この {{blockType}} を削除してください。",
|
||||
"Home page": "ホームページ",
|
||||
"Handbook": "ユーザーマニュアル",
|
||||
"License": "ライセンス"
|
||||
"License": "ライセンス",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "この変数は非推奨です。代わりに「現在のフォーム」を使用してください"
|
||||
}
|
||||
|
@ -866,5 +866,6 @@
|
||||
"The {{type}} \"{{name}}\" may have been deleted. Please remove this {{blockType}}.": "{{type}} \"{{name}}\"이(가) 삭제되었을 수 있습니다. 이 {{blockType}}을(를) 제거하십시오.",
|
||||
"Home page": "홈페이지",
|
||||
"Handbook": "사용자 매뉴얼",
|
||||
"License": "라이선스"
|
||||
"License": "라이선스",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "변수가 폐기되었습니다. \"현재 폼\"을 대체로 사용할 수 있습니다"
|
||||
}
|
||||
|
@ -733,5 +733,6 @@
|
||||
"Allow selection of existing records":"Permitir a selecção dos registos existentes",
|
||||
"Home page": "Página inicial",
|
||||
"Handbook": "Manual do usuário",
|
||||
"License": "Licença"
|
||||
"License": "Licença",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "A variável foi descontinuada; \"Formulário atual\" pode ser usada como substituto"
|
||||
}
|
||||
|
@ -569,5 +569,6 @@
|
||||
"DataSource": "Источник данных",
|
||||
"Home page": "Домашняя страница",
|
||||
"Handbook": "Руководство пользователя",
|
||||
"License": "Лицензия"
|
||||
"License": "Лицензия",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "Переменная устарела; \"Текущая форма\" может быть использована в качестве замены"
|
||||
}
|
||||
|
@ -567,5 +567,6 @@
|
||||
"The {{type}} \"{{name}}\" may have been deleted. Please remove this {{blockType}}.": "{{type}} \"{{name}}\" silinmiş olabilir. Lütfen bu {{blockType}}'yi kaldırın.",
|
||||
"Home page": "Anasayfa",
|
||||
"Handbook": "Kullanıcı kılavuzu",
|
||||
"License": "Lisans"
|
||||
"License": "Lisans",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "Değişken kullanımdan kaldırıldı; \"Geçerli form\" yerine kullanılabilir"
|
||||
}
|
||||
|
@ -775,5 +775,6 @@
|
||||
"The {{type}} \"{{name}}\" may have been deleted. Please remove this {{blockType}}.": "{{type}} \"{{name}}\" може бути видалено. Будь ласка, видаліть цей {{blockType}}.",
|
||||
"Home page": "Домашня сторінка",
|
||||
"Handbook": "Посібник користувача",
|
||||
"License": "Ліцензія"
|
||||
"License": "Ліцензія",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "Змінна була застарілою; \"Поточна форма\" може бути використана як заміна"
|
||||
}
|
||||
|
@ -928,5 +928,6 @@
|
||||
"Normal": "常规",
|
||||
"Automatically generate default values": "随机生成默认值",
|
||||
"Refresh data on close": "关闭后刷新数据",
|
||||
"Refresh data on action": "执行后刷新数据"
|
||||
"Refresh data on action": "执行后刷新数据",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "该变量已被弃用,可以使用“当前表单”替代"
|
||||
}
|
||||
|
@ -864,5 +864,6 @@
|
||||
"Allow selection of existing records":"允許選擇已有資料",
|
||||
"Home page": "主頁",
|
||||
"Handbook": "使用手冊",
|
||||
"License": "許可證"
|
||||
"License": "許可證",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "該變數已被棄用,可以使用“當前表單”作為替代"
|
||||
}
|
||||
|
@ -8472,3 +8472,411 @@ export const T3979: PageConfig = {
|
||||
'x-index': 1,
|
||||
},
|
||||
};
|
||||
export const oneTableWithUsersForDeprecatedVariables = {
|
||||
pageSchema: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Page',
|
||||
properties: {
|
||||
'0kf7f0ilx5p': {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'page:addBlock',
|
||||
properties: {
|
||||
'7nkrwaiut3c': {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
'x-app-version': '0.21.0-alpha.12',
|
||||
properties: {
|
||||
okrwqoxd83x: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
'x-app-version': '0.21.0-alpha.12',
|
||||
properties: {
|
||||
h00mdakub1n: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-decorator': 'TableBlockProvider',
|
||||
'x-acl-action': 'users:list',
|
||||
'x-use-decorator-props': 'useTableBlockDecoratorProps',
|
||||
'x-decorator-props': {
|
||||
collection: 'users',
|
||||
dataSource: 'main',
|
||||
action: 'list',
|
||||
params: {
|
||||
pageSize: 20,
|
||||
},
|
||||
rowKey: 'id',
|
||||
showIndex: true,
|
||||
dragSort: false,
|
||||
},
|
||||
'x-toolbar': 'BlockSchemaToolbar',
|
||||
'x-settings': 'blockSettings:table',
|
||||
'x-component': 'CardItem',
|
||||
'x-filter-targets': [],
|
||||
'x-app-version': '0.21.0-alpha.12',
|
||||
properties: {
|
||||
actions: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-initializer': 'table:configureActions',
|
||||
'x-component': 'ActionBar',
|
||||
'x-component-props': {
|
||||
style: {
|
||||
marginBottom: 'var(--nb-spacing)',
|
||||
},
|
||||
},
|
||||
'x-app-version': '0.21.0-alpha.12',
|
||||
'x-uid': 'esse5o4398b',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
'630zp06en3b': {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'array',
|
||||
'x-initializer': 'table:configureColumns',
|
||||
'x-component': 'TableV2',
|
||||
'x-use-component-props': 'useTableBlockProps',
|
||||
'x-component-props': {
|
||||
rowKey: 'id',
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
},
|
||||
},
|
||||
'x-app-version': '0.21.0-alpha.12',
|
||||
properties: {
|
||||
actions: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
title: '{{ t("Actions") }}',
|
||||
'x-action-column': 'actions',
|
||||
'x-decorator': 'TableV2.Column.ActionBar',
|
||||
'x-component': 'TableV2.Column',
|
||||
'x-designer': 'TableV2.ActionColumnDesigner',
|
||||
'x-initializer': 'table:configureItemActions',
|
||||
'x-app-version': '0.21.0-alpha.12',
|
||||
properties: {
|
||||
'1my2j5tqtvu': {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-decorator': 'DndContext',
|
||||
'x-component': 'Space',
|
||||
'x-component-props': {
|
||||
split: '|',
|
||||
},
|
||||
'x-app-version': '0.21.0-alpha.12',
|
||||
properties: {
|
||||
d0bwrtz9f7j: {
|
||||
'x-uid': '7mzammd9sbg',
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
title: 'Edit record',
|
||||
'x-action': 'update',
|
||||
'x-toolbar': 'ActionSchemaToolbar',
|
||||
'x-settings': 'actionSettings:edit',
|
||||
'x-component': 'Action.Link',
|
||||
'x-component-props': {
|
||||
openMode: 'drawer',
|
||||
danger: false,
|
||||
},
|
||||
'x-decorator': 'ACLActionProvider',
|
||||
'x-designer-props': {
|
||||
linkageAction: true,
|
||||
},
|
||||
properties: {
|
||||
drawer: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
title: '{{ t("Edit record") }}',
|
||||
'x-component': 'Action.Container',
|
||||
'x-component-props': {
|
||||
className: 'nb-action-popup',
|
||||
},
|
||||
properties: {
|
||||
tabs: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Tabs',
|
||||
'x-component-props': {},
|
||||
'x-initializer': 'popup:addTab',
|
||||
properties: {
|
||||
tab1: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
title: '{{t("Edit")}}',
|
||||
'x-component': 'Tabs.TabPane',
|
||||
'x-designer': 'Tabs.Designer',
|
||||
'x-component-props': {},
|
||||
properties: {
|
||||
grid: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'popup:common:addBlock',
|
||||
properties: {
|
||||
dq07f3u2u30: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
'x-app-version': '0.21.0-alpha.12',
|
||||
properties: {
|
||||
nxs6wg3n41c: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
'x-app-version': '0.21.0-alpha.12',
|
||||
properties: {
|
||||
mhk7rm0fw12: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-acl-action-props': {
|
||||
skipScopeCheck: false,
|
||||
},
|
||||
'x-acl-action': 'users:update',
|
||||
'x-decorator': 'FormBlockProvider',
|
||||
'x-use-decorator-props':
|
||||
'useEditFormBlockDecoratorProps',
|
||||
'x-decorator-props': {
|
||||
action: 'get',
|
||||
dataSource: 'main',
|
||||
collection: 'users',
|
||||
},
|
||||
'x-toolbar': 'BlockSchemaToolbar',
|
||||
'x-settings': 'blockSettings:editForm',
|
||||
'x-component': 'CardItem',
|
||||
'x-app-version': '0.21.0-alpha.12',
|
||||
properties: {
|
||||
'54b22mqfpmx': {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'FormV2',
|
||||
'x-use-component-props': 'useEditFormBlockProps',
|
||||
'x-app-version': '0.21.0-alpha.12',
|
||||
properties: {
|
||||
grid: {
|
||||
'x-uid': 'ufdkfceoidc',
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'form:configureFields',
|
||||
'x-app-version': '0.21.0-alpha.12',
|
||||
'x-linkage-rules': [
|
||||
{
|
||||
condition: {
|
||||
$and: [
|
||||
{
|
||||
nickname: {
|
||||
$includes: '{{$nRecord.nickname}}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
targetFields: ['nickname'],
|
||||
operator: 'value',
|
||||
value: {
|
||||
mode: 'express',
|
||||
value: '{{$nRecord.username}}',
|
||||
result: '{{$nRecord.username}}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
properties: {
|
||||
g5r0oap3utr: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
'x-app-version': '0.21.0-alpha.12',
|
||||
properties: {
|
||||
lsugzv7lefq: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
'x-app-version': '0.21.0-alpha.12',
|
||||
properties: {
|
||||
nickname: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'string',
|
||||
'x-toolbar':
|
||||
'FormItemSchemaToolbar',
|
||||
'x-settings':
|
||||
'fieldSettings:FormItem',
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-collection-field':
|
||||
'users.nickname',
|
||||
'x-component-props': {},
|
||||
'x-app-version': '0.21.0-alpha.12',
|
||||
'x-uid': '96ztt5zwj94',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'bx1yby02otk',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'ov7zgdkgw75',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
iia8azf8515: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-initializer': 'editForm:configureActions',
|
||||
'x-component': 'ActionBar',
|
||||
'x-component-props': {
|
||||
layout: 'one-column',
|
||||
style: {
|
||||
marginTop: 24,
|
||||
},
|
||||
},
|
||||
'x-app-version': '0.21.0-alpha.12',
|
||||
properties: {
|
||||
m2ya2utojyv: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
title: '{{ t("Submit") }}',
|
||||
'x-action': 'submit',
|
||||
'x-component': 'Action',
|
||||
'x-use-component-props':
|
||||
'useUpdateActionProps',
|
||||
'x-toolbar': 'ActionSchemaToolbar',
|
||||
'x-settings': 'actionSettings:updateSubmit',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
htmlType: 'submit',
|
||||
},
|
||||
'x-action-settings': {
|
||||
triggerWorkflows: [],
|
||||
},
|
||||
type: 'void',
|
||||
'x-app-version': '0.21.0-alpha.12',
|
||||
'x-uid': 'bk42qy23o72',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '7l3e8eg5twd',
|
||||
'x-async': false,
|
||||
'x-index': 2,
|
||||
},
|
||||
},
|
||||
'x-uid': 'hd2ivxz2yab',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'q66c3ygx762',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '6lc4ohi2dro',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'e8wspaxavfr',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'ra8agi0pvsa',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'yos0bp0yikg',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '45maugqwh7b',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'ksrayq74oxe',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-async': false,
|
||||
'x-index': 2,
|
||||
},
|
||||
},
|
||||
'x-uid': 'o0u2aidtwdw',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '14wmkjgx19j',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '5bzwy79fbvz',
|
||||
'x-async': false,
|
||||
'x-index': 2,
|
||||
},
|
||||
},
|
||||
'x-uid': 'qehg70dxj8b',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '1f9ilr6t1yn',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'fcvb1l15gv9',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '2ahlv6ydwlp',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '8uggi2e1r5s',
|
||||
'x-async': true,
|
||||
'x-index': 1,
|
||||
},
|
||||
};
|
||||
|
@ -0,0 +1,59 @@
|
||||
import { expect, test } from '@nocobase/test/e2e';
|
||||
import { oneTableWithUsersForDeprecatedVariables } from '../form-create/templatesOfBug';
|
||||
|
||||
test.describe('deprecated variables', () => {
|
||||
test('current record', async ({ page, mockPage, mockRecord }) => {
|
||||
await mockPage(oneTableWithUsersForDeprecatedVariables).goto();
|
||||
|
||||
// 1. 已设置过 Current record 的变量依然能正常显示
|
||||
await page.getByLabel('action-Action.Link-Edit').click();
|
||||
await page.getByLabel('block-item-CardItem-users-form').hover();
|
||||
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:editForm-users').hover();
|
||||
await page.getByRole('menuitem', { name: 'Linkage rules' }).click();
|
||||
await expect(page.getByLabel('variable-tag').getByText('Current record / Nickname')).toBeVisible();
|
||||
|
||||
// 2. 但是变量列表中是禁用状态
|
||||
await page.locator('button').filter({ hasText: /^x$/ }).click();
|
||||
await page.getByRole('menuitemcheckbox', { name: 'Current record right' }).hover({ position: { x: 40, y: 12 } });
|
||||
await expect(page.getByRole('tooltip', { name: 'This variable has been deprecated' })).toBeVisible();
|
||||
await expect(page.getByRole('menuitemcheckbox', { name: 'Current record right' })).toHaveClass(
|
||||
new RegExp('ant-cascader-menu-item-disabled'),
|
||||
);
|
||||
await page.mouse.move(0, 300);
|
||||
// 使下拉菜单消失
|
||||
await page.getByLabel('Linkage rules').getByText('Linkage rules').click();
|
||||
|
||||
// 表达式输入框也是一样
|
||||
await page.getByText('xSelect a variable').click();
|
||||
await page.getByRole('menuitemcheckbox', { name: 'Current record right' }).hover({ position: { x: 40, y: 12 } });
|
||||
await expect(page.getByRole('tooltip', { name: 'This variable has been deprecated' })).toBeVisible();
|
||||
await expect(page.getByRole('menuitemcheckbox', { name: 'Current record right' })).toHaveClass(
|
||||
new RegExp('ant-cascader-menu-item-disabled'),
|
||||
);
|
||||
await page.mouse.move(0, 300);
|
||||
// 使下拉菜单消失
|
||||
await page.getByLabel('Linkage rules').getByText('Linkage rules').click();
|
||||
|
||||
// 3. 当设置为其它变量后,再次打开,变量列表中的弃用变量不再显示
|
||||
await page.locator('button').filter({ hasText: /^x$/ }).click();
|
||||
await page.getByRole('menuitemcheckbox', { name: 'Current form right' }).click();
|
||||
await page.getByRole('menuitemcheckbox', { name: 'Nickname' }).click();
|
||||
await expect(page.getByLabel('variable-tag').getByText('Current form / Nickname')).toBeVisible();
|
||||
// 清空表达式
|
||||
await page.getByLabel('textbox').clear();
|
||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||
|
||||
// 4. 再次打开弹窗,变量列表中的弃用变量不再显示
|
||||
await page.getByLabel('block-item-CardItem-users-form').hover();
|
||||
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:editForm-users').hover();
|
||||
await page.getByRole('menuitem', { name: 'Linkage rules' }).click();
|
||||
await page.locator('button').filter({ hasText: /^x$/ }).click();
|
||||
await expect(page.getByRole('menuitemcheckbox', { name: 'Current record right' })).toBeHidden();
|
||||
// 使下拉菜单消失
|
||||
await page.getByLabel('Linkage rules').getByText('Linkage rules').click();
|
||||
|
||||
// 表达式也是一样
|
||||
await page.getByText('xSelect a variable').click();
|
||||
await expect(page.getByRole('menuitemcheckbox', { name: 'Current record right' })).toBeHidden();
|
||||
});
|
||||
});
|
@ -37,7 +37,7 @@ test.describe('grid card block schema settings', () => {
|
||||
await page.getByLabel('block-item-BlockItem-general-grid-card').hover();
|
||||
await page.getByLabel('designer-schema-settings-BlockItem-GridCard.Designer-general').hover();
|
||||
await page.getByRole('menuitem', { name: 'Set the count of columns displayed in a row' }).click();
|
||||
await page.getByLabel('block-item-Slider-general-Desktop device').getByText('2', { exact: true }).click();
|
||||
await page.getByLabel('block-item-Slider-general-grid-card-Desktop device').getByText('2', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||
|
||||
// 需要刷新页面才会生效
|
||||
@ -64,7 +64,7 @@ test.describe('grid card block schema settings', () => {
|
||||
await page.getByLabel('block-item-BlockItem-general-').hover();
|
||||
await page.getByLabel('designer-schema-settings-BlockItem-blockSettings:gridCard-general').hover();
|
||||
await page.getByRole('menuitem', { name: 'Set the count of columns displayed in a row' }).click();
|
||||
await page.getByLabel('block-item-Slider-general-Desktop device').getByText('2', { exact: true }).click();
|
||||
await page.getByLabel('block-item-Slider-general-grid-card-Desktop device').getByText('2', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||
|
||||
// 需要刷新页面才会生效
|
||||
|
@ -438,8 +438,8 @@ test.describe('actions schema settings', () => {
|
||||
|
||||
await showMenu(page);
|
||||
await page.getByRole('menuitem', { name: 'Edit button' }).click();
|
||||
await page.getByLabel('block-item-Input-general-Button title').getByRole('textbox').click();
|
||||
await page.getByLabel('block-item-Input-general-Button title').getByRole('textbox').fill('1234');
|
||||
await page.getByLabel('block-item-Input-general-').getByRole('textbox').click();
|
||||
await page.getByLabel('block-item-Input-general-').getByRole('textbox').fill('1234');
|
||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||
|
||||
await expect(page.getByRole('button', { name: '1234' })).toBeVisible();
|
||||
@ -569,8 +569,8 @@ test.describe('actions schema settings', () => {
|
||||
|
||||
await showMenu(page);
|
||||
await page.getByRole('menuitem', { name: 'Edit button' }).click();
|
||||
await page.getByLabel('block-item-Input-general-Button title').getByRole('textbox').click();
|
||||
await page.getByLabel('block-item-Input-general-Button title').getByRole('textbox').fill('Delete record');
|
||||
await page.getByLabel('block-item-Input-general-').getByRole('textbox').click();
|
||||
await page.getByLabel('block-item-Input-general-').getByRole('textbox').fill('Delete record');
|
||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||
|
||||
await expect(page.getByLabel('action-Action.Link-Delete record-destroy-general-table-0')).toBeVisible();
|
||||
|
@ -25,8 +25,8 @@ test.describe('tabs schema settings', () => {
|
||||
await showSettings(page);
|
||||
await page.getByRole('menuitem', { name: 'Edit', exact: true }).click();
|
||||
await page.mouse.move(300, 0);
|
||||
await page.getByLabel('block-item-Input-general-Tab name').getByRole('textbox').click();
|
||||
await page.getByLabel('block-item-Input-general-Tab name').getByRole('textbox').fill('Add new with new name');
|
||||
await page.getByLabel('block-item-Input-general-').getByRole('textbox').click();
|
||||
await page.getByLabel('block-item-Input-general-').getByRole('textbox').fill('Add new with new name');
|
||||
await page.getByRole('button', { name: 'Select icon' }).click();
|
||||
await page.getByLabel('account-book').locator('svg').click();
|
||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||
|
@ -12,7 +12,7 @@ test.describe('z-index of dialog', () => {
|
||||
await page.getByRole('button', { name: 'designer-schema-settings-' }).hover();
|
||||
await page.getByRole('menuitem', { name: 'Edit block title' }).click();
|
||||
|
||||
await expect(page.getByLabel('block-item-Input-users-Block')).toBeVisible();
|
||||
await expect(page.getByLabel('block-item-Input-users-form-')).toBeVisible();
|
||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||
await expect(page.getByLabel('block-item-Input-users-Block')).not.toBeVisible();
|
||||
});
|
||||
|
@ -7,7 +7,6 @@ import useAntdInputStyle from 'antd/es/input/style';
|
||||
import type { DefaultOptionType } from 'antd/lib/cascader';
|
||||
import classNames from 'classnames';
|
||||
import dayjs from 'dayjs';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useCompile } from '../../hooks';
|
||||
@ -151,7 +150,7 @@ export function Input(props) {
|
||||
const { t } = useTranslation();
|
||||
const form = useForm();
|
||||
const [options, setOptions] = React.useState<DefaultOptionType[]>([]);
|
||||
const [variableText, setVariableText] = React.useState('');
|
||||
const [variableText, setVariableText] = React.useState([]);
|
||||
|
||||
const parsed = useMemo(() => parseValue(value), [value]);
|
||||
const isConstant = typeof parsed === 'string';
|
||||
@ -189,8 +188,12 @@ export function Input(props) {
|
||||
}, [type, useTypedConstant]);
|
||||
|
||||
useEffect(() => {
|
||||
setOptions([compile(constantOption), ...(scope ? cloneDeep(scope) : [])]);
|
||||
}, [scope]);
|
||||
const options = [compile(constantOption), ...(scope ? [...scope] : [])].filter((item) => {
|
||||
return !item.deprecated || variable?.[0] === item[names.value];
|
||||
});
|
||||
|
||||
setOptions(options);
|
||||
}, [scope, variable]);
|
||||
|
||||
const loadData = async (selectedOptions: DefaultOptionType[]) => {
|
||||
const option = selectedOptions[selectedOptions.length - 1];
|
||||
@ -251,7 +254,7 @@ export function Input(props) {
|
||||
}
|
||||
}
|
||||
setOptions([...options]);
|
||||
setVariableText(labels.join(' / '));
|
||||
setVariableText([...labels]);
|
||||
};
|
||||
|
||||
run();
|
||||
@ -296,6 +299,7 @@ export function Input(props) {
|
||||
<div
|
||||
role="button"
|
||||
aria-label="variable-tag"
|
||||
style={{ overflow: 'hidden' }}
|
||||
onInput={(e) => e.preventDefault()}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key !== 'Backspace') {
|
||||
@ -309,7 +313,14 @@ export function Input(props) {
|
||||
suppressContentEditableWarning
|
||||
>
|
||||
<Tag contentEditable={false} color="blue">
|
||||
{variableText}
|
||||
{variableText.map((item, index) => {
|
||||
return (
|
||||
<>
|
||||
{index ? ' / ' : ''}
|
||||
{item}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</Tag>
|
||||
</div>
|
||||
{!disabled ? (
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { useForm } from '@formily/react';
|
||||
import { Input, Space } from 'antd';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { Space } from 'antd';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import sanitizeHTML from 'sanitize-html';
|
||||
|
||||
import { error } from '@nocobase/utils/client';
|
||||
|
||||
import { isReactElement } from '@formily/shared';
|
||||
import { EllipsisWithTooltip } from '../..';
|
||||
import { VariableSelect } from './VariableSelect';
|
||||
import { useStyles } from './style';
|
||||
@ -114,7 +115,17 @@ function createOptionsValueLabelMap(options: any[]) {
|
||||
}
|
||||
|
||||
function createVariableTagHTML(variable, keyLabelMap) {
|
||||
const labels = keyLabelMap.get(variable);
|
||||
let labels = keyLabelMap.get(variable);
|
||||
|
||||
if (labels) {
|
||||
labels = labels.map((label) => {
|
||||
if (isReactElement(label)) {
|
||||
return renderToString(label);
|
||||
}
|
||||
return label;
|
||||
});
|
||||
}
|
||||
|
||||
return `<span class="ant-tag ant-tag-blue" contentEditable="false" data-variable="${variable}">${
|
||||
labels ? labels.join(' / ') : '...'
|
||||
}</span>`;
|
||||
@ -428,8 +439,12 @@ export function TextArea(props) {
|
||||
);
|
||||
}
|
||||
|
||||
async function preloadOptions(scope, value) {
|
||||
const options = cloneDeep(scope ?? []);
|
||||
async function preloadOptions(scope, value: string) {
|
||||
let options = [...(scope ?? [])];
|
||||
|
||||
options = options.filter((item) => {
|
||||
return !item.deprecated || value?.includes(item.value);
|
||||
});
|
||||
|
||||
// 重置正则的匹配位置
|
||||
VARIABLE_RE.lastIndex = 0;
|
||||
|
@ -68,8 +68,10 @@ import {
|
||||
useSortFields,
|
||||
} from '..';
|
||||
import {
|
||||
BlockContext,
|
||||
BlockRequestContext_deprecated,
|
||||
FormBlockContext,
|
||||
useBlockContext,
|
||||
useFormBlockContext,
|
||||
useFormBlockType,
|
||||
useTableBlockContext,
|
||||
@ -975,6 +977,7 @@ export const SchemaSettingsModalItem: FC<SchemaSettingsModalItemProps> = (props)
|
||||
const record = useCollectionRecord();
|
||||
const { association } = useDataBlockProps() || {};
|
||||
const formCtx = useFormBlockContext();
|
||||
const blockOptions = useBlockContext();
|
||||
|
||||
// 解决变量`当前对象`值在弹窗中丢失的问题
|
||||
const { formValue: subFormValue, collection: subFormCollection } = useSubFormValue();
|
||||
@ -996,58 +999,60 @@ export const SchemaSettingsModalItem: FC<SchemaSettingsModalItemProps> = (props)
|
||||
{ title: schema.title || title, width },
|
||||
() => {
|
||||
return (
|
||||
<DeclareVariable
|
||||
name="$nPopupRecord"
|
||||
title={popupRecordVariable.title}
|
||||
value={popupRecordVariable.value}
|
||||
collection={popupRecordVariable.collection}
|
||||
>
|
||||
<CollectionRecordProvider record={noRecord ? null : record}>
|
||||
<FormBlockContext.Provider value={formCtx}>
|
||||
<SubFormProvider value={{ value: subFormValue, collection: subFormCollection }}>
|
||||
<FormActiveFieldsProvider
|
||||
name="form"
|
||||
getActiveFieldsName={upLevelActiveFields?.getActiveFieldsName}
|
||||
>
|
||||
<Router location={location} navigator={null}>
|
||||
<BlockRequestContext_deprecated.Provider value={ctx}>
|
||||
<DataSourceApplicationProvider dataSourceManager={dm} dataSource={dataSourceKey}>
|
||||
<AssociationOrCollectionProvider
|
||||
allowNull
|
||||
collection={collection.name}
|
||||
association={association}
|
||||
>
|
||||
<SchemaComponentOptions scope={options.scope} components={options.components}>
|
||||
<FormLayout
|
||||
layout={'vertical'}
|
||||
className={css`
|
||||
// screen > 576px
|
||||
@media (min-width: 576px) {
|
||||
min-width: 520px;
|
||||
}
|
||||
<BlockContext.Provider value={blockOptions}>
|
||||
<DeclareVariable
|
||||
name="$nPopupRecord"
|
||||
title={popupRecordVariable.title}
|
||||
value={popupRecordVariable.value}
|
||||
collection={popupRecordVariable.collection}
|
||||
>
|
||||
<CollectionRecordProvider record={noRecord ? null : record}>
|
||||
<FormBlockContext.Provider value={formCtx}>
|
||||
<SubFormProvider value={{ value: subFormValue, collection: subFormCollection }}>
|
||||
<FormActiveFieldsProvider
|
||||
name="form"
|
||||
getActiveFieldsName={upLevelActiveFields?.getActiveFieldsName}
|
||||
>
|
||||
<Router location={location} navigator={null}>
|
||||
<BlockRequestContext_deprecated.Provider value={ctx}>
|
||||
<DataSourceApplicationProvider dataSourceManager={dm} dataSource={dataSourceKey}>
|
||||
<AssociationOrCollectionProvider
|
||||
allowNull
|
||||
collection={collection.name}
|
||||
association={association}
|
||||
>
|
||||
<SchemaComponentOptions scope={options.scope} components={options.components}>
|
||||
<FormLayout
|
||||
layout={'vertical'}
|
||||
className={css`
|
||||
// screen > 576px
|
||||
@media (min-width: 576px) {
|
||||
min-width: 520px;
|
||||
}
|
||||
|
||||
// screen <= 576px
|
||||
@media (max-width: 576px) {
|
||||
min-width: 320px;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<APIClientProvider apiClient={apiClient}>
|
||||
<ConfigProvider locale={locale}>
|
||||
<SchemaComponent components={components} scope={scope} schema={schema} />
|
||||
</ConfigProvider>
|
||||
</APIClientProvider>
|
||||
</FormLayout>
|
||||
</SchemaComponentOptions>
|
||||
</AssociationOrCollectionProvider>
|
||||
</DataSourceApplicationProvider>
|
||||
</BlockRequestContext_deprecated.Provider>
|
||||
</Router>
|
||||
</FormActiveFieldsProvider>
|
||||
</SubFormProvider>
|
||||
</FormBlockContext.Provider>
|
||||
</CollectionRecordProvider>
|
||||
</DeclareVariable>
|
||||
// screen <= 576px
|
||||
@media (max-width: 576px) {
|
||||
min-width: 320px;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<APIClientProvider apiClient={apiClient}>
|
||||
<ConfigProvider locale={locale}>
|
||||
<SchemaComponent components={components} scope={scope} schema={schema} />
|
||||
</ConfigProvider>
|
||||
</APIClientProvider>
|
||||
</FormLayout>
|
||||
</SchemaComponentOptions>
|
||||
</AssociationOrCollectionProvider>
|
||||
</DataSourceApplicationProvider>
|
||||
</BlockRequestContext_deprecated.Provider>
|
||||
</Router>
|
||||
</FormActiveFieldsProvider>
|
||||
</SubFormProvider>
|
||||
</FormBlockContext.Provider>
|
||||
</CollectionRecordProvider>
|
||||
</DeclareVariable>
|
||||
</BlockContext.Provider>
|
||||
);
|
||||
},
|
||||
theme,
|
||||
|
@ -248,7 +248,7 @@ export function useCompatOldVariables(props: {
|
||||
return variables;
|
||||
}
|
||||
|
||||
variables = _.cloneDeep(variables);
|
||||
variables = [...variables];
|
||||
|
||||
const systemVariable: Option = {
|
||||
value: '$system',
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ISchema, Schema } from '@formily/json-schema';
|
||||
import { Tooltip } from 'antd';
|
||||
import React, { useContext, useMemo } from 'react';
|
||||
import { CollectionFieldOptions_deprecated, useCollectionManager_deprecated } from '../../../collection-manager';
|
||||
import { useCompile, useGetFilterOptions } from '../../../schema-component';
|
||||
@ -29,6 +30,10 @@ interface GetOptionsParams {
|
||||
compile: (value: string) => any;
|
||||
isDisabled?: (params: IsDisabledParams) => boolean;
|
||||
getCollectionField?: (name: string) => CollectionFieldOptions_deprecated;
|
||||
/**
|
||||
* 如果为 true 则表示该变量已被弃用
|
||||
*/
|
||||
deprecated?: boolean;
|
||||
}
|
||||
|
||||
interface BaseProps {
|
||||
@ -60,6 +65,11 @@ interface BaseProps {
|
||||
*/
|
||||
returnFields?(fields: FieldOption[], option: Option): FieldOption[];
|
||||
dataSource?: string;
|
||||
/**
|
||||
* 如果为 true 则表示该变量已被弃用
|
||||
*/
|
||||
deprecated?: boolean;
|
||||
tooltip?: string;
|
||||
}
|
||||
|
||||
interface BaseVariableProviderProps {
|
||||
@ -89,6 +99,7 @@ const getChildren = (
|
||||
isDisabled,
|
||||
targetFieldSchema,
|
||||
getCollectionField,
|
||||
deprecated,
|
||||
}: GetOptionsParams,
|
||||
): Option[] => {
|
||||
const result = options
|
||||
@ -98,9 +109,11 @@ const getChildren = (
|
||||
key: option.name,
|
||||
value: option.name,
|
||||
label: compile(option.title),
|
||||
disabled: noDisabled
|
||||
? false
|
||||
: isDisabled({ option, collectionField, uiSchema, targetFieldSchema, getCollectionField }),
|
||||
disabled:
|
||||
deprecated ||
|
||||
(noDisabled
|
||||
? false
|
||||
: isDisabled({ option, collectionField, uiSchema, targetFieldSchema, getCollectionField })),
|
||||
isLeaf: true,
|
||||
depth,
|
||||
};
|
||||
@ -117,9 +130,11 @@ const getChildren = (
|
||||
isLeaf: false,
|
||||
field: option,
|
||||
depth,
|
||||
disabled: noDisabled
|
||||
? false
|
||||
: isDisabled({ option, collectionField, uiSchema, targetFieldSchema, getCollectionField }),
|
||||
disabled:
|
||||
deprecated ||
|
||||
(noDisabled
|
||||
? false
|
||||
: isDisabled({ option, collectionField, uiSchema, targetFieldSchema, getCollectionField })),
|
||||
loadChildren,
|
||||
};
|
||||
})
|
||||
@ -141,6 +156,8 @@ export const useBaseVariable = ({
|
||||
noDisabled = true,
|
||||
dataSource,
|
||||
returnFields = (fields) => fields,
|
||||
deprecated,
|
||||
tooltip,
|
||||
}: BaseProps) => {
|
||||
const compile = useCompile();
|
||||
const getFilterOptions = useGetFilterOptions();
|
||||
@ -167,6 +184,7 @@ export const useBaseVariable = ({
|
||||
compile,
|
||||
isDisabled: isDisabled || isDisabledDefault,
|
||||
getCollectionField,
|
||||
deprecated,
|
||||
}) || []
|
||||
)
|
||||
// 将叶子节点排列在上面,方便用户选择
|
||||
@ -206,7 +224,25 @@ export const useBaseVariable = ({
|
||||
|
||||
const result = useMemo(() => {
|
||||
return {
|
||||
label: title,
|
||||
label: tooltip ? (
|
||||
<Tooltip placement="left" title={tooltip} zIndex={9999}>
|
||||
<span
|
||||
style={{
|
||||
position: 'relative',
|
||||
display: 'inline-block',
|
||||
marginLeft: -14,
|
||||
paddingLeft: 14,
|
||||
marginRight: -80,
|
||||
paddingRight: 80,
|
||||
zIndex: 1,
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</span>
|
||||
</Tooltip>
|
||||
) : (
|
||||
title
|
||||
),
|
||||
value: name,
|
||||
key: name,
|
||||
isLeaf: noChildren,
|
||||
@ -216,6 +252,8 @@ export const useBaseVariable = ({
|
||||
depth: 0,
|
||||
loadChildren,
|
||||
children: [],
|
||||
disabled: !!deprecated,
|
||||
deprecated,
|
||||
} as Option;
|
||||
}, [uiSchema?.['x-component']]);
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Schema } from '@formily/json-schema';
|
||||
import _ from 'lodash';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useBlockContext } from '../../../block-provider/BlockProvider';
|
||||
import { useFormBlockContext } from '../../../block-provider/FormBlockProvider';
|
||||
import { CollectionFieldOptions_deprecated } from '../../../collection-manager';
|
||||
import { useCollection, useCollectionRecordData } from '../../../data-source';
|
||||
@ -46,6 +47,7 @@ export const useRecordVariable = (props: Props) => {
|
||||
*/
|
||||
export const useCurrentRecordVariable = (props: Props = {}) => {
|
||||
const { t } = useTranslation();
|
||||
const { name: blockType } = useBlockContext() || {};
|
||||
const collection = useCollection();
|
||||
const recordData = useCollectionRecordData();
|
||||
const { formRecord, collectionName } = useFormBlockContext();
|
||||
@ -58,6 +60,8 @@ export const useCurrentRecordVariable = (props: Props = {}) => {
|
||||
collectionName: realCollectionName,
|
||||
noDisabled: props.noDisabled,
|
||||
targetFieldSchema: props.targetFieldSchema,
|
||||
deprecated: blockType === 'form',
|
||||
tooltip: blockType === 'form' ? t('This variable has been deprecated and can be replaced with "Current form"') : '',
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -14,6 +14,7 @@ export interface Option extends DefaultOptionType {
|
||||
loadChildren?(option: Option): Promise<void>;
|
||||
field?: FieldOption;
|
||||
depth?: number;
|
||||
deprecated?: boolean;
|
||||
}
|
||||
|
||||
export interface FieldOption {
|
||||
|
@ -55,7 +55,7 @@ test.describe('form item & create form', () => {
|
||||
},
|
||||
supportVariables: ['Constant', 'Current user', 'Date variables', 'Current form'],
|
||||
inputConstantValue: async () => {
|
||||
await page.getByLabel('block-item-CollectionField-general-general.richText').locator('.ql-editor').click();
|
||||
await page.getByLabel('block-item-VariableInput-').locator('.ql-editor').click();
|
||||
await page.keyboard.type('test rich text');
|
||||
},
|
||||
expectConstantValue: async () => {
|
||||
|
@ -303,8 +303,8 @@ test.describe('table column & table', () => {
|
||||
// 显示出原字段名称
|
||||
await expect(page.getByRole('dialog').getByText('Original field title: singleLineText')).toBeVisible();
|
||||
// 输入新字段名称
|
||||
await page.getByLabel('block-item-Input-general-column title').getByRole('textbox').click();
|
||||
await page.getByLabel('block-item-Input-general-column title').getByRole('textbox').fill('new column title');
|
||||
await page.getByLabel('block-item-Input-general-').getByRole('textbox').click();
|
||||
await page.getByLabel('block-item-Input-general-').getByRole('textbox').fill('new column title');
|
||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||
|
||||
// 新名称应该显示出来
|
||||
@ -415,7 +415,7 @@ test.describe('table column & sub-table in edit form', () => {
|
||||
'Current role',
|
||||
'Current form',
|
||||
'Current object',
|
||||
'Current record',
|
||||
// 'Current record',
|
||||
],
|
||||
variableValue: ['Current user', 'Nickname'],
|
||||
expectVariableValue: async () => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user