@nocobase/client-v2 2.1.0-beta.26 → 2.1.0-beta.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/flow/components/code-editor/types.d.ts +1 -0
- package/es/flow/models/blocks/filter-form/FilterFormGridModel.d.ts +15 -6
- package/es/flow/models/blocks/shared/filterOperators.d.ts +9 -0
- package/es/flow-compat/data.d.ts +9 -2
- package/es/flow-compat/index.d.ts +1 -1
- package/es/index.d.ts +1 -1
- package/es/index.mjs +90 -90
- package/lib/index.js +83 -83
- package/package.json +5 -5
- package/src/BaseApplication.tsx +1 -1
- package/src/__tests__/app.test.tsx +23 -6
- package/src/flow/actions/titleField.tsx +8 -3
- package/src/flow/components/FieldAssignValueInput.tsx +1 -0
- package/src/flow/components/code-editor/__tests__/linter.test.ts +18 -0
- package/src/flow/components/code-editor/__tests__/runjsDiagnostics.test.ts +23 -0
- package/src/flow/components/code-editor/index.tsx +18 -17
- package/src/flow/components/code-editor/linter.ts +222 -158
- package/src/flow/components/code-editor/runjsDiagnostics.ts +161 -97
- package/src/flow/components/code-editor/types.ts +1 -0
- package/src/flow/components/filter/LinkageFilterItem.tsx +6 -5
- package/src/flow/components/filter/VariableFilterItem.tsx +14 -13
- package/src/flow/components/filter/__tests__/LinkageFilterItem.test.tsx +33 -0
- package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +48 -5
- package/src/flow/internal/utils/__tests__/titleFieldQuickSync.test.ts +1 -0
- package/src/flow/internal/utils/titleFieldQuickSync.ts +2 -2
- package/src/flow/models/actions/FilterActionModel.tsx +17 -9
- package/src/flow/models/blocks/filter-form/FilterFormGridModel.tsx +200 -36
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormGridModel.toggleFormFieldsCollapse.test.ts +270 -1
- package/src/flow/models/blocks/filter-form/__tests__/customFieldOperators.test.tsx +23 -0
- package/src/flow/models/blocks/filter-form/customFieldOperators.ts +12 -1
- package/src/flow/models/blocks/filter-form/fields/FieldComponentProps.tsx +22 -8
- package/src/flow/models/blocks/filter-form/fields/__tests__/FilterFormCustomFieldModel.recordSelect.test.tsx +18 -0
- package/src/flow/models/blocks/filter-manager/FilterManager.ts +51 -1
- package/src/flow/models/blocks/filter-manager/__tests__/FilterManager.test.ts +75 -0
- package/src/flow/models/blocks/form/FormItemModel.tsx +48 -28
- package/src/flow/models/blocks/shared/filterOperators.ts +14 -0
- package/src/flow/models/blocks/table/TableBlockModel.tsx +19 -3
- package/src/flow/models/fields/DividerItemModel.tsx +30 -15
- package/src/flow-compat/data.ts +25 -3
- package/src/flow-compat/index.ts +7 -1
- package/src/index.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/client-v2",
|
|
3
|
-
"version": "2.1.0-beta.
|
|
3
|
+
"version": "2.1.0-beta.27",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "es/index.mjs",
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"@formily/antd-v5": "1.2.3",
|
|
25
25
|
"@formily/react": "^2.2.27",
|
|
26
26
|
"@formily/shared": "^2.2.27",
|
|
27
|
-
"@nocobase/flow-engine": "2.1.0-beta.
|
|
28
|
-
"@nocobase/sdk": "2.1.0-beta.
|
|
29
|
-
"@nocobase/shared": "2.1.0-beta.
|
|
27
|
+
"@nocobase/flow-engine": "2.1.0-beta.27",
|
|
28
|
+
"@nocobase/sdk": "2.1.0-beta.27",
|
|
29
|
+
"@nocobase/shared": "2.1.0-beta.27",
|
|
30
30
|
"ahooks": "^3.7.2",
|
|
31
31
|
"antd": "5.24.2",
|
|
32
32
|
"classnames": "^2.3.1",
|
|
@@ -36,5 +36,5 @@
|
|
|
36
36
|
"react-i18next": "^11.15.1",
|
|
37
37
|
"react-router-dom": "^6.30.1"
|
|
38
38
|
},
|
|
39
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "6d9e2d4ac3c6bf40951c8eb252860e47aa3a3c37"
|
|
40
40
|
}
|
package/src/BaseApplication.tsx
CHANGED
|
@@ -72,29 +72,46 @@ describe('app', () => {
|
|
|
72
72
|
});
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
it('should mount the app
|
|
75
|
+
it('should mount the base app with blank fallback route', async () => {
|
|
76
76
|
const app = createMockClient();
|
|
77
77
|
const element = document.createElement('div');
|
|
78
78
|
act(() => {
|
|
79
79
|
app.mount(element);
|
|
80
80
|
});
|
|
81
|
-
await waitFor(() => expect(element.
|
|
81
|
+
await waitFor(() => expect(element.querySelector('.ant-app')).not.toBeNull());
|
|
82
|
+
expect(element.textContent).toBe('');
|
|
82
83
|
});
|
|
83
84
|
|
|
84
|
-
it('should render
|
|
85
|
+
it('should render blank fallback route by default', async () => {
|
|
85
86
|
const app = createMockClient();
|
|
86
87
|
await renderApp(app);
|
|
87
|
-
expect(screen.
|
|
88
|
+
expect(screen.queryByText('Sorry, the page you visited does not exist.')).not.toBeInTheDocument();
|
|
88
89
|
});
|
|
89
90
|
|
|
90
|
-
it('should render custom "Not Found" component', async () => {
|
|
91
|
+
it('should not render custom "Not Found" component before builtin routes are added', async () => {
|
|
91
92
|
class PluginHelloClient extends Plugin {}
|
|
92
93
|
const app = createMockClient({
|
|
93
94
|
plugins: [PluginHelloClient],
|
|
94
95
|
components: { AppNotFound: () => <div>Not Found2</div> },
|
|
95
96
|
});
|
|
96
97
|
await renderApp(app);
|
|
97
|
-
expect(screen.
|
|
98
|
+
expect(screen.queryByText('Not Found2')).not.toBeInTheDocument();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should render builtin "Not Found" view after builtin routes are added', async () => {
|
|
102
|
+
const app = createMockClient({
|
|
103
|
+
plugins: [NocoBaseBuildInPlugin as any],
|
|
104
|
+
router: { type: 'memory', initialEntries: ['/missing'] },
|
|
105
|
+
});
|
|
106
|
+
app.apiMock.onGet('app:getLang').reply(200, {
|
|
107
|
+
data: {
|
|
108
|
+
lang: 'en-US',
|
|
109
|
+
resources: { client: {} },
|
|
110
|
+
cron: {},
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
await renderApp(app);
|
|
114
|
+
expect(screen.getByText('Sorry, the page you visited does not exist.')).toBeInTheDocument();
|
|
98
115
|
});
|
|
99
116
|
|
|
100
117
|
it('should support app provider functionality', async () => {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { defineAction, DisplayItemModel, FlowModelContext, tExpr } from '@nocobase/flow-engine';
|
|
11
|
-
import {
|
|
11
|
+
import { getFlowFieldInterfaceOptions, isTitleFieldInterface } from '../../flow-compat';
|
|
12
12
|
|
|
13
13
|
const normalizeFilterTargetKey = (filterTargetKey: any) => {
|
|
14
14
|
if (typeof filterTargetKey === 'string') {
|
|
@@ -37,10 +37,15 @@ export const titleField = defineAction({
|
|
|
37
37
|
title: tExpr('Title field'),
|
|
38
38
|
uiMode: (ctx) => {
|
|
39
39
|
const targetCollection = ctx.collectionField.targetCollection;
|
|
40
|
-
const dataSourceManager =
|
|
40
|
+
const dataSourceManager =
|
|
41
|
+
ctx.dataSourceManager || ctx.model?.context?.dataSourceManager || ctx.app?.dataSourceManager;
|
|
41
42
|
const targetFields = targetCollection?.getFields?.() ?? [];
|
|
42
43
|
const options = targetFields
|
|
43
|
-
.filter((field) =>
|
|
44
|
+
.filter((field) =>
|
|
45
|
+
isTitleFieldInterface(
|
|
46
|
+
getFlowFieldInterfaceOptions(field.options?.interface || field.interface, dataSourceManager),
|
|
47
|
+
),
|
|
48
|
+
)
|
|
44
49
|
.map((field) => ({
|
|
45
50
|
value: field.name,
|
|
46
51
|
label: field?.title,
|
|
@@ -39,6 +39,24 @@ describe('code-editor linter', () => {
|
|
|
39
39
|
);
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
+
it('does not warn for callback parameters used inside JSX', () => {
|
|
43
|
+
const code = `
|
|
44
|
+
const columns = [
|
|
45
|
+
{
|
|
46
|
+
render: (roles, record) => (
|
|
47
|
+
<div>
|
|
48
|
+
{roles.map((role) => <Tag key={role.name}>{record.nickname || role.title}</Tag>)}
|
|
49
|
+
</div>
|
|
50
|
+
),
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
`;
|
|
54
|
+
const diags = computeDiagnosticsFromText(code);
|
|
55
|
+
expect(diags.some((d) => d.message.includes('Possible undefined variable: roles'))).toBe(false);
|
|
56
|
+
expect(diags.some((d) => d.message.includes('Possible undefined variable: record'))).toBe(false);
|
|
57
|
+
expect(diags.some((d) => d.message.includes('Possible undefined variable: role'))).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
|
|
42
60
|
it('reports non-callable expression warning', () => {
|
|
43
61
|
const code = `(1+2)()`;
|
|
44
62
|
const diags = computeDiagnosticsFromText(code);
|
|
@@ -38,6 +38,29 @@ describe('runjsDiagnostics', () => {
|
|
|
38
38
|
expect(res.issues.some((i) => i.type === 'lint' && i.ruleId === 'no-noncallable-call')).toBe(true);
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
+
it('does not report JSX callback parameters as undefined variables', async () => {
|
|
42
|
+
const ctx = createTestCtx();
|
|
43
|
+
const code = `
|
|
44
|
+
const { Tag } = ctx.libs.antd;
|
|
45
|
+
const columns = [
|
|
46
|
+
{
|
|
47
|
+
render: (roles, record) => (
|
|
48
|
+
<div>
|
|
49
|
+
{roles.map((role) => <Tag key={role.name}>{record.nickname || role.title}</Tag>)}
|
|
50
|
+
</div>
|
|
51
|
+
),
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
ctx.render(<div />);
|
|
55
|
+
`;
|
|
56
|
+
const res = await diagnoseRunJS(code, ctx);
|
|
57
|
+
expect(
|
|
58
|
+
res.issues.some(
|
|
59
|
+
(i) => i.type === 'lint' && i.ruleId === 'possible-undefined-variable' && /roles|record|role/.test(i.message),
|
|
60
|
+
),
|
|
61
|
+
).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
|
|
41
64
|
it('reports suspicious short ctx member call as a lint issue', async () => {
|
|
42
65
|
const ctx = createTestCtx();
|
|
43
66
|
const res = await diagnoseRunJS('ctx.fw();', ctx);
|
|
@@ -133,6 +133,19 @@ export const CodeEditor: React.FC<CodeEditorProps> = ({
|
|
|
133
133
|
return createRunJSCompletionSource({ hostCtx, staticOptions: finalExtra });
|
|
134
134
|
}, [hostCtx, finalExtra]);
|
|
135
135
|
|
|
136
|
+
const runCurrentCode = useCallback(async () => {
|
|
137
|
+
const code = viewRef.current?.state.doc.toString() || '';
|
|
138
|
+
clearDiagnostics(viewRef.current);
|
|
139
|
+
const res = await run(code);
|
|
140
|
+
if (!res?.success) {
|
|
141
|
+
const rawErr = res?.error;
|
|
142
|
+
const errText = res?.timeout ? tr('Execution timed out') : String(rawErr || tr('Unknown error'));
|
|
143
|
+
const pos = parseErrorLineColumn(rawErr);
|
|
144
|
+
if (pos && viewRef.current) markErrorAt(viewRef.current, pos.line, pos.column, errText);
|
|
145
|
+
}
|
|
146
|
+
return res;
|
|
147
|
+
}, [run, tr]);
|
|
148
|
+
|
|
136
149
|
// JSX 转换支持暂时移除:直接按原样运行代码
|
|
137
150
|
|
|
138
151
|
// 错误标注相关工具已提取至 errorHelpers.ts
|
|
@@ -153,6 +166,9 @@ export const CodeEditor: React.FC<CodeEditorProps> = ({
|
|
|
153
166
|
const v = viewRef.current;
|
|
154
167
|
return v ? v.state.doc.toString() : '';
|
|
155
168
|
},
|
|
169
|
+
run() {
|
|
170
|
+
return Promise.resolve(undefined);
|
|
171
|
+
},
|
|
156
172
|
|
|
157
173
|
buttonGroupHeight: 0,
|
|
158
174
|
snippetEntries: [],
|
|
@@ -160,6 +176,7 @@ export const CodeEditor: React.FC<CodeEditorProps> = ({
|
|
|
160
176
|
});
|
|
161
177
|
extraEditorRef.current.snippetEntries = snippetEntries;
|
|
162
178
|
extraEditorRef.current.logs = logs;
|
|
179
|
+
extraEditorRef.current.run = runCurrentCode;
|
|
163
180
|
|
|
164
181
|
// snippet group display handled in SnippetsDrawer
|
|
165
182
|
|
|
@@ -210,23 +227,7 @@ export const CodeEditor: React.FC<CodeEditorProps> = ({
|
|
|
210
227
|
{tr('Snippets')}
|
|
211
228
|
</Button>
|
|
212
229
|
<>
|
|
213
|
-
<Button
|
|
214
|
-
size="small"
|
|
215
|
-
loading={running}
|
|
216
|
-
onClick={async () => {
|
|
217
|
-
const code = viewRef.current?.state.doc.toString() || '';
|
|
218
|
-
clearDiagnostics(viewRef.current);
|
|
219
|
-
const res = await run(code);
|
|
220
|
-
if (!res?.success) {
|
|
221
|
-
const rawErr = res?.error;
|
|
222
|
-
const errText = res?.timeout
|
|
223
|
-
? tr('Execution timed out')
|
|
224
|
-
: String(rawErr || tr('Unknown error'));
|
|
225
|
-
const pos = parseErrorLineColumn(rawErr);
|
|
226
|
-
if (pos && viewRef.current) markErrorAt(viewRef.current, pos.line, pos.column, errText);
|
|
227
|
-
}
|
|
228
|
-
}}
|
|
229
|
-
>
|
|
230
|
+
<Button size="small" loading={running} onClick={runCurrentCode}>
|
|
230
231
|
{tr('Run')}
|
|
231
232
|
</Button>
|
|
232
233
|
</>
|