@nocobase/client-v2 2.3.0-beta.15 → 2.3.0-beta.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/client-v2",
3
- "version": "2.3.0-beta.15",
3
+ "version": "2.3.0-beta.17",
4
4
  "license": "Apache-2.0",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.mjs",
@@ -27,11 +27,11 @@
27
27
  "@formily/antd-v5": "1.2.3",
28
28
  "@formily/react": "^2.2.27",
29
29
  "@formily/shared": "^2.2.27",
30
- "@nocobase/evaluators": "2.3.0-beta.15",
31
- "@nocobase/flow-engine": "2.3.0-beta.15",
32
- "@nocobase/sdk": "2.3.0-beta.15",
33
- "@nocobase/shared": "2.3.0-beta.15",
34
- "@nocobase/utils": "2.3.0-beta.15",
30
+ "@nocobase/evaluators": "2.3.0-beta.17",
31
+ "@nocobase/flow-engine": "2.3.0-beta.17",
32
+ "@nocobase/sdk": "2.3.0-beta.17",
33
+ "@nocobase/shared": "2.3.0-beta.17",
34
+ "@nocobase/utils": "2.3.0-beta.17",
35
35
  "ahooks": "^3.7.2",
36
36
  "antd": "5.24.2",
37
37
  "antd-style": "3.7.1",
@@ -49,5 +49,5 @@
49
49
  "react-router-dom": "^6.30.1",
50
50
  "zxing-wasm": "^3.1.3"
51
51
  },
52
- "gitHead": "b27658d859f752a05c9956d416f8f03fd7f8e07f"
52
+ "gitHead": "f4203bf51579acea038f4e64f8d897bb79dfd314"
53
53
  }
@@ -46,21 +46,23 @@ LinkActionModel.registerFlow({
46
46
 
47
47
  if (link) {
48
48
  const isExternalLink = isURL(link);
49
+ // embed 是页面容器,不应销毁;仅弹窗视图在站内同窗口跳转后销毁。
50
+ const shouldDestroyView =
51
+ !!ctx.view &&
52
+ shouldDestroyViewAfterLinkNavigation({
53
+ openInNewWindow,
54
+ isExternalLink,
55
+ viewType: ctx.view.type,
56
+ });
49
57
  handleLinkNavigation({
50
58
  link,
51
59
  openInNewWindow,
52
60
  router: ctx.router,
53
61
  isExternalLink,
62
+ // 弹窗会被销毁,用 replace 丢弃弹窗路由;页面(含移动端 embed)跳转需保留历史,以便逐级返回。
63
+ replace: shouldDestroyView,
54
64
  });
55
- // embed 是页面容器,不应销毁;仅弹窗视图在站内同窗口跳转后销毁。
56
- if (
57
- ctx.view &&
58
- shouldDestroyViewAfterLinkNavigation({
59
- openInNewWindow,
60
- isExternalLink,
61
- viewType: ctx.view.type,
62
- })
63
- ) {
65
+ if (shouldDestroyView) {
64
66
  ctx.view.destroy();
65
67
  }
66
68
  } else {
@@ -25,10 +25,11 @@ export function handleLinkNavigation(options: {
25
25
  openInNewWindow?: boolean;
26
26
  router: { navigate: (to: string, options?: { replace?: boolean }) => void };
27
27
  isExternalLink: boolean;
28
+ replace?: boolean;
28
29
  setLocationHref?: (url: string) => void;
29
30
  openWindow?: (url?: string, target?: string) => Window | null;
30
31
  }) {
31
- const { link, openInNewWindow, router, isExternalLink } = options;
32
+ const { link, openInNewWindow, router, isExternalLink, replace = false } = options;
32
33
  const setLocationHref = options.setLocationHref ?? ((url: string) => (window.location.href = url));
33
34
  const openWindow = options.openWindow ?? ((url?: string, target?: string) => window.open(url, target));
34
35
 
@@ -44,7 +45,8 @@ export function handleLinkNavigation(options: {
44
45
 
45
46
  // 该辅助方法仅负责导航,是否关闭弹窗由调用方按场景决定;
46
47
  // 在弹窗上下文中,错误的关闭时机会把新路由回滚为原页面。
47
- router.navigate(link, { replace: true });
48
+ // 默认 push 新的历史记录,保证浏览器/手机右滑返回能逐级回退;仅在需要丢弃当前弹窗路由时由调用方传 replace。
49
+ router.navigate(link, { replace });
48
50
  }
49
51
 
50
52
  /**
@@ -22,6 +22,20 @@ describe('handleLinkNavigation', () => {
22
22
  isExternalLink: false,
23
23
  });
24
24
 
25
+ expect(navigate).toHaveBeenCalledWith('/target-page', { replace: false });
26
+ });
27
+
28
+ it('should replace history entry when caller requests replace', () => {
29
+ const navigate = vi.fn();
30
+
31
+ handleLinkNavigation({
32
+ link: '/target-page',
33
+ openInNewWindow: false,
34
+ router: { navigate },
35
+ isExternalLink: false,
36
+ replace: true,
37
+ });
38
+
25
39
  expect(navigate).toHaveBeenCalledWith('/target-page', { replace: true });
26
40
  });
27
41
 
@@ -58,7 +58,7 @@ export class InputFieldModel extends FieldModel {
58
58
  return <ScanInput {...this.props} />;
59
59
  }
60
60
  const { enableScan, disableManualInput, ...inputProps } = this.props;
61
- return <IMESafeInput {...inputProps} />;
61
+ return <IMESafeInput {...inputProps} style={{ verticalAlign: 'top', ...inputProps.style }} />;
62
62
  }
63
63
  }
64
64
 
@@ -27,6 +27,22 @@ function createInputFieldModel(props?: Record<string, unknown>) {
27
27
  }
28
28
 
29
29
  describe('InputFieldModel', () => {
30
+ it('aligns plain inputs with the top of other filter form controls', () => {
31
+ const model = createInputFieldModel();
32
+
33
+ render(<>{model.render()}</>);
34
+
35
+ expect(screen.getByRole('textbox')).toHaveStyle({ verticalAlign: 'top' });
36
+ });
37
+
38
+ it('preserves explicitly configured input styles', () => {
39
+ const model = createInputFieldModel({ style: { verticalAlign: 'middle', width: 200 } });
40
+
41
+ render(<>{model.render()}</>);
42
+
43
+ expect(screen.getByRole('textbox')).toHaveStyle({ verticalAlign: 'middle', width: '200px' });
44
+ });
45
+
30
46
  it('renders the normal input when scan input is disabled', () => {
31
47
  const consoleError = vi.spyOn(console, 'error').mockImplementation(() => undefined);
32
48
  const model = createInputFieldModel({ disableManualInput: true, enableScan: false });
@@ -263,6 +263,7 @@ export function MobileLazySelect(props: Readonly<LazySelectProps>) {
263
263
  <>
264
264
  <Select
265
265
  {...restProps}
266
+ disabled={disabled}
266
267
  dropdownStyle={buildDropdownStyle(dropdownStyle)}
267
268
  fieldNames={fieldNames}
268
269
  options={realOptions}
@@ -388,6 +388,26 @@ describe('MobileLazySelect', () => {
388
388
  resetMockState();
389
389
  });
390
390
 
391
+ it.each([false, true])('updates the disabled trigger state in %s multiple mode', (multiple) => {
392
+ const onDropdownVisibleChange = vi.fn();
393
+ const { rerender } = renderMobileLazySelect({ multiple, onDropdownVisibleChange });
394
+
395
+ rerender({ disabled: true });
396
+
397
+ expect(mockState.selectProps?.disabled).toBe(true);
398
+ act(() => {
399
+ mockState.selectProps?.onClick?.();
400
+ });
401
+ expect(screen.queryByTestId('popup')).not.toBeInTheDocument();
402
+ expect(onDropdownVisibleChange).not.toHaveBeenCalled();
403
+
404
+ rerender({ disabled: false });
405
+
406
+ expect(mockState.selectProps?.disabled).toBe(false);
407
+ openLazyPopup();
408
+ expect(onDropdownVisibleChange).toHaveBeenCalledWith(true);
409
+ });
410
+
391
411
  it('renders a translated cancel action after searching', () => {
392
412
  renderMobileLazySelect();
393
413