@firecms/core 3.0.1 → 3.1.0-canary.02232f4

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 (334) hide show
  1. package/README.md +1 -1
  2. package/dist/components/AIIcon.d.ts +16 -0
  3. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +7 -1
  4. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
  5. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +14 -0
  6. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +6 -0
  7. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +5 -4
  8. package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +6 -0
  9. package/dist/components/EntityCollectionTable/internal/popup_field/useDraggable.d.ts +2 -2
  10. package/dist/components/EntityCollectionView/Board.d.ts +2 -0
  11. package/dist/components/EntityCollectionView/BoardColumn.d.ts +42 -0
  12. package/dist/components/EntityCollectionView/BoardColumnTitle.d.ts +9 -0
  13. package/dist/components/EntityCollectionView/BoardSortableList.d.ts +14 -0
  14. package/dist/components/EntityCollectionView/CollectionDataErrorBanner.d.ts +4 -0
  15. package/dist/components/EntityCollectionView/EntityBoardCard.d.ts +26 -0
  16. package/dist/components/EntityCollectionView/EntityCard.d.ts +19 -0
  17. package/dist/components/EntityCollectionView/EntityCollectionBoardView.d.ts +20 -0
  18. package/dist/components/EntityCollectionView/EntityCollectionCardView.d.ts +31 -0
  19. package/dist/components/EntityCollectionView/EntityCollectionViewActions.d.ts +2 -2
  20. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +7 -3
  21. package/dist/components/EntityCollectionView/FiltersDialog.d.ts +14 -0
  22. package/dist/components/EntityCollectionView/ViewModeToggle.d.ts +44 -0
  23. package/dist/components/EntityCollectionView/board_types.d.ts +105 -0
  24. package/dist/components/EntityCollectionView/useBoardDataController.d.ts +60 -0
  25. package/dist/components/ErrorBoundary.d.ts +4 -2
  26. package/dist/components/HomePage/DefaultHomePage.d.ts +0 -1
  27. package/dist/components/LanguageToggle.d.ts +1 -0
  28. package/dist/components/SelectableTable/SelectableTable.d.ts +5 -1
  29. package/dist/components/SelectableTable/filters/DateTimeFilterField.d.ts +2 -1
  30. package/dist/components/UnsavedChangesDialog.d.ts +1 -0
  31. package/dist/components/VirtualTable/VirtualTableCell.d.ts +6 -0
  32. package/dist/components/VirtualTable/VirtualTableHeader.d.ts +4 -1
  33. package/dist/components/VirtualTable/VirtualTableHeaderRow.d.ts +1 -1
  34. package/dist/components/VirtualTable/VirtualTableProps.d.ts +17 -1
  35. package/dist/components/VirtualTable/fields/VirtualTableDateField.d.ts +1 -0
  36. package/dist/components/VirtualTable/types.d.ts +3 -0
  37. package/dist/components/index.d.ts +4 -0
  38. package/dist/contexts/index.d.ts +10 -0
  39. package/dist/core/DrawerNavigationGroup.d.ts +45 -0
  40. package/dist/core/index.d.ts +1 -0
  41. package/dist/editor/components/SlashCommandMenu.d.ts +6 -0
  42. package/dist/editor/components/editor-bubble-item.d.ts +8 -0
  43. package/dist/editor/components/editor-bubble.d.ts +8 -0
  44. package/dist/editor/components/image-bubble.d.ts +5 -0
  45. package/dist/editor/components/index.d.ts +16 -0
  46. package/dist/editor/components/table-bubble.d.ts +5 -0
  47. package/dist/editor/editor.d.ts +30 -0
  48. package/dist/editor/extensions/HighlightDecorationExtension.d.ts +24 -0
  49. package/dist/editor/extensions/Image/index.d.ts +6 -0
  50. package/dist/editor/extensions/Image.d.ts +6 -0
  51. package/dist/editor/extensions/TextLoadingDecorationExtension.d.ts +16 -0
  52. package/dist/editor/extensions/clipboard.d.ts +7 -0
  53. package/dist/editor/extensions/custom-keymap.d.ts +1 -0
  54. package/dist/editor/extensions/drag-and-drop.d.ts +9 -0
  55. package/dist/editor/hooks/useProseMirror.d.ts +13 -0
  56. package/dist/editor/hooks/useProseMirrorContext.d.ts +9 -0
  57. package/dist/editor/index.d.ts +2 -0
  58. package/dist/editor/markdown.d.ts +5 -0
  59. package/dist/editor/nodeViews/ImageComponent.d.ts +3 -0
  60. package/dist/editor/nodeViews/ReactNodeView.d.ts +29 -0
  61. package/dist/editor/nodeViews/TaskItemComponent.d.ts +3 -0
  62. package/dist/editor/nodeViews/index.d.ts +6 -0
  63. package/dist/editor/plugins/index.d.ts +2 -0
  64. package/dist/editor/plugins/inputrules.d.ts +6 -0
  65. package/dist/editor/plugins/placeholderPlugin.d.ts +3 -0
  66. package/dist/editor/plugins/slashCommandPlugin.d.ts +12 -0
  67. package/dist/editor/schema.d.ts +2 -0
  68. package/dist/editor/selectors/ai-selector.d.ts +0 -0
  69. package/dist/editor/selectors/color-selector.d.ts +10 -0
  70. package/dist/editor/selectors/link-selector.d.ts +8 -0
  71. package/dist/editor/selectors/node-selector.d.ts +15 -0
  72. package/dist/editor/selectors/text-buttons.d.ts +1 -0
  73. package/dist/editor/types.d.ts +5 -0
  74. package/dist/editor/useProseMirror.d.ts +16 -0
  75. package/dist/editor/utils/prosemirror-utils.d.ts +6 -0
  76. package/dist/editor/utils/remove_classes.d.ts +1 -0
  77. package/dist/editor/utils/useDebouncedCallback.d.ts +1 -0
  78. package/dist/form/components/ErrorFocus.d.ts +1 -1
  79. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  80. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
  81. package/dist/form/validation.d.ts +3 -2
  82. package/dist/hooks/index.d.ts +1 -0
  83. package/dist/hooks/useBreadcrumbsController.d.ts +16 -0
  84. package/dist/hooks/useBuildNavigationController.d.ts +0 -1
  85. package/dist/hooks/useCollapsedGroups.d.ts +6 -3
  86. package/dist/hooks/useTranslation.d.ts +17 -0
  87. package/dist/i18n/FireCMSi18nProvider.d.ts +33 -0
  88. package/dist/index.d.ts +5 -0
  89. package/dist/index.es.js +30146 -15178
  90. package/dist/index.es.js.map +1 -1
  91. package/dist/index.umd.js +30032 -15085
  92. package/dist/index.umd.js.map +1 -1
  93. package/dist/internal/useRestoreScroll.d.ts +1 -1
  94. package/dist/locales/de.d.ts +2 -0
  95. package/dist/locales/en.d.ts +10 -0
  96. package/dist/locales/es.d.ts +10 -0
  97. package/dist/locales/fr.d.ts +2 -0
  98. package/dist/locales/hi.d.ts +2 -0
  99. package/dist/locales/it.d.ts +2 -0
  100. package/dist/locales/pt.d.ts +7 -0
  101. package/dist/preview/PropertyPreviewProps.d.ts +5 -0
  102. package/dist/preview/components/DatePreview.d.ts +13 -3
  103. package/dist/preview/components/ImagePreview.d.ts +5 -1
  104. package/dist/preview/components/StorageThumbnail.d.ts +2 -1
  105. package/dist/preview/components/UrlComponentPreview.d.ts +2 -1
  106. package/dist/preview/property_previews/ArrayOfStorageComponentsPreview.d.ts +1 -1
  107. package/dist/preview/property_previews/ArrayOfStringsPreview.d.ts +1 -1
  108. package/dist/preview/property_previews/SkeletonPropertyComponent.d.ts +1 -1
  109. package/dist/types/analytics.d.ts +1 -1
  110. package/dist/types/collections.d.ts +88 -2
  111. package/dist/types/customization_controller.d.ts +2 -1
  112. package/dist/types/datasource.d.ts +0 -1
  113. package/dist/types/firecms.d.ts +2 -1
  114. package/dist/types/index.d.ts +1 -0
  115. package/dist/types/navigation.d.ts +2 -2
  116. package/dist/types/plugins.d.ts +69 -1
  117. package/dist/types/properties.d.ts +268 -12
  118. package/dist/types/storage.d.ts +1 -0
  119. package/dist/types/translations.d.ts +669 -0
  120. package/dist/util/__tests__/conditions.test.d.ts +1 -0
  121. package/dist/util/__tests__/objects.test.d.ts +1 -0
  122. package/dist/util/conditions.d.ts +26 -0
  123. package/dist/util/entities.d.ts +2 -3
  124. package/dist/util/index.d.ts +3 -1
  125. package/dist/util/lazy_eager.d.ts +7 -0
  126. package/dist/util/objects.d.ts +1 -0
  127. package/dist/util/property_utils.d.ts +2 -1
  128. package/dist/util/resolutions.d.ts +3 -3
  129. package/dist/util/useStorageUploadController.d.ts +10 -1
  130. package/package.json +51 -12
  131. package/src/app/Scaffold.tsx +20 -19
  132. package/src/components/AIIcon.tsx +41 -0
  133. package/src/components/ArrayContainer.tsx +7 -8
  134. package/src/components/ClearFilterSortButton.tsx +25 -19
  135. package/src/components/ConfirmationDialog.tsx +4 -4
  136. package/src/components/DeleteEntityDialog.tsx +12 -11
  137. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +82 -43
  138. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +130 -79
  139. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +121 -104
  140. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +132 -103
  141. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +6 -3
  142. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +24 -44
  143. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +90 -49
  144. package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +1 -1
  145. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +3 -2
  146. package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +11 -11
  147. package/src/components/EntityCollectionView/Board.tsx +324 -0
  148. package/src/components/EntityCollectionView/BoardColumn.tsx +158 -0
  149. package/src/components/EntityCollectionView/BoardColumnTitle.tsx +45 -0
  150. package/src/components/EntityCollectionView/BoardSortableList.tsx +174 -0
  151. package/src/components/EntityCollectionView/CollectionDataErrorBanner.tsx +43 -0
  152. package/src/components/EntityCollectionView/EntityBoardCard.tsx +212 -0
  153. package/src/components/EntityCollectionView/EntityCard.tsx +235 -0
  154. package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +706 -0
  155. package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +236 -0
  156. package/src/components/EntityCollectionView/EntityCollectionView.tsx +531 -209
  157. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +35 -22
  158. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +86 -15
  159. package/src/components/EntityCollectionView/FiltersDialog.tsx +252 -0
  160. package/src/components/EntityCollectionView/ViewModeToggle.tsx +202 -0
  161. package/src/components/EntityCollectionView/board_types.ts +113 -0
  162. package/src/components/EntityCollectionView/useBoardDataController.tsx +490 -0
  163. package/src/components/EntityJsonPreview.tsx +2 -1
  164. package/src/components/EntityView.tsx +3 -2
  165. package/src/components/ErrorBoundary.tsx +27 -15
  166. package/src/components/ErrorTooltip.tsx +2 -1
  167. package/src/components/HomePage/DefaultHomePage.tsx +65 -22
  168. package/src/components/HomePage/HomePageDnD.tsx +59 -42
  169. package/src/components/HomePage/NavigationCard.tsx +20 -18
  170. package/src/components/HomePage/NavigationGroup.tsx +20 -17
  171. package/src/components/HomePage/RenameGroupDialog.tsx +15 -15
  172. package/src/components/HomePage/SmallNavigationCard.tsx +10 -9
  173. package/src/components/LanguageToggle.tsx +66 -0
  174. package/src/components/NotFoundPage.tsx +5 -3
  175. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +12 -17
  176. package/src/components/ReferenceWidget.tsx +5 -6
  177. package/src/components/SearchIconsView.tsx +3 -1
  178. package/src/components/SelectableTable/SelectableTable.tsx +75 -67
  179. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +7 -6
  180. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +50 -40
  181. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +53 -40
  182. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +60 -58
  183. package/src/components/UnsavedChangesDialog.tsx +6 -6
  184. package/src/components/UserDisplay.tsx +4 -4
  185. package/src/components/VirtualTable/VirtualTable.performance.test.tsx +1 -0
  186. package/src/components/VirtualTable/VirtualTable.tsx +275 -119
  187. package/src/components/VirtualTable/VirtualTableCell.tsx +18 -2
  188. package/src/components/VirtualTable/VirtualTableHeader.tsx +76 -64
  189. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +163 -42
  190. package/src/components/VirtualTable/VirtualTableProps.tsx +21 -2
  191. package/src/components/VirtualTable/VirtualTableRow.tsx +1 -1
  192. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +3 -0
  193. package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +19 -6
  194. package/src/components/VirtualTable/types.tsx +3 -0
  195. package/src/components/common/default_entity_actions.tsx +4 -0
  196. package/src/components/common/useColumnsIds.tsx +95 -3
  197. package/src/components/common/useDataSourceTableController.tsx +12 -4
  198. package/src/components/index.tsx +5 -0
  199. package/src/contexts/BreacrumbsContext.tsx +15 -8
  200. package/src/contexts/index.ts +10 -0
  201. package/src/core/DefaultAppBar.tsx +49 -32
  202. package/src/core/DefaultDrawer.tsx +49 -57
  203. package/src/core/DrawerNavigationGroup.tsx +120 -0
  204. package/src/core/DrawerNavigationItem.tsx +4 -3
  205. package/src/core/EntityEditView.tsx +94 -50
  206. package/src/core/EntityEditViewFormActions.tsx +24 -17
  207. package/src/core/EntitySidePanel.tsx +34 -30
  208. package/src/core/FireCMS.tsx +33 -6
  209. package/src/core/SideDialogs.tsx +4 -2
  210. package/src/core/field_configs.tsx +18 -11
  211. package/src/core/index.tsx +1 -0
  212. package/src/editor/components/SlashCommandMenu.tsx +516 -0
  213. package/src/editor/components/editor-bubble-item.tsx +32 -0
  214. package/src/editor/components/editor-bubble.tsx +118 -0
  215. package/src/editor/components/image-bubble.tsx +156 -0
  216. package/src/editor/components/index.ts +14 -0
  217. package/src/editor/components/table-bubble.tsx +165 -0
  218. package/src/editor/editor.tsx +455 -0
  219. package/src/editor/extensions/HighlightDecorationExtension.ts +114 -0
  220. package/src/editor/extensions/Image/index.ts +133 -0
  221. package/src/editor/extensions/Image.ts +159 -0
  222. package/src/editor/extensions/TextLoadingDecorationExtension.tsx +107 -0
  223. package/src/editor/extensions/clipboard.ts +72 -0
  224. package/src/editor/extensions/custom-keymap.ts +24 -0
  225. package/src/editor/extensions/drag-and-drop.tsx +480 -0
  226. package/src/editor/hooks/useProseMirror.ts +124 -0
  227. package/src/editor/hooks/useProseMirrorContext.ts +15 -0
  228. package/src/editor/index.ts +2 -0
  229. package/src/editor/markdown.ts +172 -0
  230. package/src/editor/nodeViews/ImageComponent.tsx +20 -0
  231. package/src/editor/nodeViews/ReactNodeView.tsx +89 -0
  232. package/src/editor/nodeViews/TaskItemComponent.tsx +29 -0
  233. package/src/editor/nodeViews/index.ts +35 -0
  234. package/src/editor/plugins/index.ts +58 -0
  235. package/src/editor/plugins/inputrules.ts +82 -0
  236. package/src/editor/plugins/placeholderPlugin.ts +55 -0
  237. package/src/editor/plugins/slashCommandPlugin.ts +61 -0
  238. package/src/editor/schema.ts +240 -0
  239. package/src/editor/selectors/ai-selector.tsx +111 -0
  240. package/src/editor/selectors/color-selector.tsx +200 -0
  241. package/src/editor/selectors/link-selector.tsx +118 -0
  242. package/src/editor/selectors/node-selector.tsx +157 -0
  243. package/src/editor/selectors/text-buttons.tsx +86 -0
  244. package/src/editor/types.ts +6 -0
  245. package/src/editor/useProseMirror.ts +126 -0
  246. package/src/editor/utils/prosemirror-utils.ts +108 -0
  247. package/src/editor/utils/remove_classes.ts +17 -0
  248. package/src/editor/utils/useDebouncedCallback.ts +25 -0
  249. package/src/form/EntityForm.tsx +149 -67
  250. package/src/form/EntityFormActions.tsx +19 -12
  251. package/src/form/PropertyFieldBinding.tsx +68 -51
  252. package/src/form/components/ErrorFocus.tsx +3 -3
  253. package/src/form/components/LocalChangesMenu.tsx +13 -13
  254. package/src/form/components/StorageItemPreview.tsx +5 -3
  255. package/src/form/components/StorageUploadProgress.tsx +18 -3
  256. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +18 -5
  257. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +22 -10
  258. package/src/form/field_bindings/BlockFieldBinding.tsx +26 -9
  259. package/src/form/field_bindings/DateTimeFieldBinding.tsx +18 -17
  260. package/src/form/field_bindings/KeyValueFieldBinding.tsx +46 -25
  261. package/src/form/field_bindings/MapFieldBinding.tsx +88 -70
  262. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +93 -52
  263. package/src/form/field_bindings/MultiSelectFieldBinding.tsx +15 -1
  264. package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +25 -11
  265. package/src/form/field_bindings/ReferenceFieldBinding.tsx +25 -11
  266. package/src/form/field_bindings/RepeatFieldBinding.tsx +21 -6
  267. package/src/form/field_bindings/SelectFieldBinding.tsx +7 -5
  268. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +110 -92
  269. package/src/form/field_bindings/SwitchFieldBinding.tsx +31 -14
  270. package/src/form/field_bindings/TextFieldBinding.tsx +77 -38
  271. package/src/form/field_bindings/UserSelectFieldBinding.tsx +7 -5
  272. package/src/form/validation.ts +245 -160
  273. package/src/hooks/index.tsx +1 -0
  274. package/src/hooks/useBreadcrumbsController.tsx +18 -0
  275. package/src/hooks/useBuildNavigationController.tsx +91 -41
  276. package/src/hooks/useCollapsedGroups.ts +18 -9
  277. package/src/hooks/useTranslation.ts +31 -0
  278. package/src/hooks/useValidateAuthenticator.tsx +1 -1
  279. package/src/i18n/FireCMSi18nProvider.tsx +160 -0
  280. package/src/index.ts +5 -0
  281. package/src/internal/useBuildDataSource.ts +68 -34
  282. package/src/internal/useBuildSideDialogsController.tsx +11 -8
  283. package/src/internal/useBuildSideEntityController.tsx +24 -24
  284. package/src/internal/useRestoreScroll.tsx +26 -14
  285. package/src/locales/de.ts +718 -0
  286. package/src/locales/en.ts +730 -0
  287. package/src/locales/es.ts +730 -0
  288. package/src/locales/fr.ts +718 -0
  289. package/src/locales/hi.ts +718 -0
  290. package/src/locales/it.ts +718 -0
  291. package/src/locales/pt.ts +727 -0
  292. package/src/preview/PropertyPreview.tsx +43 -33
  293. package/src/preview/PropertyPreviewProps.tsx +6 -0
  294. package/src/preview/components/DatePreview.tsx +72 -4
  295. package/src/preview/components/EmptyValue.tsx +1 -1
  296. package/src/preview/components/ImagePreview.tsx +37 -21
  297. package/src/preview/components/ReferencePreview.tsx +2 -1
  298. package/src/preview/components/StorageThumbnail.tsx +16 -12
  299. package/src/preview/components/UrlComponentPreview.tsx +32 -27
  300. package/src/preview/components/UserPreview.tsx +3 -1
  301. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +9 -7
  302. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +11 -9
  303. package/src/preview/property_previews/ArrayPropertyPreview.tsx +26 -24
  304. package/src/preview/property_previews/MapPropertyPreview.tsx +49 -27
  305. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +61 -56
  306. package/src/routes/CustomCMSRoute.tsx +1 -0
  307. package/src/routes/FireCMSRoute.tsx +87 -65
  308. package/src/types/analytics.ts +10 -0
  309. package/src/types/collections.ts +97 -3
  310. package/src/types/customization_controller.tsx +2 -1
  311. package/src/types/datasource.ts +54 -56
  312. package/src/types/firecms.tsx +2 -1
  313. package/src/types/index.ts +1 -0
  314. package/src/types/navigation.ts +2 -2
  315. package/src/types/plugins.tsx +77 -1
  316. package/src/types/properties.ts +359 -37
  317. package/src/types/storage.ts +2 -1
  318. package/src/types/translations.ts +752 -0
  319. package/src/util/__tests__/conditions.test.ts +506 -0
  320. package/src/util/__tests__/objects.test.ts +196 -0
  321. package/src/util/callbacks.ts +6 -3
  322. package/src/util/collections.ts +51 -6
  323. package/src/util/conditions.ts +339 -0
  324. package/src/util/entities.ts +29 -30
  325. package/src/util/entity_cache.ts +2 -1
  326. package/src/util/index.ts +3 -1
  327. package/src/util/join_collections.ts +10 -8
  328. package/src/util/lazy_eager.tsx +33 -0
  329. package/src/util/objects.ts +46 -13
  330. package/src/util/{references.ts → previews.ts} +16 -2
  331. package/src/util/property_utils.tsx +37 -11
  332. package/src/util/resolutions.ts +62 -58
  333. package/src/util/useStorageUploadController.tsx +23 -29
  334. /package/dist/util/{references.d.ts → previews.d.ts} +0 -0
@@ -0,0 +1,506 @@
1
+ import {
2
+ applyPropertyConditions,
3
+ buildConditionContext,
4
+ evaluateCondition,
5
+ registerConditionOperations
6
+ } from "../conditions";
7
+ import { ConditionContext, ResolvedProperty } from "../../types";
8
+
9
+ describe("Property Conditions", () => {
10
+
11
+ beforeAll(() => {
12
+ registerConditionOperations();
13
+ });
14
+
15
+ describe("evaluateCondition", () => {
16
+
17
+ it("should evaluate simple equality", () => {
18
+ const context: ConditionContext = {
19
+ values: { status: "archived" },
20
+ previousValues: {},
21
+ propertyValue: undefined,
22
+ path: "products",
23
+ entityId: "123",
24
+ isNew: false,
25
+ user: { uid: "user1", email: null, displayName: null, photoURL: null, roles: [] },
26
+ now: Date.now()
27
+ };
28
+
29
+ const rule = { "==": [{ "var": "values.status" }, "archived"] };
30
+ expect(evaluateCondition(rule, context)).toBe(true);
31
+ });
32
+
33
+ it("should evaluate var access to nested values", () => {
34
+ const context: ConditionContext = {
35
+ values: { shipping: { method: "pickup" } },
36
+ previousValues: {},
37
+ propertyValue: undefined,
38
+ path: "orders",
39
+ isNew: false,
40
+ user: { uid: "user1", email: null, displayName: null, photoURL: null, roles: [] },
41
+ now: Date.now()
42
+ };
43
+
44
+ const rule = { "==": [{ "var": "values.shipping.method" }, "pickup"] };
45
+ expect(evaluateCondition(rule, context)).toBe(true);
46
+ });
47
+
48
+ it("should check isNew status", () => {
49
+ const contextNew: ConditionContext = {
50
+ values: {},
51
+ previousValues: {},
52
+ propertyValue: undefined,
53
+ path: "products",
54
+ isNew: true,
55
+ user: { uid: "user1", email: null, displayName: null, photoURL: null, roles: [] },
56
+ now: Date.now()
57
+ };
58
+
59
+ const rule = { "var": "isNew" };
60
+ expect(evaluateCondition(rule, contextNew)).toBe(true);
61
+
62
+ const contextExisting: ConditionContext = {
63
+ ...contextNew,
64
+ entityId: "123",
65
+ isNew: false
66
+ };
67
+ expect(evaluateCondition(rule, contextExisting)).toBe(false);
68
+ });
69
+
70
+ it("should handle if/then/else with object values (Firestore workaround)", () => {
71
+ const context: ConditionContext = {
72
+ values: { status: "active" },
73
+ previousValues: {},
74
+ propertyValue: undefined,
75
+ path: "products",
76
+ isNew: false,
77
+ user: { uid: "user1", email: null, displayName: null, photoURL: null, roles: [] },
78
+ now: Date.now()
79
+ };
80
+
81
+ // Firestore stores arrays as objects like {"0": "a", "1": "b"}
82
+ const rule = {
83
+ "if": [
84
+ { "==": [{ "var": "values.status" }, "active"] },
85
+ { "0": "electronics", "1": "clothing" },
86
+ { "0": "electronics", "1": "clothing", "2": "food" }
87
+ ]
88
+ };
89
+
90
+ const result = evaluateCondition(rule, context);
91
+ expect(result).toEqual({ "0": "electronics", "1": "clothing" });
92
+ });
93
+
94
+ it("should evaluate truthy operator (!!)", () => {
95
+ const context: ConditionContext = {
96
+ values: { name: "Product", emptyField: "" },
97
+ previousValues: {},
98
+ propertyValue: undefined,
99
+ path: "products",
100
+ isNew: false,
101
+ user: { uid: "user1", email: null, displayName: null, photoURL: null, roles: [] },
102
+ now: Date.now()
103
+ };
104
+
105
+ expect(evaluateCondition({ "!!": { "var": "values.name" } }, context)).toBe(true);
106
+ expect(evaluateCondition({ "!!": { "var": "values.emptyField" } }, context)).toBe(false);
107
+ });
108
+
109
+ it("should evaluate falsy operator (!)", () => {
110
+ const context: ConditionContext = {
111
+ values: { name: "Product", emptyField: "" },
112
+ previousValues: {},
113
+ propertyValue: undefined,
114
+ path: "products",
115
+ isNew: false,
116
+ user: { uid: "user1", email: null, displayName: null, photoURL: null, roles: [] },
117
+ now: Date.now()
118
+ };
119
+
120
+ expect(evaluateCondition({ "!": { "var": "values.name" } }, context)).toBe(false);
121
+ expect(evaluateCondition({ "!": { "var": "values.emptyField" } }, context)).toBe(true);
122
+ });
123
+
124
+ it("should evaluate greater than operator", () => {
125
+ const context: ConditionContext = {
126
+ values: { price: 100 },
127
+ previousValues: {},
128
+ propertyValue: undefined,
129
+ path: "products",
130
+ isNew: false,
131
+ user: { uid: "user1", email: null, displayName: null, photoURL: null, roles: [] },
132
+ now: Date.now()
133
+ };
134
+
135
+ expect(evaluateCondition({ ">": [{ "var": "values.price" }, 50] }, context)).toBe(true);
136
+ expect(evaluateCondition({ ">": [{ "var": "values.price" }, 100] }, context)).toBe(false);
137
+ });
138
+
139
+ it("should evaluate in operator with array", () => {
140
+ const context: ConditionContext = {
141
+ values: { category: "electronics" },
142
+ previousValues: {},
143
+ propertyValue: undefined,
144
+ path: "products",
145
+ isNew: false,
146
+ user: { uid: "user1", email: null, displayName: null, photoURL: null, roles: [] },
147
+ now: Date.now()
148
+ };
149
+
150
+ expect(evaluateCondition({ "in": [{ "var": "values.category" }, ["electronics", "clothing"]] }, context)).toBe(true);
151
+ expect(evaluateCondition({ "in": [{ "var": "values.category" }, ["food", "toys"]] }, context)).toBe(false);
152
+ });
153
+ });
154
+
155
+ describe("Custom operations", () => {
156
+
157
+ it("isPast should check if timestamp is in the past", () => {
158
+ const context: ConditionContext = {
159
+ values: {},
160
+ previousValues: {},
161
+ propertyValue: undefined,
162
+ path: "products",
163
+ isNew: false,
164
+ user: { uid: "user1", email: null, displayName: null, photoURL: null, roles: [] },
165
+ now: Date.now()
166
+ };
167
+
168
+ const pastTimestamp = Date.now() - 86400000;
169
+ const futureTimestamp = Date.now() + 86400000;
170
+
171
+ expect(evaluateCondition({ "isPast": pastTimestamp }, context)).toBe(true);
172
+ expect(evaluateCondition({ "isPast": futureTimestamp }, context)).toBe(false);
173
+ });
174
+
175
+ it("isFuture should check if timestamp is in the future", () => {
176
+ const context: ConditionContext = {
177
+ values: {},
178
+ previousValues: {},
179
+ propertyValue: undefined,
180
+ path: "products",
181
+ isNew: false,
182
+ user: { uid: "user1", email: null, displayName: null, photoURL: null, roles: [] },
183
+ now: Date.now()
184
+ };
185
+
186
+ const pastTimestamp = Date.now() - 86400000;
187
+ const futureTimestamp = Date.now() + 86400000;
188
+
189
+ expect(evaluateCondition({ "isFuture": futureTimestamp }, context)).toBe(true);
190
+ expect(evaluateCondition({ "isFuture": pastTimestamp }, context)).toBe(false);
191
+ });
192
+ });
193
+
194
+ describe("applyPropertyConditions", () => {
195
+
196
+ const baseContext: ConditionContext = {
197
+ values: { status: "archived" },
198
+ previousValues: {},
199
+ propertyValue: undefined,
200
+ path: "products",
201
+ entityId: "123",
202
+ isNew: false,
203
+ user: { uid: "user1", email: null, displayName: null, photoURL: null, roles: ["admin"] },
204
+ now: Date.now()
205
+ };
206
+
207
+ it("should apply disabled condition", () => {
208
+ const property = {
209
+ dataType: "string",
210
+ name: "Title",
211
+ resolved: true,
212
+ fromBuilder: false,
213
+ conditions: {
214
+ disabled: { "==": [{ "var": "values.status" }, "archived"] },
215
+ disabledMessage: "Cannot edit archived items"
216
+ }
217
+ } as ResolvedProperty<string>;
218
+
219
+ const result = applyPropertyConditions(property, baseContext);
220
+
221
+ expect(result.disabled).toEqual({
222
+ clearOnDisabled: false,
223
+ disabledMessage: "Cannot edit archived items",
224
+ hidden: false
225
+ });
226
+ });
227
+
228
+ it("should apply hidden condition", () => {
229
+ const property = {
230
+ dataType: "string",
231
+ name: "Internal Notes",
232
+ resolved: true,
233
+ fromBuilder: false,
234
+ conditions: {
235
+ hidden: { "==": [{ "var": "values.status" }, "archived"] }
236
+ }
237
+ } as ResolvedProperty<string>;
238
+
239
+ const result = applyPropertyConditions(property, baseContext);
240
+
241
+ expect(result.disabled).toEqual(expect.objectContaining({
242
+ hidden: true
243
+ }));
244
+ });
245
+
246
+ it("should apply required condition", () => {
247
+ const property = {
248
+ dataType: "string",
249
+ name: "Email",
250
+ resolved: true,
251
+ fromBuilder: false,
252
+ conditions: {
253
+ required: { "!!": { "var": "values.status" } }
254
+ }
255
+ } as ResolvedProperty<string>;
256
+
257
+ const result = applyPropertyConditions(property, baseContext);
258
+
259
+ expect(result.validation?.required).toBe(true);
260
+ });
261
+
262
+ it("should not apply disabled when condition is false", () => {
263
+ const property = {
264
+ dataType: "string",
265
+ name: "Title",
266
+ resolved: true,
267
+ fromBuilder: false,
268
+ conditions: {
269
+ disabled: { "==": [{ "var": "values.status" }, "draft"] }
270
+ }
271
+ } as ResolvedProperty<string>;
272
+
273
+ const result = applyPropertyConditions(property, baseContext);
274
+
275
+ expect(result.disabled).toBeUndefined();
276
+ });
277
+
278
+ it("should apply enum conditions to filter values", () => {
279
+ const property = {
280
+ dataType: "string",
281
+ name: "Category",
282
+ resolved: true,
283
+ fromBuilder: false,
284
+ enumValues: [
285
+ { id: "electronics", label: "Electronics" },
286
+ { id: "clothing", label: "Clothing" },
287
+ { id: "food", label: "Food" }
288
+ ],
289
+ conditions: {
290
+ allowedEnumValues: ["electronics", "clothing"]
291
+ }
292
+ } as any;
293
+
294
+ const result = applyPropertyConditions(property, baseContext) as any;
295
+
296
+ expect(result.enumValues).toHaveLength(2);
297
+ expect(result.enumValues.map((e: any) => e.id)).toEqual(["electronics", "clothing"]);
298
+ });
299
+
300
+ it("should apply enum conditions with object format (Firestore workaround)", () => {
301
+ const property = {
302
+ dataType: "string",
303
+ name: "Category",
304
+ resolved: true,
305
+ fromBuilder: false,
306
+ enumValues: [
307
+ { id: "electronics", label: "Electronics" },
308
+ { id: "clothing", label: "Clothing" },
309
+ { id: "food", label: "Food" }
310
+ ],
311
+ conditions: {
312
+ allowedEnumValues: {
313
+ "if": [
314
+ { "!!": { "var": "values.status" } },
315
+ { "0": "electronics", "1": "clothing" },
316
+ { "0": "electronics", "1": "clothing", "2": "food" }
317
+ ]
318
+ }
319
+ }
320
+ } as any;
321
+
322
+ const result = applyPropertyConditions(property, baseContext) as any;
323
+
324
+ expect(result.enumValues).toHaveLength(2);
325
+ expect(result.enumValues.map((e: any) => e.id)).toEqual(["electronics", "clothing"]);
326
+ });
327
+
328
+ it("should apply excludedEnumValues to remove specific values", () => {
329
+ const property = {
330
+ dataType: "string",
331
+ name: "Status",
332
+ resolved: true,
333
+ fromBuilder: false,
334
+ enumValues: [
335
+ { id: "draft", label: "Draft" },
336
+ { id: "published", label: "Published" },
337
+ { id: "archived", label: "Archived" }
338
+ ],
339
+ conditions: {
340
+ // Simple array of excluded values
341
+ excludedEnumValues: ["published"]
342
+ }
343
+ } as any;
344
+
345
+ const result = applyPropertyConditions(property, baseContext) as any;
346
+
347
+ expect(result.enumValues).toHaveLength(2);
348
+ expect(result.enumValues.map((e: any) => e.id)).toEqual(["draft", "archived"]);
349
+ });
350
+
351
+ it("should apply enum conditions to disable specific values", () => {
352
+ const property = {
353
+ dataType: "string",
354
+ name: "Status",
355
+ resolved: true,
356
+ fromBuilder: false,
357
+ enumValues: [
358
+ { id: "draft", label: "Draft" },
359
+ { id: "published", label: "Published" },
360
+ { id: "archived", label: "Archived" }
361
+ ],
362
+ conditions: {
363
+ enumConditions: {
364
+ archived: {
365
+ disabled: { "!=": [{ "var": "values.status" }, "archived"] }
366
+ }
367
+ }
368
+ }
369
+ } as any;
370
+
371
+ const result = applyPropertyConditions(property, baseContext) as any;
372
+ const archivedOption = result.enumValues.find((e: any) => e.id === "archived");
373
+ expect(archivedOption.disabled).toBeFalsy();
374
+
375
+ const contextDraft = { ...baseContext, values: { status: "draft" } };
376
+ const resultDraft = applyPropertyConditions(property, contextDraft) as any;
377
+ const archivedOptionDraft = resultDraft.enumValues.find((e: any) => e.id === "archived");
378
+ expect(archivedOptionDraft.disabled).toBe(true);
379
+ });
380
+
381
+ it("should handle multiple conditions together", () => {
382
+ const property = {
383
+ dataType: "string",
384
+ name: "Notes",
385
+ resolved: true,
386
+ fromBuilder: false,
387
+ conditions: {
388
+ disabled: { "==": [{ "var": "values.status" }, "archived"] },
389
+ required: { "==": [{ "var": "values.status" }, "published"] },
390
+ disabledMessage: "Cannot edit notes on archived items"
391
+ }
392
+ } as ResolvedProperty<string>;
393
+
394
+ const resultArchived = applyPropertyConditions(property, baseContext);
395
+ expect(resultArchived.disabled).toBeDefined();
396
+ expect(resultArchived.validation?.required).toBeFalsy();
397
+
398
+ const contextPublished = { ...baseContext, values: { status: "published" } };
399
+ const resultPublished = applyPropertyConditions(property, contextPublished);
400
+ expect(resultPublished.disabled).toBeUndefined();
401
+ expect(resultPublished.validation?.required).toBe(true);
402
+ });
403
+
404
+ it("should handle clearOnDisabled option", () => {
405
+ const property = {
406
+ dataType: "string",
407
+ name: "Title",
408
+ resolved: true,
409
+ fromBuilder: false,
410
+ conditions: {
411
+ disabled: { "==": [{ "var": "values.status" }, "archived"] },
412
+ clearOnDisabled: true
413
+ }
414
+ } as ResolvedProperty<string>;
415
+
416
+ const result = applyPropertyConditions(property, baseContext);
417
+
418
+ expect(result.disabled).toEqual(expect.objectContaining({
419
+ clearOnDisabled: true
420
+ }));
421
+ });
422
+ });
423
+
424
+ describe("buildConditionContext", () => {
425
+
426
+ it("should build context with serialized dates", () => {
427
+ const mockAuthController = {
428
+ user: {
429
+ uid: "user123",
430
+ email: "test@example.com",
431
+ displayName: "Test User",
432
+ photoURL: null,
433
+ providerId: "google.com",
434
+ isAnonymous: false,
435
+ roles: [{ id: "admin", name: "Admin" }, { id: "editor", name: "Editor" }]
436
+ }
437
+ };
438
+
439
+ const now = new Date();
440
+ const context = buildConditionContext({
441
+ propertyKey: "title",
442
+ values: { title: "Hello", createdAt: now },
443
+ path: "products",
444
+ entityId: "123",
445
+ authController: mockAuthController as any
446
+ });
447
+
448
+ expect(context.values.createdAt).toBe(now.getTime());
449
+ expect(context.user.uid).toBe("user123");
450
+ expect(context.user.roles).toEqual(["admin", "editor"]);
451
+ expect(context.isNew).toBe(false);
452
+ expect(context.entityId).toBe("123");
453
+ });
454
+
455
+ it("should mark isNew as true when no entityId", () => {
456
+ const mockAuthController = {
457
+ user: null
458
+ };
459
+
460
+ const context = buildConditionContext({
461
+ path: "products",
462
+ authController: mockAuthController as any
463
+ });
464
+
465
+ expect(context.isNew).toBe(true);
466
+ expect(context.entityId).toBeUndefined();
467
+ });
468
+
469
+ it("should handle user with Role object roles", () => {
470
+ const mockAuthController = {
471
+ user: {
472
+ uid: "user123",
473
+ email: "test@example.com",
474
+ displayName: "Test User",
475
+ photoURL: null,
476
+ roles: [{ id: "admin", name: "Admin" }, { id: "editor", name: "Editor" }]
477
+ }
478
+ };
479
+
480
+ const context = buildConditionContext({
481
+ path: "products",
482
+ entityId: "123",
483
+ authController: mockAuthController as any
484
+ });
485
+
486
+ // Roles are mapped from Role.id
487
+ expect(context.user.roles).toEqual(["admin", "editor"]);
488
+ });
489
+
490
+ it("should handle null user", () => {
491
+ const mockAuthController = {
492
+ user: null
493
+ };
494
+
495
+ const context = buildConditionContext({
496
+ path: "products",
497
+ authController: mockAuthController as any
498
+ });
499
+
500
+ expect(context.user).toBeDefined();
501
+ // uid defaults to empty string when user is null
502
+ expect(context.user.uid).toBe("");
503
+ expect(context.user.roles).toEqual([]);
504
+ });
505
+ });
506
+ });
@@ -0,0 +1,196 @@
1
+ import { removeFunctions, removeUndefined, isPlainObject } from "../objects";
2
+
3
+ describe("objects utilities", () => {
4
+
5
+ describe("removeFunctions", () => {
6
+
7
+ it("should remove functions from objects", () => {
8
+ const obj = {
9
+ name: "test",
10
+ value: 42,
11
+ callback: () => console.log("hi")
12
+ };
13
+
14
+ const result = removeFunctions(obj);
15
+
16
+ expect(result.name).toBe("test");
17
+ expect(result.value).toBe(42);
18
+ expect(result.callback).toBeUndefined();
19
+ });
20
+
21
+ it("should preserve arrays at top level", () => {
22
+ const arr = ["a", "b", "c"];
23
+ const result = removeFunctions(arr);
24
+
25
+ expect(Array.isArray(result)).toBe(true);
26
+ expect(result).toEqual(["a", "b", "c"]);
27
+ });
28
+
29
+ it("should preserve nested arrays", () => {
30
+ const obj = {
31
+ items: ["a", "b", "c"],
32
+ numbers: [1, 2, 3]
33
+ };
34
+
35
+ const result = removeFunctions(obj);
36
+
37
+ expect(Array.isArray(result.items)).toBe(true);
38
+ expect(result.items).toEqual(["a", "b", "c"]);
39
+ expect(Array.isArray(result.numbers)).toBe(true);
40
+ expect(result.numbers).toEqual([1, 2, 3]);
41
+ });
42
+
43
+ it("should NOT convert arrays to objects with numeric keys", () => {
44
+ const arr = ["first", "second", "third"];
45
+ const result = removeFunctions(arr);
46
+
47
+ // Key check: the result should be an Array, not a plain object
48
+ // If it were converted to an object, Array.isArray would return false
49
+ expect(Array.isArray(result)).toBe(true);
50
+ expect(result.length).toBe(3);
51
+ expect(result[0]).toBe("first");
52
+ expect(result[1]).toBe("second");
53
+ expect(result[2]).toBe("third");
54
+ });
55
+
56
+ it("should handle undefined", () => {
57
+ expect(removeFunctions(undefined)).toBeUndefined();
58
+ });
59
+
60
+ it("should handle null", () => {
61
+ expect(removeFunctions(null as any)).toBeNull();
62
+ });
63
+
64
+ it("should preserve class instances (not recurse into them)", () => {
65
+ class CustomClass {
66
+ value = 42;
67
+ getDouble() { return this.value * 2; }
68
+ }
69
+
70
+ const instance = new CustomClass();
71
+ const obj = { custom: instance };
72
+
73
+ const result = removeFunctions(obj);
74
+
75
+ // Class instances should be preserved as-is (not have functions stripped)
76
+ expect(result.custom).toBe(instance);
77
+ expect(result.custom.getDouble()).toBe(84);
78
+ });
79
+
80
+ it("should recursively remove functions from nested objects", () => {
81
+ const obj = {
82
+ level1: {
83
+ name: "nested",
84
+ fn: () => "should be removed",
85
+ level2: {
86
+ value: 100,
87
+ anotherFn: () => "also removed"
88
+ }
89
+ }
90
+ };
91
+
92
+ const result = removeFunctions(obj);
93
+
94
+ expect(result.level1.name).toBe("nested");
95
+ expect(result.level1.fn).toBeUndefined();
96
+ expect(result.level1.level2.value).toBe(100);
97
+ expect(result.level1.level2.anotherFn).toBeUndefined();
98
+ });
99
+
100
+ it("should handle arrays containing objects with functions", () => {
101
+ const arr = [
102
+ { name: "item1", fn: () => { } },
103
+ { name: "item2", fn: () => { } }
104
+ ];
105
+
106
+ const result = removeFunctions(arr);
107
+
108
+ expect(Array.isArray(result)).toBe(true);
109
+ expect(result[0].name).toBe("item1");
110
+ expect(result[0].fn).toBeUndefined();
111
+ expect(result[1].name).toBe("item2");
112
+ expect(result[1].fn).toBeUndefined();
113
+ });
114
+
115
+ it("should handle deeply nested arrays", () => {
116
+ const obj = {
117
+ conditions: {
118
+ allowedEnumValues: {
119
+ if: [
120
+ { "==": [{ var: "values.status" }, "active"] },
121
+ ["a", "b"],
122
+ ["a", "b", "c"]
123
+ ]
124
+ }
125
+ }
126
+ };
127
+
128
+ const result = removeFunctions(obj);
129
+
130
+ // Ensure arrays inside the if array are preserved as arrays
131
+ expect(Array.isArray(result.conditions.allowedEnumValues.if)).toBe(true);
132
+ expect(Array.isArray(result.conditions.allowedEnumValues.if[1])).toBe(true);
133
+ expect(result.conditions.allowedEnumValues.if[1]).toEqual(["a", "b"]);
134
+ });
135
+ });
136
+
137
+ describe("removeUndefined", () => {
138
+
139
+ it("should remove undefined values from objects", () => {
140
+ const obj = {
141
+ name: "test",
142
+ value: undefined,
143
+ count: 0
144
+ };
145
+
146
+ const result = removeUndefined(obj);
147
+
148
+ expect(result.name).toBe("test");
149
+ expect(result.count).toBe(0);
150
+ expect("value" in result).toBe(false);
151
+ });
152
+
153
+ it("should preserve arrays", () => {
154
+ const arr = ["a", "b", "c"];
155
+ const result = removeUndefined(arr);
156
+
157
+ expect(Array.isArray(result)).toBe(true);
158
+ expect(result).toEqual(["a", "b", "c"]);
159
+ });
160
+
161
+ it("should handle null values", () => {
162
+ const obj = { value: null };
163
+ const result = removeUndefined(obj);
164
+
165
+ expect(result.value).toBeNull();
166
+ });
167
+ });
168
+
169
+ describe("isPlainObject", () => {
170
+
171
+ it("should return true for plain objects", () => {
172
+ expect(isPlainObject({})).toBe(true);
173
+ expect(isPlainObject({ a: 1 })).toBe(true);
174
+ });
175
+
176
+ it("should return false for arrays", () => {
177
+ expect(isPlainObject([])).toBe(false);
178
+ expect(isPlainObject([1, 2, 3])).toBe(false);
179
+ });
180
+
181
+ it("should return false for null", () => {
182
+ expect(isPlainObject(null)).toBe(false);
183
+ });
184
+
185
+ it("should return false for primitives", () => {
186
+ expect(isPlainObject("string")).toBe(false);
187
+ expect(isPlainObject(42)).toBe(false);
188
+ expect(isPlainObject(true)).toBe(false);
189
+ });
190
+
191
+ it("should return false for class instances", () => {
192
+ class MyClass { }
193
+ expect(isPlainObject(new MyClass())).toBe(false);
194
+ });
195
+ });
196
+ });