@nocobase/client-v2 2.2.0-beta.9 → 2.2.1

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.
Files changed (224) hide show
  1. package/es/APIClient.d.ts +13 -3
  2. package/es/BaseApplication.d.ts +1 -0
  3. package/es/collection-field-interface/CollectionFieldInterface.d.ts +1 -0
  4. package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +1 -0
  5. package/es/components/category-tabs/SortableCategoryTabs.d.ts +55 -0
  6. package/es/components/category-tabs/index.d.ts +9 -0
  7. package/es/components/form/ScanInput/useCodeScanner.d.ts +3 -2
  8. package/es/components/form/TypedVariableInput.d.ts +31 -5
  9. package/es/components/form/filter/CollectionFilter.d.ts +2 -0
  10. package/es/components/form/filter/CollectionFilterItem.d.ts +11 -1
  11. package/es/components/form/filter/CollectionFilterPanel.d.ts +2 -0
  12. package/es/components/form/filter/index.d.ts +2 -0
  13. package/es/components/form/filter/useFilterActionProps.d.ts +2 -0
  14. package/es/components/index.d.ts +1 -0
  15. package/es/flow/actions/index.d.ts +1 -1
  16. package/es/flow/actions/linkageRules.d.ts +27 -0
  17. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +1 -0
  18. package/es/flow/components/FieldAssignExactDatePicker.d.ts +1 -0
  19. package/es/flow/components/FieldAssignValueInput.d.ts +7 -24
  20. package/es/flow/components/FlowRoute.d.ts +2 -2
  21. package/es/flow/components/RunJSValueEditor.d.ts +1 -0
  22. package/es/flow/components/field-value-variable/DateVariableEditor.d.ts +24 -0
  23. package/es/flow/components/field-value-variable/FieldValueVariableInput.d.ts +31 -0
  24. package/es/flow/components/field-value-variable/dateValue.d.ts +36 -0
  25. package/es/flow/components/field-value-variable/index.d.ts +11 -0
  26. package/es/flow/components/filter/FilterGroup.d.ts +1 -0
  27. package/es/flow/components/filter/VariableFilterItem.d.ts +6 -1
  28. package/es/flow/components/placeholders/BlockPlaceholder.d.ts +1 -0
  29. package/es/flow/index.d.ts +1 -0
  30. package/es/flow/internal/registerDeviceTypeContext.d.ts +10 -0
  31. package/es/flow/internal/utils/operatorSchemaHelper.d.ts +2 -2
  32. package/es/flow/models/actions/UpdateRecordActionUtils.d.ts +8 -2
  33. package/es/flow/models/base/ActionModelCore.d.ts +4 -2
  34. package/es/flow/models/base/BlockGridModel.d.ts +3 -0
  35. package/es/flow/models/base/CollectionBlockModel.d.ts +3 -0
  36. package/es/flow/models/base/PageModel/PageModel.d.ts +12 -0
  37. package/es/flow/models/base/PageModel/PageModelTabBar.d.ts +22 -0
  38. package/es/flow/models/base/PageModel/PageTabModel.d.ts +12 -0
  39. package/es/flow/models/blocks/details/DetailsBlockModel.d.ts +3 -0
  40. package/es/flow/models/blocks/filter-form/FilterFormGridModel.d.ts +1 -0
  41. package/es/flow/models/blocks/filter-form/FilterFormSubmitActionModel.d.ts +3 -1
  42. package/es/flow/models/blocks/form/FormBlockModel.d.ts +7 -0
  43. package/es/flow/models/blocks/form/FormGridModel.d.ts +6 -0
  44. package/es/flow/models/blocks/form/QuickEditFormModel.d.ts +17 -2
  45. package/es/flow/models/blocks/form/value-runtime/rules.d.ts +1 -0
  46. package/es/flow/models/blocks/table/JSColumnModel.d.ts +2 -0
  47. package/es/flow/models/blocks/table/TableBlockModel.d.ts +1 -0
  48. package/es/flow/models/blocks/table/TableColumnModel.d.ts +12 -0
  49. package/es/flow/models/blocks/table/utils.d.ts +1 -0
  50. package/es/flow/models/fields/AssociationFieldModel/RecordPickerFieldModel.d.ts +2 -1
  51. package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +0 -17
  52. package/es/flow/models/fields/AssociationFieldModel/recordSelectShared.d.ts +41 -0
  53. package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +15 -7
  54. package/es/flow-compat/fieldValidationConstants.d.ts +1 -1
  55. package/es/flow-compat/passwordUtils.d.ts +1 -1
  56. package/es/flow-compat/routeTypes.d.ts +1 -0
  57. package/es/index.d.ts +5 -0
  58. package/es/index.mjs +199 -161
  59. package/es/ui-operation/index.d.ts +15 -0
  60. package/es/ui-operation/ui-operation-codec.d.ts +10 -0
  61. package/es/utils/getRouteRuntimeVersion.d.ts +23 -0
  62. package/es/utils/index.d.ts +1 -0
  63. package/lib/index.js +215 -177
  64. package/lib/locale/languageCodes.js +3 -1
  65. package/package.json +9 -7
  66. package/src/APIClient.ts +90 -7
  67. package/src/Application.tsx +7 -1
  68. package/src/BaseApplication.tsx +11 -6
  69. package/src/__tests__/APIClient.test.tsx +58 -0
  70. package/src/__tests__/app.test.tsx +73 -0
  71. package/src/__tests__/browserChecker.test.ts +61 -0
  72. package/src/__tests__/getRouteRuntimeVersion.test.ts +68 -0
  73. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +35 -2
  74. package/src/__tests__/settings-center.test.tsx +270 -6
  75. package/src/acl/ACLProvider.tsx +2 -0
  76. package/src/acl/__tests__/ACLProvider.test.tsx +92 -0
  77. package/src/collection-field-interface/CollectionFieldInterface.ts +1 -0
  78. package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +1 -0
  79. package/src/collection-manager/field-validation.ts +1 -1
  80. package/src/components/README.md +7 -1
  81. package/src/components/README.zh-CN.md +6 -1
  82. package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
  83. package/src/components/category-tabs/index.ts +10 -0
  84. package/src/components/form/JsonTextArea.tsx +4 -0
  85. package/src/components/form/ScanInput/CodeScanner.tsx +79 -35
  86. package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +101 -0
  87. package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +284 -11
  88. package/src/components/form/ScanInput/useCodeScanner.ts +332 -24
  89. package/src/components/form/TypedVariableInput.tsx +452 -101
  90. package/src/components/form/__tests__/TypedVariableInput.test.tsx +364 -12
  91. package/src/components/form/filter/CollectionFilter.tsx +4 -0
  92. package/src/components/form/filter/CollectionFilterItem.tsx +36 -9
  93. package/src/components/form/filter/CollectionFilterPanel.tsx +4 -0
  94. package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +55 -0
  95. package/src/components/form/filter/__tests__/useFilterActionProps.test.tsx +95 -0
  96. package/src/components/form/filter/index.ts +2 -0
  97. package/src/components/form/filter/useFilterActionProps.ts +37 -6
  98. package/src/components/form/table/Table.tsx +81 -6
  99. package/src/components/form/table/__tests__/Table.columnWidth.test.tsx +433 -0
  100. package/src/components/index.ts +1 -0
  101. package/src/flow/FlowPage.tsx +9 -1
  102. package/src/flow/__tests__/FlowPage.test.tsx +50 -3
  103. package/src/flow/__tests__/FlowRoute.test.tsx +128 -10
  104. package/src/flow/__tests__/PluginFlowEngine.test.ts +58 -0
  105. package/src/flow/actions/__tests__/actionLinkageRules.forkProps.test.ts +314 -0
  106. package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +102 -0
  107. package/src/flow/actions/__tests__/linkageRules.actionStates.test.ts +33 -0
  108. package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +72 -0
  109. package/src/flow/actions/__tests__/linkageRules.tab.test.ts +171 -0
  110. package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +7 -3
  111. package/src/flow/actions/index.ts +2 -0
  112. package/src/flow/actions/linkageRules.tsx +144 -26
  113. package/src/flow/actions/linkageRulesRefresh.tsx +2 -6
  114. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +0 -1
  115. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +78 -59
  116. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +253 -6
  117. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +228 -0
  118. package/src/flow/common/Markdown/style.ts +4 -0
  119. package/src/flow/components/BlockItemCard.tsx +2 -2
  120. package/src/flow/components/FieldAssignExactDatePicker.tsx +25 -11
  121. package/src/flow/components/FieldAssignValueInput.tsx +72 -584
  122. package/src/flow/components/FlowRoute.tsx +78 -32
  123. package/src/flow/components/RunJSValueEditor.tsx +9 -1
  124. package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +216 -0
  125. package/src/flow/components/field-value-variable/DateVariableEditor.tsx +181 -0
  126. package/src/flow/components/field-value-variable/FieldValueVariableInput.tsx +326 -0
  127. package/src/flow/components/field-value-variable/__tests__/FieldValueVariableInput.test.tsx +380 -0
  128. package/src/flow/components/field-value-variable/dateValue.ts +223 -0
  129. package/src/flow/components/field-value-variable/index.ts +12 -0
  130. package/src/flow/components/filter/FilterGroup.tsx +33 -5
  131. package/src/flow/components/filter/VariableFilterItem.tsx +170 -13
  132. package/src/flow/components/filter/__tests__/FilterGroup.test.tsx +45 -0
  133. package/src/flow/components/filter/__tests__/VariableFilterItem.leftMetaTree.test.tsx +9 -0
  134. package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +195 -1
  135. package/src/flow/components/placeholders/BlockPlaceholder.tsx +70 -23
  136. package/src/flow/components/placeholders/__tests__/BlockPlaceholder.test.tsx +57 -7
  137. package/src/flow/index.ts +3 -0
  138. package/src/flow/internal/registerDeviceTypeContext.ts +39 -0
  139. package/src/flow/internal/utils/operatorSchemaHelper.ts +3 -2
  140. package/src/flow/models/actions/UpdateRecordActionModel.tsx +18 -1
  141. package/src/flow/models/actions/UpdateRecordActionUtils.ts +18 -6
  142. package/src/flow/models/actions/__tests__/UpdateRecordActionModel.test.ts +76 -4
  143. package/src/flow/models/base/ActionModelCore.tsx +11 -2
  144. package/src/flow/models/base/BlockGridModel.tsx +26 -0
  145. package/src/flow/models/base/CollectionBlockModel.tsx +38 -3
  146. package/src/flow/models/base/PageModel/PageModel.tsx +387 -53
  147. package/src/flow/models/base/PageModel/PageModelTabBar.tsx +114 -0
  148. package/src/flow/models/base/PageModel/PageTabModel.tsx +249 -18
  149. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +790 -10
  150. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.module-isolation.test.tsx +130 -0
  151. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.test.tsx +118 -0
  152. package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +1075 -7
  153. package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +49 -0
  154. package/src/flow/models/base/__tests__/BlockGridModel.selectSceneActivation.test.ts +124 -0
  155. package/src/flow/models/base/__tests__/CollectionBlockModel.addAppends.test.ts +41 -0
  156. package/src/flow/models/base/__tests__/CollectionBlockModel.initialBeforeRenderRefresh.test.ts +125 -9
  157. package/src/flow/models/blocks/assign-form/AssignFormItemModel.tsx +28 -53
  158. package/src/flow/models/blocks/details/DetailsBlockModel.tsx +8 -3
  159. package/src/flow/models/blocks/details/__tests__/DetailsBlockModel.initialPaginationChangeRefresh.test.ts +32 -1
  160. package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +28 -3
  161. package/src/flow/models/blocks/filter-form/FilterFormGridModel.tsx +36 -5
  162. package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +155 -25
  163. package/src/flow/models/blocks/filter-form/FilterFormSubmitActionModel.tsx +57 -4
  164. package/src/flow/models/blocks/filter-form/__tests__/FilterFormGridModel.onModelCreated.test.ts +174 -1
  165. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +395 -1
  166. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.getFilterValue.test.ts +72 -0
  167. package/src/flow/models/blocks/filter-form/__tests__/FilterFormSubmitActionModel.lifecycle.test.ts +90 -0
  168. package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +66 -4
  169. package/src/flow/models/blocks/filter-manager/FilterManager.ts +5 -0
  170. package/src/flow/models/blocks/filter-manager/__tests__/FilterManager.test.ts +40 -0
  171. package/src/flow/models/blocks/form/FormBlockModel.tsx +47 -0
  172. package/src/flow/models/blocks/form/FormGridModel.tsx +4 -0
  173. package/src/flow/models/blocks/form/QuickEditFormModel.tsx +189 -36
  174. package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +8 -2
  175. package/src/flow/models/blocks/form/__tests__/QuickEditFormModel.quickEdit.test.ts +350 -2
  176. package/src/flow/models/blocks/form/__tests__/runJsFormSubmit.test.ts +131 -0
  177. package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +435 -0
  178. package/src/flow/models/blocks/form/value-runtime/rules.ts +67 -14
  179. package/src/flow/models/blocks/form/value-runtime/runtime.ts +43 -19
  180. package/src/flow/models/blocks/js-block/JSBlock.tsx +1 -1
  181. package/src/flow/models/blocks/table/JSColumnModel.tsx +10 -1
  182. package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +2 -1
  183. package/src/flow/models/blocks/table/TableBlockModel.tsx +29 -3
  184. package/src/flow/models/blocks/table/TableColumnModel.tsx +48 -9
  185. package/src/flow/models/blocks/table/__tests__/JSColumnModel.test.tsx +52 -5
  186. package/src/flow/models/blocks/table/__tests__/TableBlockModel.filterReset.test.ts +78 -0
  187. package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +12 -0
  188. package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +33 -0
  189. package/src/flow/models/blocks/table/utils.ts +7 -0
  190. package/src/flow/models/fields/AssociationFieldModel/CascadeSelectFieldModel.tsx +33 -1
  191. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +10 -1
  192. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/__tests__/popupContext.test.ts +120 -0
  193. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/actions/PopupSubTableEditActionModel.tsx +10 -2
  194. package/src/flow/models/fields/AssociationFieldModel/RecordPickerFieldModel.tsx +24 -1
  195. package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +9 -136
  196. package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +167 -3
  197. package/src/flow/models/fields/AssociationFieldModel/recordSelectShared.tsx +156 -0
  198. package/src/flow/models/fields/DateTimeFieldModel/__tests__/DateTimeNoTzFieldModel.dateLimit.test.tsx +149 -0
  199. package/src/flow/models/fields/DateTimeFieldModel/dateLimit.ts +149 -113
  200. package/src/flow/models/fields/DisplayNumberFieldModel.tsx +1 -1
  201. package/src/flow/models/fields/JsonFieldModel.tsx +31 -8
  202. package/src/flow/models/fields/NumberFieldModel.tsx +1 -1
  203. package/src/flow/models/fields/VariableFieldFormModel.tsx +3 -3
  204. package/src/flow/models/fields/__tests__/DisplayNumberFieldModel.test.ts +33 -0
  205. package/src/flow/models/fields/__tests__/JsonFieldModel.test.ts +82 -0
  206. package/src/flow/models/fields/__tests__/NumberFieldModel.test.tsx +47 -0
  207. package/src/flow/models/fields/__tests__/VariableFieldFormModel.test.tsx +51 -0
  208. package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +26 -10
  209. package/src/flow/models/fields/mobile-components/MobileSelect.tsx +51 -14
  210. package/src/flow/models/fields/mobile-components/__tests__/MobileSelect.test.tsx +139 -13
  211. package/src/flow/utils/dataScopeFormValueClear.ts +4 -3
  212. package/src/flow-compat/fieldValidationConstants.ts +1 -1
  213. package/src/flow-compat/routeTypes.ts +1 -0
  214. package/src/index.ts +5 -0
  215. package/src/layout-manager/LayoutContentRoute.tsx +2 -1
  216. package/src/layout-manager/LayoutRoute.tsx +2 -1
  217. package/src/layout-manager/__tests__/LayoutRoute.test.tsx +87 -1
  218. package/src/locale/languageCodes.ts +3 -1
  219. package/src/nocobase-buildin-plugin/index.tsx +12 -0
  220. package/src/settings-center/AdminSettingsLayout.tsx +23 -2
  221. package/src/ui-operation/index.ts +33 -0
  222. package/src/ui-operation/ui-operation-codec.ts +54 -0
  223. package/src/utils/getRouteRuntimeVersion.ts +185 -0
  224. package/src/utils/index.tsx +1 -0
@@ -19,6 +19,7 @@ import {
19
19
  AdminLayoutMenuItemModel,
20
20
  AdminLayoutModel,
21
21
  ADMIN_LAYOUT_MODEL_UID,
22
+ buildMenuTitleWithIcon,
22
23
  getAdminLayoutMenuMovePositionOptions,
23
24
  normalizeAdminLayoutMenuLegacyVariables,
24
25
  openAdminLayoutMenuLink,
@@ -119,6 +120,233 @@ describe('AdminLayoutModel menu items', () => {
119
120
  ...options,
120
121
  });
121
122
 
123
+ type MenuTitleOptions = {
124
+ title: string;
125
+ collapsed: boolean;
126
+ name?: React.ReactNode;
127
+ routeTitle?: string;
128
+ renderType?: 'item' | 'group';
129
+ tooltip?: string;
130
+ badgeCount?: number;
131
+ badgeIndicatorColor?: string;
132
+ badgeTextColor?: string;
133
+ depth?: number;
134
+ };
135
+
136
+ const createMenuTitle = (options: MenuTitleOptions) => {
137
+ const {
138
+ title,
139
+ collapsed,
140
+ name = title,
141
+ routeTitle = title,
142
+ renderType = 'item',
143
+ tooltip,
144
+ badgeCount,
145
+ badgeIndicatorColor,
146
+ badgeTextColor,
147
+ depth = 1,
148
+ } = options;
149
+
150
+ return React.createElement(
151
+ FlowEngineProvider,
152
+ { engine },
153
+ React.createElement(
154
+ MemoryRouter,
155
+ { initialEntries: ['/admin/current-page'] },
156
+ React.createElement(AdminLayoutMenuItemRenderer, {
157
+ renderType,
158
+ item: {
159
+ name,
160
+ path: '/admin/menu-title',
161
+ _runtimePath: '/apps/demo/v2/admin/menu-title',
162
+ _navigationMode: 'spa',
163
+ _isLegacy: false,
164
+ _depth: depth,
165
+ _route: {
166
+ type: NocoBaseDesktopRouteType.flowPage,
167
+ title: routeTitle,
168
+ tooltip,
169
+ schemaUid: 'menu-title',
170
+ options:
171
+ badgeCount == null
172
+ ? {}
173
+ : {
174
+ badge: {
175
+ count: badgeCount,
176
+ styles: badgeIndicatorColor ? { indicator: { color: badgeIndicatorColor } } : undefined,
177
+ textColor: badgeTextColor,
178
+ },
179
+ },
180
+ },
181
+ },
182
+ dom: React.createElement(
183
+ 'span',
184
+ { className: 'ant-pro-base-menu-inline-item-text' },
185
+ collapsed ? title.slice(0, 1) : title,
186
+ ),
187
+ options: { isMobile: false, collapsed },
188
+ }),
189
+ ),
190
+ );
191
+ };
192
+
193
+ const renderMenuTitle = (options: MenuTitleOptions) => render(createMenuTitle(options));
194
+
195
+ it.each([false, true])(
196
+ 'should use a native title without an extra tooltip trigger when collapsed is %s',
197
+ async (collapsed) => {
198
+ vi.useFakeTimers();
199
+ const title = 'A complete menu title that may be truncated';
200
+
201
+ try {
202
+ const { container } = renderMenuTitle({ title, collapsed });
203
+ const link = screen.getByRole('link', { name: title });
204
+ const menuItem = link.closest<HTMLElement>('[role="none"]');
205
+
206
+ expect(link).toHaveAttribute('title', title);
207
+ expect(link.parentElement).toBe(menuItem);
208
+ expect(menuItem).toHaveStyle({ width: '100%', minWidth: 0 });
209
+ expect(link).toHaveStyle({ display: 'block', width: '100%', minWidth: 0, overflow: 'hidden' });
210
+ expect(container.querySelector('.ant-tooltip')).not.toBeInTheDocument();
211
+
212
+ await act(async () => {
213
+ fireEvent.mouseEnter(link);
214
+ await vi.advanceTimersByTimeAsync(500);
215
+ });
216
+
217
+ expect(screen.queryByRole('tooltip')).not.toBeInTheDocument();
218
+ } finally {
219
+ vi.clearAllTimers();
220
+ vi.useRealTimers();
221
+ }
222
+ },
223
+ );
224
+
225
+ it('should use the translated native title for an icon-wrapped nested menu name', () => {
226
+ const title = 'A translated nested menu title';
227
+ const { name } = buildMenuTitleWithIcon(
228
+ { title: 'menu.nested.title', icon: 'AppstoreOutlined' },
229
+ () => title,
230
+ true,
231
+ );
232
+
233
+ renderMenuTitle({ title, name, collapsed: true, depth: 2 });
234
+
235
+ expect(screen.getByRole('link', { name: title })).toHaveAttribute('title', title);
236
+ });
237
+
238
+ it('should use the translated native title for an icon-wrapped nested group name', () => {
239
+ const title = 'A translated nested group title';
240
+ const { name } = buildMenuTitleWithIcon(
241
+ { title: 'menu.nested.group', icon: 'AppstoreOutlined' },
242
+ () => title,
243
+ true,
244
+ );
245
+ const { container } = renderMenuTitle({
246
+ title,
247
+ name,
248
+ routeTitle: 'menu.nested.group',
249
+ renderType: 'group',
250
+ collapsed: true,
251
+ depth: 2,
252
+ });
253
+ const group = container.querySelector('[role="none"]');
254
+
255
+ expect(group).toHaveAttribute('title', title);
256
+ expect(group).toHaveAttribute('aria-label', title);
257
+ });
258
+
259
+ it('should preserve a numeric group name as the native title', () => {
260
+ const { container } = renderMenuTitle({
261
+ title: '0',
262
+ name: 0,
263
+ routeTitle: 'menu.numeric.group',
264
+ renderType: 'group',
265
+ collapsed: true,
266
+ });
267
+ const group = container.querySelector('[role="none"]');
268
+
269
+ expect(group).toHaveAttribute('title', '0');
270
+ expect(group).toHaveAttribute('aria-label', '0');
271
+ });
272
+
273
+ it.each(['item', 'group'] as const)(
274
+ 'should let an explicit route tooltip own hover behavior for a sider %s',
275
+ (renderType) => {
276
+ const title = 'A menu title with an explicit description';
277
+ const { container } = renderMenuTitle({
278
+ title,
279
+ renderType,
280
+ collapsed: false,
281
+ tooltip: 'An explicitly configured menu description',
282
+ });
283
+ const titleTarget =
284
+ renderType === 'item' ? screen.getByRole('link', { name: title }) : container.querySelector('[role="none"]');
285
+
286
+ expect(titleTarget).not.toHaveAttribute('title');
287
+ expect(screen.getByRole('img', { name: 'question-circle' })).toBeInTheDocument();
288
+ },
289
+ );
290
+
291
+ it('should keep the same menu link when the sider expands', async () => {
292
+ vi.useFakeTimers();
293
+ const title = 'A title that fits after expanding';
294
+
295
+ try {
296
+ const { rerender } = renderMenuTitle({ title, collapsed: true });
297
+ const link = screen.getByRole('link', { name: title });
298
+
299
+ await act(async () => {
300
+ rerender(createMenuTitle({ title, collapsed: false }));
301
+ await vi.runOnlyPendingTimersAsync();
302
+ });
303
+
304
+ expect(screen.getByRole('link', { name: title })).toBe(link);
305
+ expect(link).toHaveAttribute('title', title);
306
+ } finally {
307
+ vi.clearAllTimers();
308
+ vi.useRealTimers();
309
+ }
310
+ });
311
+
312
+ it.each([true, false])(
313
+ 'should render one badge indicator for a nested menu title when collapsed is %s',
314
+ (collapsed) => {
315
+ const title = 'A nested menu title with a badge';
316
+ const { container } = renderMenuTitle({ title, collapsed, badgeCount: 7 });
317
+
318
+ expect(container.querySelectorAll('.ant-badge-count')).toHaveLength(1);
319
+ },
320
+ );
321
+
322
+ it('should render one badge indicator for a top-level collapsed menu title', () => {
323
+ const title = 'A top-level collapsed menu title with a badge';
324
+ const { container } = renderMenuTitle({
325
+ title,
326
+ collapsed: true,
327
+ badgeCount: 7,
328
+ badgeTextColor: 'rgb(1, 2, 3)',
329
+ depth: 0,
330
+ });
331
+ const indicator = container.querySelector<HTMLElement>('.ant-badge-count');
332
+
333
+ expect(container.querySelectorAll('.ant-badge-count')).toHaveLength(1);
334
+ expect(indicator).toHaveStyle({ color: 'rgb(1, 2, 3)' });
335
+ });
336
+
337
+ it('should preserve the Ant Design indicator color when collapsed badge textColor is unset', () => {
338
+ const title = 'A top-level collapsed menu title with an indicator color';
339
+ const { container } = renderMenuTitle({
340
+ title,
341
+ collapsed: true,
342
+ badgeCount: 7,
343
+ badgeIndicatorColor: 'rgb(4, 5, 6)',
344
+ depth: 0,
345
+ });
346
+
347
+ expect(container.querySelector('.ant-badge-count')).toHaveStyle({ color: 'rgb(4, 5, 6)' });
348
+ });
349
+
122
350
  it('should normalize legacy variables only inside template expressions', () => {
123
351
  expect(normalizeAdminLayoutMenuLegacyVariables('https://a.com?id={{ $user.id }}')).toBe(
124
352
  'https://a.com?id={{ ctx.user.id }}',
@@ -26,6 +26,10 @@ export default function useStyle() {
26
26
  () => ({
27
27
  [`.${COMPONENT_CLS}`]: {
28
28
  '.vditor-reset': { fontSize: `${token.fontSize}px !important`, color: 'unset' },
29
+ '.vditor-reset h2': {
30
+ borderBottom: 'none !important',
31
+ boxShadow: 'none !important',
32
+ },
29
33
  '.vditor': {
30
34
  borderRadius: 8,
31
35
  },
@@ -150,7 +150,7 @@ export const BlockItemCard = React.forwardRef(
150
150
  ) => {
151
151
  const { t } = useTranslation();
152
152
  const { token } = theme.useToken();
153
- const { title: blockTitle, description, children, className, heightMode, ...rest } = props;
153
+ const { title: blockTitle, description, children, className, heightMode, style, ...rest } = props;
154
154
  const cardRef = useRef<HTMLDivElement | null>(null);
155
155
  const setCardRef = useCallback(
156
156
  (node: HTMLDivElement | null) => {
@@ -185,7 +185,7 @@ export const BlockItemCard = React.forwardRef(
185
185
  <Card
186
186
  ref={setCardRef as any}
187
187
  title={title}
188
- style={{ display: 'flex', flexDirection: 'column', height: height }}
188
+ style={{ display: 'flex', flexDirection: 'column', ...style, height: height ?? style?.height }}
189
189
  styles={{
190
190
  body: { flex: 1, display: 'flex', flexDirection: 'column', overflow: 'auto' },
191
191
  header: {
@@ -7,7 +7,7 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
 
10
- import React, { useMemo, useState } from 'react';
10
+ import React, { useCallback, useMemo, useState } from 'react';
11
11
  import { DatePicker, Select, Space } from 'antd';
12
12
  import { inferPickerType } from '../../flow-compat';
13
13
  import { dayjs, getDateTimeFormat, getPickerFormat } from '@nocobase/utils/client';
@@ -29,6 +29,7 @@ export interface FieldAssignExactDatePickerProps {
29
29
  isRange?: boolean;
30
30
  onChange?: (value: ExactDatePickerValue) => void;
31
31
  style?: React.CSSProperties;
32
+ disabled?: boolean;
32
33
  }
33
34
 
34
35
  function getRawSingleValue(value: unknown, isRange: boolean): unknown {
@@ -114,7 +115,7 @@ function parseRangeValue(value: unknown, format: string): [dayjs.Dayjs, dayjs.Da
114
115
  }
115
116
 
116
117
  export const FieldAssignExactDatePicker: React.FC<FieldAssignExactDatePickerProps> = (props) => {
117
- const { picker = 'date', format, showTime, timeFormat, value, isRange = false, onChange, style } = props;
118
+ const { picker = 'date', format, showTime, timeFormat, value, isRange = false, onChange, style, disabled } = props;
118
119
  const flowCtx = useFlowContext();
119
120
  const t = flowCtx.model.translate.bind(flowCtx.model);
120
121
 
@@ -124,17 +125,17 @@ export const FieldAssignExactDatePicker: React.FC<FieldAssignExactDatePickerProp
124
125
  return inferPickerFromRawValue(rawSingleValue, picker);
125
126
  });
126
127
 
127
- const getResolvedFormat = (nextPicker: ExactDatePickerMode) => {
128
- const baseFormat = nextPicker === picker && format ? format : getPickerFormat(nextPicker);
129
- const dateFormat = nextPicker === 'date' ? stripTimeFromFormat(baseFormat) : baseFormat;
130
- return getDateTimeFormat(nextPicker, dateFormat, showTime, timeFormat);
131
- };
132
-
133
- const resolvedFormat = useMemo(
134
- () => getResolvedFormat(targetPicker),
135
- [targetPicker, format, picker, showTime, timeFormat],
128
+ const getResolvedFormat = useCallback(
129
+ (nextPicker: ExactDatePickerMode) => {
130
+ const baseFormat = nextPicker === picker && format ? format : getPickerFormat(nextPicker);
131
+ const dateFormat = nextPicker === 'date' ? stripTimeFromFormat(baseFormat) : baseFormat;
132
+ return getDateTimeFormat(nextPicker, dateFormat, showTime, timeFormat);
133
+ },
134
+ [format, picker, showTime, timeFormat],
136
135
  );
137
136
 
137
+ const resolvedFormat = useMemo(() => getResolvedFormat(targetPicker), [getResolvedFormat, targetPicker]);
138
+
138
139
  const singleValue = useMemo(() => {
139
140
  if (isRange) return null;
140
141
  return parseDateFromRawValue(value, resolvedFormat);
@@ -156,6 +157,10 @@ export const FieldAssignExactDatePicker: React.FC<FieldAssignExactDatePickerProp
156
157
  );
157
158
 
158
159
  const handlePickerTypeChange = (nextPicker: ExactDatePickerMode) => {
160
+ if (disabled) {
161
+ return;
162
+ }
163
+
159
164
  setTargetPicker(nextPicker);
160
165
 
161
166
  if (!onChange) {
@@ -182,7 +187,11 @@ export const FieldAssignExactDatePicker: React.FC<FieldAssignExactDatePickerProp
182
187
  inputReadOnly: flowCtx.isMobileLayout,
183
188
  showTime: showTime ? { defaultValue: dayjs('00:00:00', 'HH:mm:ss') } : false,
184
189
  value: singleValue,
190
+ disabled,
185
191
  onChange: (nextDate: dayjs.Dayjs | null) => {
192
+ if (disabled) {
193
+ return;
194
+ }
186
195
  if (nextDate && dayjs.isDayjs(nextDate)) {
187
196
  onChange?.(nextDate);
188
197
  return;
@@ -200,7 +209,11 @@ export const FieldAssignExactDatePicker: React.FC<FieldAssignExactDatePickerProp
200
209
  inputReadOnly: flowCtx.isMobileLayout,
201
210
  showTime: showTime ? { defaultValue: [dayjs('00:00:00', 'HH:mm:ss'), dayjs('23:59:59', 'HH:mm:ss')] } : false,
202
211
  value: rangeValue,
212
+ disabled,
203
213
  onChange: (nextDates: [dayjs.Dayjs | null, dayjs.Dayjs | null] | null) => {
214
+ if (disabled) {
215
+ return;
216
+ }
204
217
  if (Array.isArray(nextDates) && nextDates[0] && nextDates[1]) {
205
218
  onChange?.([nextDates[0], nextDates[1]]);
206
219
  return;
@@ -218,6 +231,7 @@ export const FieldAssignExactDatePicker: React.FC<FieldAssignExactDatePickerProp
218
231
  value={targetPicker}
219
232
  options={pickerOptions}
220
233
  onChange={handlePickerTypeChange}
234
+ disabled={disabled}
221
235
  />
222
236
  {isRange ? <DatePicker.RangePicker {...rangePickerProps} /> : <DatePicker {...singlePickerProps} />}
223
237
  </Space.Compact>