@cosmicdrift/kumiko-renderer 0.328.0 → 0.328.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 (259) hide show
  1. package/dist/app/action-form-shim.d.ts +14 -0
  2. package/{src/app/action-form-shim.ts → dist/app/action-form-shim.js} +26 -46
  3. package/dist/app/app-features-context.d.ts +8 -0
  4. package/dist/app/app-features-context.js +16 -0
  5. package/dist/app/column-renderers.d.ts +30 -0
  6. package/dist/app/column-renderers.js +34 -0
  7. package/dist/app/config-edit-shim.d.ts +11 -0
  8. package/{src/app/config-edit-shim.ts → dist/app/config-edit-shim.js} +12 -25
  9. package/dist/app/content-editors.d.ts +38 -0
  10. package/dist/app/content-editors.js +26 -0
  11. package/dist/app/content-preview.d.ts +11 -0
  12. package/dist/app/content-preview.js +30 -0
  13. package/dist/app/custom-screens.d.ts +11 -0
  14. package/dist/app/custom-screens.js +20 -0
  15. package/dist/app/dashboard-body.d.ts +16 -0
  16. package/dist/app/dashboard-body.js +11 -0
  17. package/dist/app/extension-form-submit.d.ts +32 -0
  18. package/dist/app/extension-form-submit.js +73 -0
  19. package/dist/app/extension-sections.d.ts +72 -0
  20. package/dist/app/extension-sections.js +34 -0
  21. package/dist/app/feature-schema.d.ts +4 -0
  22. package/{src/app/feature-schema.ts → dist/app/feature-schema.js} +12 -22
  23. package/dist/app/form-schema.d.ts +4 -0
  24. package/dist/app/form-schema.js +85 -0
  25. package/dist/app/kumiko-screen.d.ts +43 -0
  26. package/dist/app/kumiko-screen.js +2206 -0
  27. package/dist/app/layout-fields.d.ts +3 -0
  28. package/dist/app/layout-fields.js +20 -0
  29. package/dist/app/list-facets.d.ts +48 -0
  30. package/dist/app/list-facets.js +185 -0
  31. package/dist/app/nav.d.ts +61 -0
  32. package/dist/app/nav.js +139 -0
  33. package/dist/app/projection-detail-shim.d.ts +8 -0
  34. package/dist/app/projection-detail-shim.js +71 -0
  35. package/dist/app/projection-list-shim.d.ts +13 -0
  36. package/{src/app/projection-list-shim.ts → dist/app/projection-list-shim.js} +26 -41
  37. package/dist/app/qn.d.ts +7 -0
  38. package/{src/app/qn.ts → dist/app/qn.js} +9 -11
  39. package/dist/app/qualify-screen-id.d.ts +2 -0
  40. package/{src/app/qualify-screen-id.ts → dist/app/qualify-screen-id.js} +5 -6
  41. package/dist/app/reference-facet-bridge.d.ts +10 -0
  42. package/dist/app/reference-facet-bridge.js +22 -0
  43. package/dist/app/return-to.d.ts +31 -0
  44. package/dist/app/return-to.js +188 -0
  45. package/dist/app/row-actions.d.ts +77 -0
  46. package/dist/app/row-actions.js +416 -0
  47. package/dist/app/screen-access.d.ts +8 -0
  48. package/dist/app/screen-access.js +48 -0
  49. package/dist/app/secret-mint-body.d.ts +10 -0
  50. package/dist/app/secret-mint-body.js +140 -0
  51. package/dist/app/secrets-edit-body.d.ts +8 -0
  52. package/dist/app/secrets-edit-body.js +76 -0
  53. package/dist/app/use-embedded-screen.d.ts +6 -0
  54. package/dist/app/use-embedded-screen.js +19 -0
  55. package/dist/app/variable-chips.d.ts +7 -0
  56. package/dist/app/variable-chips.js +16 -0
  57. package/dist/app/write-failed-error.d.ts +6 -0
  58. package/dist/app/write-failed-error.js +21 -0
  59. package/dist/components/embedded-list-field.d.ts +10 -0
  60. package/dist/components/embedded-list-field.js +259 -0
  61. package/dist/components/grid-cell-for-field.d.ts +25 -0
  62. package/dist/components/grid-cell-for-field.js +31 -0
  63. package/dist/components/multi-select-checkboxes.d.ts +14 -0
  64. package/dist/components/multi-select-checkboxes.js +36 -0
  65. package/dist/components/reference-create-dialog.d.ts +13 -0
  66. package/dist/components/reference-create-dialog.js +46 -0
  67. package/dist/components/related-list-section.d.ts +25 -0
  68. package/dist/components/related-list-section.js +273 -0
  69. package/dist/components/render-edit-action-button.d.ts +22 -0
  70. package/dist/components/render-edit-action-button.js +43 -0
  71. package/dist/components/render-edit-logic.d.ts +6 -0
  72. package/dist/components/render-edit-logic.js +63 -0
  73. package/dist/components/render-edit-types.d.ts +209 -0
  74. package/dist/components/render-edit-types.js +1 -0
  75. package/dist/components/render-edit.d.ts +6 -0
  76. package/dist/components/render-edit.js +1169 -0
  77. package/dist/components/render-field.d.ts +38 -0
  78. package/dist/components/render-field.js +544 -0
  79. package/dist/components/render-list.d.ts +117 -0
  80. package/dist/components/render-list.js +232 -0
  81. package/dist/components/write-form-section.d.ts +17 -0
  82. package/dist/components/write-form-section.js +104 -0
  83. package/dist/context/dispatcher-context.d.ts +10 -0
  84. package/{src/context/dispatcher-context.tsx → dist/context/dispatcher-context.js} +16 -29
  85. package/dist/context/draft-storage-context.d.ts +12 -0
  86. package/dist/context/draft-storage-context.js +18 -0
  87. package/dist/context/user-roles-context.d.ts +7 -0
  88. package/{src/context/user-roles-context.tsx → dist/context/user-roles-context.js} +7 -15
  89. package/dist/format-when.d.ts +1 -0
  90. package/{src/format-when.ts → dist/format-when.js} +7 -7
  91. package/dist/hooks/reference-limits.d.ts +3 -0
  92. package/{src/hooks/reference-limits.ts → dist/hooks/reference-limits.js} +0 -1
  93. package/dist/hooks/use-ai-text.d.ts +52 -0
  94. package/dist/hooks/use-ai-text.js +90 -0
  95. package/dist/hooks/use-disclosure.d.ts +7 -0
  96. package/dist/hooks/use-disclosure.js +8 -0
  97. package/dist/hooks/use-form.d.ts +7 -0
  98. package/dist/hooks/use-form.js +36 -0
  99. package/dist/hooks/use-list-url-state.d.ts +43 -0
  100. package/dist/hooks/use-list-url-state.js +131 -0
  101. package/dist/hooks/use-mutation.d.ts +9 -0
  102. package/dist/hooks/use-mutation.js +37 -0
  103. package/dist/hooks/use-query.d.ts +14 -0
  104. package/dist/hooks/use-query.js +118 -0
  105. package/dist/hooks/use-reference-lookup.d.ts +14 -0
  106. package/dist/hooks/use-reference-lookup.js +63 -0
  107. package/dist/hooks/use-store.d.ts +3 -0
  108. package/dist/hooks/use-store.js +38 -0
  109. package/dist/hooks/use-stream-handler.d.ts +14 -0
  110. package/dist/hooks/use-stream-handler.js +93 -0
  111. package/dist/i18n-defaults.d.ts +2 -0
  112. package/dist/i18n-defaults.js +201 -0
  113. package/dist/i18n.d.ts +64 -0
  114. package/dist/i18n.js +166 -0
  115. package/dist/index.d.ts +77 -0
  116. package/dist/index.js +55 -0
  117. package/dist/inside-drawer.d.ts +2 -0
  118. package/{src/inside-drawer.ts → dist/inside-drawer.js} +2 -5
  119. package/dist/page-header-slot.d.ts +2 -0
  120. package/{src/page-header-slot.ts → dist/page-header-slot.js} +2 -5
  121. package/dist/primitives.d.ts +1351 -0
  122. package/dist/primitives.js +73 -0
  123. package/dist/screen-fills-height.d.ts +3 -0
  124. package/dist/screen-fills-height.js +3 -0
  125. package/dist/share-links.d.ts +4 -0
  126. package/dist/share-links.js +11 -0
  127. package/dist/sort-by-accessor.d.ts +5 -0
  128. package/dist/sort-by-accessor.js +16 -0
  129. package/dist/sse/live-events.d.ts +23 -0
  130. package/dist/sse/live-events.js +28 -0
  131. package/dist/tokens.d.ts +83 -0
  132. package/dist/tokens.js +80 -0
  133. package/package.json +16 -9
  134. package/src/__tests__/error-i18n-defaults.test.ts +0 -82
  135. package/src/__tests__/format-when.test.ts +0 -12
  136. package/src/__tests__/i18n.test.tsx +0 -283
  137. package/src/__tests__/merge-search-params-into-initial.test.ts +0 -525
  138. package/src/__tests__/qn.test.ts +0 -83
  139. package/src/__tests__/share-links.test.ts +0 -26
  140. package/src/__tests__/sort-by-accessor.test.ts +0 -48
  141. package/src/__tests__/stringify-nav-params.test.ts +0 -33
  142. package/src/__tests__/use-list-url-state.test.tsx +0 -250
  143. package/src/app/__tests__/action-form-field-labels.test.tsx +0 -92
  144. package/src/app/__tests__/action-form-shim.test.ts +0 -137
  145. package/src/app/__tests__/build-list-query-payload.test.ts +0 -107
  146. package/src/app/__tests__/config-edit-shim.test.ts +0 -57
  147. package/src/app/__tests__/content-editors.test.tsx +0 -128
  148. package/src/app/__tests__/content-preview.test.tsx +0 -184
  149. package/src/app/__tests__/entity-edit-create-search-params.test.tsx +0 -236
  150. package/src/app/__tests__/entity-edit-fill-height.test.tsx +0 -118
  151. package/src/app/__tests__/entity-edit-jsonb-embedded-submit.test.tsx +0 -239
  152. package/src/app/__tests__/entity-list-create-singleton.test.tsx +0 -128
  153. package/src/app/__tests__/entity-list-row-action-entity-target.test.tsx +0 -424
  154. package/src/app/__tests__/entity-list-row-action-icon.test.tsx +0 -248
  155. package/src/app/__tests__/entity-list-row-action-kumiko-actions-view.test.tsx +0 -162
  156. package/src/app/__tests__/entity-list-row-action-refetch.test.tsx +0 -401
  157. package/src/app/__tests__/entity-list-screen-padding.test.tsx +0 -128
  158. package/src/app/__tests__/feature-schema.test.ts +0 -40
  159. package/src/app/__tests__/form-schema.test.ts +0 -247
  160. package/src/app/__tests__/list-daterange-facet.test.ts +0 -165
  161. package/src/app/__tests__/list-filter-facets.test.tsx +0 -521
  162. package/src/app/__tests__/nav.test.ts +0 -198
  163. package/src/app/__tests__/projection-detail-actions.test.tsx +0 -719
  164. package/src/app/__tests__/projection-detail-reference-field.test.tsx +0 -165
  165. package/src/app/__tests__/projection-detail-shim.test.ts +0 -188
  166. package/src/app/__tests__/projection-detail-singleton.test.tsx +0 -191
  167. package/src/app/__tests__/projection-list-bad-shape.test.tsx +0 -152
  168. package/src/app/__tests__/projection-list-reference-column.test.tsx +0 -219
  169. package/src/app/__tests__/projection-list-search-sort.test.tsx +0 -212
  170. package/src/app/__tests__/qualified-names.test.ts +0 -37
  171. package/src/app/__tests__/return-to.test.ts +0 -396
  172. package/src/app/__tests__/row-actions-navigate-tab.test.ts +0 -223
  173. package/src/app/__tests__/row-default-edit-action.test.tsx +0 -324
  174. package/src/app/__tests__/screen-access-allows.test.ts +0 -34
  175. package/src/app/__tests__/secret-mint-body.test.tsx +0 -404
  176. package/src/app/__tests__/variable-chips.test.tsx +0 -70
  177. package/src/app/app-features-context.tsx +0 -25
  178. package/src/app/column-renderers.tsx +0 -68
  179. package/src/app/content-editors.tsx +0 -85
  180. package/src/app/content-preview.tsx +0 -67
  181. package/src/app/custom-screens.tsx +0 -29
  182. package/src/app/dashboard-body.tsx +0 -33
  183. package/src/app/extension-form-submit.tsx +0 -123
  184. package/src/app/extension-sections.tsx +0 -111
  185. package/src/app/form-schema.ts +0 -91
  186. package/src/app/kumiko-screen.tsx +0 -3801
  187. package/src/app/layout-fields.ts +0 -31
  188. package/src/app/list-facets.ts +0 -259
  189. package/src/app/nav.tsx +0 -258
  190. package/src/app/projection-detail-shim.ts +0 -86
  191. package/src/app/reference-facet-bridge.tsx +0 -58
  192. package/src/app/return-to.tsx +0 -234
  193. package/src/app/row-actions.ts +0 -640
  194. package/src/app/screen-access.ts +0 -63
  195. package/src/app/secret-mint-body.tsx +0 -259
  196. package/src/app/secrets-edit-body.tsx +0 -194
  197. package/src/app/use-embedded-screen.ts +0 -27
  198. package/src/app/variable-chips.tsx +0 -39
  199. package/src/app/write-failed-error.ts +0 -27
  200. package/src/components/__tests__/embedded-list-field.test.tsx +0 -531
  201. package/src/components/__tests__/multi-select-checkboxes.test.tsx +0 -162
  202. package/src/components/__tests__/related-list-section-empty-state.test.tsx +0 -143
  203. package/src/components/__tests__/related-list-section.test.tsx +0 -1733
  204. package/src/components/__tests__/render-edit-action-button.test.tsx +0 -205
  205. package/src/components/__tests__/render-edit-hidden-section.test.tsx +0 -128
  206. package/src/components/__tests__/render-edit-logic.test.ts +0 -314
  207. package/src/components/__tests__/render-edit-screen-description.test.tsx +0 -152
  208. package/src/components/__tests__/render-edit-submit-actions.test.tsx +0 -460
  209. package/src/components/__tests__/render-field-app-locale.test.tsx +0 -142
  210. package/src/components/__tests__/render-field-enum-option.test.tsx +0 -99
  211. package/src/components/__tests__/render-field-icon-derivation.test.tsx +0 -119
  212. package/src/components/__tests__/render-field-json-format.test.tsx +0 -164
  213. package/src/components/__tests__/render-field-money-embedded-list-consistency.test.tsx +0 -212
  214. package/src/components/__tests__/render-field-money-roundtrip.test.tsx +0 -217
  215. package/src/components/__tests__/render-field-number-grouping.test.tsx +0 -76
  216. package/src/components/__tests__/render-field-number-unit.test.tsx +0 -227
  217. package/src/components/__tests__/render-field-password-format.test.tsx +0 -101
  218. package/src/components/__tests__/render-field-select-display.test.tsx +0 -134
  219. package/src/components/__tests__/render-field-unit-format.test.tsx +0 -107
  220. package/src/components/__tests__/render-field-unsupported-types.test.tsx +0 -195
  221. package/src/components/__tests__/render-list-toolbar-icon-only.test.tsx +0 -146
  222. package/src/components/__tests__/write-form-section.test.tsx +0 -375
  223. package/src/components/embedded-list-field.tsx +0 -354
  224. package/src/components/grid-cell-for-field.tsx +0 -88
  225. package/src/components/multi-select-checkboxes.tsx +0 -93
  226. package/src/components/reference-create-dialog.tsx +0 -91
  227. package/src/components/related-list-section.tsx +0 -491
  228. package/src/components/render-edit-action-button.tsx +0 -109
  229. package/src/components/render-edit-logic.ts +0 -85
  230. package/src/components/render-edit-types.ts +0 -226
  231. package/src/components/render-edit.tsx +0 -1871
  232. package/src/components/render-field.tsx +0 -951
  233. package/src/components/render-list.tsx +0 -593
  234. package/src/components/write-form-section.tsx +0 -198
  235. package/src/context/draft-storage-context.tsx +0 -39
  236. package/src/hooks/__tests__/use-ai-text.test.tsx +0 -177
  237. package/src/hooks/__tests__/use-disclosure.test.tsx +0 -25
  238. package/src/hooks/__tests__/use-form.test.tsx +0 -65
  239. package/src/hooks/__tests__/use-mutation.test.tsx +0 -77
  240. package/src/hooks/__tests__/use-query.test.tsx +0 -149
  241. package/src/hooks/__tests__/use-stream-handler.test.tsx +0 -107
  242. package/src/hooks/use-ai-text.ts +0 -172
  243. package/src/hooks/use-disclosure.ts +0 -20
  244. package/src/hooks/use-form.ts +0 -78
  245. package/src/hooks/use-list-url-state.ts +0 -207
  246. package/src/hooks/use-mutation.ts +0 -61
  247. package/src/hooks/use-query.ts +0 -166
  248. package/src/hooks/use-reference-lookup.ts +0 -76
  249. package/src/hooks/use-store.ts +0 -48
  250. package/src/hooks/use-stream-handler.ts +0 -134
  251. package/src/i18n-defaults.ts +0 -240
  252. package/src/i18n.tsx +0 -286
  253. package/src/index.ts +0 -279
  254. package/src/primitives.tsx +0 -1567
  255. package/src/screen-fills-height.ts +0 -3
  256. package/src/share-links.ts +0 -12
  257. package/src/sort-by-accessor.ts +0 -20
  258. package/src/sse/live-events.tsx +0 -56
  259. package/src/tokens.tsx +0 -170
@@ -1,324 +0,0 @@
1
- // entityList and projectionList rowActions
2
- // get a default "Edit" row action for free when the row's entity has a
3
- // visible entityEdit screen — same cross-feature resolution as
4
- // projectionDetail's header defaultEditAction (fw#2166), reused here via
5
- // findEditScreenFor (screen-access.ts). Declared rowActions with id "edit"
6
- // win; access-gating and a missing entityEdit screen both suppress the
7
- // default. Mirrors entity-list-row-action-entity-target.test.tsx's harness.
8
-
9
- import { describe, expect, test } from "bun:test";
10
- import type {
11
- EntityDefinition,
12
- EntityEditScreenDefinition,
13
- EntityListScreenDefinition,
14
- ProjectionListScreenDefinition,
15
- } from "@cosmicdrift/kumiko-framework/ui-types";
16
- import type { Dispatcher } from "@cosmicdrift/kumiko-headless";
17
- import { render, waitFor } from "@testing-library/react";
18
- import type { ComponentType, ReactNode } from "react";
19
- import { DispatcherProvider } from "../../context/dispatcher-context";
20
- import { UserRolesProvider } from "../../context/user-roles-context";
21
- import { createStaticLocaleResolver, LocaleProvider } from "../../i18n";
22
- import { kumikoDefaultTranslations } from "../../i18n-defaults";
23
- import {
24
- type CorePrimitives,
25
- type DataTableProps,
26
- type DataTableRowAction,
27
- PrimitivesProvider,
28
- } from "../../primitives";
29
- import { AppFeaturesProvider } from "../app-features-context";
30
- import type { FeatureSchema } from "../feature-schema";
31
- import { KumikoScreen } from "../kumiko-screen";
32
- import type { NavTarget } from "../nav";
33
- import { NavProvider } from "../nav";
34
-
35
- function stubDispatcher(rows: readonly Record<string, unknown>[]): Dispatcher {
36
- return {
37
- write: (async () => ({ isSuccess: true, data: {} })) as unknown as Dispatcher["write"],
38
- query: (async () => ({
39
- isSuccess: true,
40
- data: { rows, nextCursor: null, total: rows.length },
41
- })) as unknown as Dispatcher["query"],
42
- batch: (async () => ({ isSuccess: true, results: [] })) as unknown as Dispatcher["batch"],
43
- statusStore: {
44
- getState: () => "online",
45
- subscribe: () => () => {},
46
- } as unknown as Dispatcher["statusStore"],
47
- async *stream() {},
48
- pendingWrites: () => [],
49
- pendingFiles: () => [],
50
- };
51
- }
52
-
53
- type Captured = {
54
- readonly rowActions: readonly DataTableRowAction[] | undefined;
55
- readonly rowCount: number;
56
- };
57
- let captured: Captured = { rowActions: undefined, rowCount: 0 };
58
- const captureDataTable: ComponentType<DataTableProps> = (props) => {
59
- captured = { rowActions: props.rowActions, rowCount: props.rows.length };
60
- return null;
61
- };
62
- const noop = (): ReactNode => null;
63
- const passChildren = ({ children }: { readonly children?: ReactNode }): ReactNode => children;
64
-
65
- const testPrimitives: CorePrimitives = {
66
- Button: noop,
67
- Banner: passChildren,
68
- Field: passChildren,
69
- Input: noop,
70
- DataTable: captureDataTable,
71
- Form: passChildren,
72
- Section: passChildren,
73
- Card: passChildren,
74
- Grid: passChildren,
75
- GridCell: passChildren,
76
- Text: passChildren,
77
- Heading: noop,
78
- Dialog: noop,
79
- Modal: noop,
80
- Lightbox: noop,
81
- ConfigSourceBadge: noop,
82
- ConfigCascadeView: noop,
83
- Link: noop,
84
- };
85
-
86
- function editScreen(entity: string, roles?: readonly string[]): EntityEditScreenDefinition {
87
- return {
88
- id: "app:screen:rent-edit",
89
- type: "entityEdit",
90
- entity,
91
- layout: { sections: [{ columns: 1, fields: ["name"] }] },
92
- ...(roles !== undefined && { access: { roles } }),
93
- };
94
- }
95
-
96
- function requireAction(id: string): DataTableRowAction {
97
- const action = captured.rowActions?.find((a) => a.id === id);
98
- if (!action) throw new Error(`expected the '${id}' row action to be captured`);
99
- return action;
100
- }
101
-
102
- function renderScreen(
103
- schema: FeatureSchema,
104
- qn: string,
105
- navigateSpy: (target: NavTarget) => void,
106
- userRoles: readonly string[],
107
- ): void {
108
- render(
109
- <LocaleProvider
110
- resolver={createStaticLocaleResolver({ locale: "en" })}
111
- fallbackBundles={[kumikoDefaultTranslations]}
112
- >
113
- <DispatcherProvider dispatcher={stubDispatcher([{ id: "row-1", name: "Alice" }])}>
114
- <AppFeaturesProvider features={[schema]}>
115
- <UserRolesProvider roles={userRoles}>
116
- <NavProvider
117
- value={{
118
- route: undefined,
119
- navigate: navigateSpy,
120
- replace: () => {},
121
- hrefFor: () => "",
122
- searchParams: {},
123
- setSearchParams: () => {},
124
- }}
125
- >
126
- <PrimitivesProvider value={testPrimitives}>
127
- <KumikoScreen schema={schema} qn={qn} />
128
- </PrimitivesProvider>
129
- </NavProvider>
130
- </UserRolesProvider>
131
- </AppFeaturesProvider>
132
- </DispatcherProvider>
133
- </LocaleProvider>,
134
- );
135
- }
136
-
137
- const entity: EntityDefinition = {
138
- fields: {
139
- name: { type: "text", maxLength: 50, required: false, searchable: false, sortable: false },
140
- },
141
- };
142
-
143
- describe("entityList default edit row action", () => {
144
- function schemaWith(
145
- rowActions?: EntityListScreenDefinition["rowActions"],
146
- includeEdit = true,
147
- ): FeatureSchema {
148
- const listScreen: EntityListScreenDefinition = {
149
- id: "rent-list",
150
- type: "entityList",
151
- entity: "rent",
152
- columns: ["name"],
153
- ...(rowActions !== undefined && { rowActions }),
154
- };
155
- return {
156
- featureName: "app",
157
- entities: { rent: entity },
158
- screens: includeEdit ? [listScreen, editScreen("rent")] : [listScreen],
159
- };
160
- }
161
-
162
- test("adds a default edit action at the first position when an entityEdit screen exists", async () => {
163
- captured = { rowActions: undefined, rowCount: 0 };
164
- const navigateCalls: NavTarget[] = [];
165
- renderScreen(schemaWith(undefined), "app:screen:rent-list", (t) => navigateCalls.push(t), []);
166
- await waitFor(() => expect(captured.rowCount).toBe(1));
167
-
168
- expect(captured.rowActions?.[0]?.id).toBe("edit");
169
- expect(captured.rowActions?.[0]?.label).toBe("Edit");
170
-
171
- await requireAction("edit").onTrigger({
172
- id: "row-1",
173
- values: { id: "row-1", name: "Alice" },
174
- });
175
- expect(navigateCalls).toEqual([{ screenId: "rent-edit", entityId: "row-1" }]);
176
- });
177
-
178
- test("a declared rowAction with id 'edit' wins — no doubling", async () => {
179
- captured = { rowActions: undefined, rowCount: 0 };
180
- renderScreen(
181
- schemaWith([{ kind: "navigate", id: "edit", label: "custom-edit", screen: "rent-edit" }]),
182
- "app:screen:rent-list",
183
- () => {},
184
- [],
185
- );
186
- await waitFor(() => expect(captured.rowCount).toBe(1));
187
-
188
- const editActions = captured.rowActions?.filter((a) => a.id === "edit") ?? [];
189
- expect(editActions).toHaveLength(1);
190
- // "custom-edit" has no translation registered, so translate() returns
191
- // the raw key — proving this is the declared action, not the default
192
- // (whose label is the translated "kumiko.actions.edit" → "Edit").
193
- expect(editActions[0]?.label).toBe("custom-edit");
194
- });
195
-
196
- test("no entityEdit screen for the entity → no default edit action", async () => {
197
- captured = { rowActions: undefined, rowCount: 0 };
198
- renderScreen(schemaWith(undefined, false), "app:screen:rent-list", () => {}, []);
199
- await waitFor(() => expect(captured.rowCount).toBe(1));
200
-
201
- expect(captured.rowActions?.some((a) => a.id === "edit") ?? false).toBe(false);
202
- });
203
-
204
- test("access denied to the entityEdit screen → no default edit action", async () => {
205
- captured = { rowActions: undefined, rowCount: 0 };
206
- const listScreen: EntityListScreenDefinition = {
207
- id: "rent-list",
208
- type: "entityList",
209
- entity: "rent",
210
- columns: ["name"],
211
- };
212
- const schema: FeatureSchema = {
213
- featureName: "app",
214
- entities: { rent: entity },
215
- screens: [listScreen, editScreen("rent", ["admin"])],
216
- };
217
- renderScreen(schema, "app:screen:rent-list", () => {}, []);
218
- await waitFor(() => expect(captured.rowCount).toBe(1));
219
-
220
- expect(captured.rowActions?.some((a) => a.id === "edit") ?? false).toBe(false);
221
- });
222
- });
223
-
224
- describe("projectionList default edit row action", () => {
225
- function schemaWith(
226
- rowActions?: ProjectionListScreenDefinition["rowActions"],
227
- includeEdit = true,
228
- ): FeatureSchema {
229
- const listScreen: ProjectionListScreenDefinition = {
230
- id: "rent-projection-list",
231
- type: "projectionList",
232
- query: "app:query:rent:list",
233
- detailFor: "rent",
234
- columns: [{ field: "name", label: "Name" }],
235
- ...(rowActions !== undefined && { rowActions }),
236
- };
237
- return {
238
- featureName: "app",
239
- entities: {},
240
- screens: includeEdit ? [listScreen, editScreen("rent")] : [listScreen],
241
- };
242
- }
243
-
244
- test("adds a default edit action at the first position when detailFor resolves an entityEdit screen", async () => {
245
- captured = { rowActions: undefined, rowCount: 0 };
246
- const navigateCalls: NavTarget[] = [];
247
- renderScreen(
248
- schemaWith(undefined),
249
- "app:screen:rent-projection-list",
250
- (t) => navigateCalls.push(t),
251
- [],
252
- );
253
- await waitFor(() => expect(captured.rowCount).toBe(1));
254
-
255
- expect(captured.rowActions?.[0]?.id).toBe("edit");
256
- expect(captured.rowActions?.[0]?.label).toBe("Edit");
257
-
258
- await requireAction("edit").onTrigger({
259
- id: "row-1",
260
- values: { id: "row-1", name: "Alice" },
261
- });
262
- expect(navigateCalls).toEqual([{ screenId: "rent-edit", entityId: "row-1" }]);
263
- });
264
-
265
- test("a declared rowAction with id 'edit' wins — no doubling", async () => {
266
- captured = { rowActions: undefined, rowCount: 0 };
267
- renderScreen(
268
- schemaWith([{ kind: "navigate", id: "edit", label: "custom-edit", screen: "rent-edit" }]),
269
- "app:screen:rent-projection-list",
270
- () => {},
271
- [],
272
- );
273
- await waitFor(() => expect(captured.rowCount).toBe(1));
274
-
275
- const editActions = captured.rowActions?.filter((a) => a.id === "edit") ?? [];
276
- expect(editActions).toHaveLength(1);
277
- expect(editActions[0]?.label).toBe("custom-edit");
278
- });
279
-
280
- test("no detailFor → no default edit action", async () => {
281
- captured = { rowActions: undefined, rowCount: 0 };
282
- const listScreen: ProjectionListScreenDefinition = {
283
- id: "rent-projection-list",
284
- type: "projectionList",
285
- query: "app:query:rent:list",
286
- columns: [{ field: "name", label: "Name" }],
287
- };
288
- const schema: FeatureSchema = {
289
- featureName: "app",
290
- entities: {},
291
- screens: [listScreen, editScreen("rent")],
292
- };
293
- renderScreen(schema, "app:screen:rent-projection-list", () => {}, []);
294
- await waitFor(() => expect(captured.rowCount).toBe(1));
295
-
296
- expect(captured.rowActions?.some((a) => a.id === "edit") ?? false).toBe(false);
297
- });
298
-
299
- test("access denied to the entityEdit screen → no default edit action", async () => {
300
- captured = { rowActions: undefined, rowCount: 0 };
301
- renderScreen(
302
- {
303
- featureName: "app",
304
- entities: {},
305
- screens: [
306
- {
307
- id: "rent-projection-list",
308
- type: "projectionList",
309
- query: "app:query:rent:list",
310
- detailFor: "rent",
311
- columns: [{ field: "name", label: "Name" }],
312
- },
313
- editScreen("rent", ["admin"]),
314
- ],
315
- },
316
- "app:screen:rent-projection-list",
317
- () => {},
318
- [],
319
- );
320
- await waitFor(() => expect(captured.rowCount).toBe(1));
321
-
322
- expect(captured.rowActions?.some((a) => a.id === "edit") ?? false).toBe(false);
323
- });
324
- });
@@ -1,34 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import type { AccessRule } from "@cosmicdrift/kumiko-framework/ui-types";
3
- import { screenAccessAllows } from "../kumiko-screen";
4
-
5
- describe("screenAccessAllows", () => {
6
- test("allows when no access rule is set", () => {
7
- expect(screenAccessAllows(undefined, undefined)).toBe(true);
8
- });
9
-
10
- test("openToAll with a reason allows regardless of roles", () => {
11
- expect(
12
- screenAccessAllows(
13
- { openToAll: { reason: "test handler callable by any signed-in test user" } },
14
- undefined,
15
- ),
16
- ).toBe(true);
17
- });
18
-
19
- test("the deprecated openToAll: true form denies — fail-closed", () => {
20
- expect(screenAccessAllows({ openToAll: true } as unknown as AccessRule, undefined)).toBe(false);
21
- });
22
-
23
- test("roles-gated allows a matching role", () => {
24
- expect(screenAccessAllows({ roles: ["Admin"] }, ["Admin"])).toBe(true);
25
- });
26
-
27
- test("roles-gated denies with no matching role", () => {
28
- expect(screenAccessAllows({ roles: ["Admin"] }, ["Member"])).toBe(false);
29
- });
30
-
31
- test("roles-gated denies when userRoles is undefined", () => {
32
- expect(screenAccessAllows({ roles: ["Admin"] }, undefined)).toBe(false);
33
- });
34
- });