@antelopejs/dms 0.7.21 → 0.8.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 (208) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/implementations/dms/resolver-timeout.d.ts +1 -0
  3. package/dist/implementations/dms/resolver-timeout.js +13 -3
  4. package/dist/implementations/dms/resolver-timeout.js.map +1 -1
  5. package/dist/implementations/dms-auth/account-emails.d.ts +4 -0
  6. package/dist/implementations/dms-auth/account-emails.js +62 -0
  7. package/dist/implementations/dms-auth/account-emails.js.map +1 -0
  8. package/dist/implementations/dms-auth/index.d.ts +1 -3
  9. package/dist/implementations/dms-auth/index.js +15 -61
  10. package/dist/implementations/dms-auth/index.js.map +1 -1
  11. package/dist/pages/settings/users/email-change.js +3 -4
  12. package/dist/pages/settings/users/email-change.js.map +1 -1
  13. package/dist/pages/settings/users/invites.js +1 -1
  14. package/dist/pages/settings/users/invites.js.map +1 -1
  15. package/dist/pages/settings/users/members.js +1 -1
  16. package/dist/pages/settings/users/members.js.map +1 -1
  17. package/dist/pages/settings/users/notifications.js +1 -0
  18. package/dist/pages/settings/users/notifications.js.map +1 -1
  19. package/dist/test/antelope.test.js +2 -1
  20. package/dist/test/antelope.test.js.map +1 -1
  21. package/dist/test/helpers/db.js +19 -11
  22. package/dist/test/helpers/db.js.map +1 -1
  23. package/dist/test/integration/auth/two-factor-codes.test.js +4 -2
  24. package/dist/test/integration/auth/two-factor-codes.test.js.map +1 -1
  25. package/dist/test/unit/implementations/dms/dynamic-menu-hardening.test.js +8 -5
  26. package/dist/test/unit/implementations/dms/dynamic-menu-hardening.test.js.map +1 -1
  27. package/dist/test/unit/implementations/dms/layout-banners.test.js +20 -7
  28. package/dist/test/unit/implementations/dms/layout-banners.test.js.map +1 -1
  29. package/dist/test/unit/implementations/dms-html-render/built-in-emails.test.js +34 -0
  30. package/dist/test/unit/implementations/dms-html-render/built-in-emails.test.js.map +1 -0
  31. package/dist/test/unit/interfaces/dms/hook-handler-results.test.js +49 -0
  32. package/dist/test/unit/interfaces/dms/hook-handler-results.test.js.map +1 -0
  33. package/dist/test/unit/interfaces/dms/page-topics.test.js +17 -0
  34. package/dist/test/unit/interfaces/dms/page-topics.test.js.map +1 -1
  35. package/dist/test/unit/interfaces/dms-base/confirm-dialog-secondary-action.test.js +2 -2
  36. package/dist/test/unit/interfaces/dms-base/confirm-dialog-secondary-action.test.js.map +1 -1
  37. package/dist/test/unit/interfaces/dms-base/query-response-delta.test.js +29 -0
  38. package/dist/test/unit/interfaces/dms-base/query-response-delta.test.js.map +1 -0
  39. package/dist/test/unit/interfaces/dms-base/resource-form.test.js +5 -0
  40. package/dist/test/unit/interfaces/dms-base/resource-form.test.js.map +1 -1
  41. package/dist/test/unit/interfaces/dms-base/table-view-form-redirect.test.js +12 -2
  42. package/dist/test/unit/interfaces/dms-base/table-view-form-redirect.test.js.map +1 -1
  43. package/dist/test/unit/interfaces/dms-base/table-view-reduced-chrome.test.js +29 -4
  44. package/dist/test/unit/interfaces/dms-base/table-view-reduced-chrome.test.js.map +1 -1
  45. package/dist/test/unit/interfaces/dms-base/table-view-source-reorder.test.js +14 -11
  46. package/dist/test/unit/interfaces/dms-base/table-view-source-reorder.test.js.map +1 -1
  47. package/dist/test/unit/interfaces/dms-base/table-view-views.test.js +8 -5
  48. package/dist/test/unit/interfaces/dms-base/table-view-views.test.js.map +1 -1
  49. package/dist/test/unit/utils/email-subjects.test.js +27 -0
  50. package/dist/test/unit/utils/email-subjects.test.js.map +1 -0
  51. package/dist/utils/admin-invite-email.js +2 -4
  52. package/dist/utils/admin-invite-email.js.map +1 -1
  53. package/dist/utils/email-change-mail.d.ts +1 -1
  54. package/dist/utils/email-change-mail.js +11 -6
  55. package/dist/utils/email-change-mail.js.map +1 -1
  56. package/dist/utils/email-subjects.d.ts +3 -0
  57. package/dist/utils/email-subjects.js +32 -0
  58. package/dist/utils/email-subjects.js.map +1 -0
  59. package/dist/utils/export-jobs.js +15 -10
  60. package/dist/utils/export-jobs.js.map +1 -1
  61. package/frontend-vue/layers/dms-core/app/utils/url-interpolation.ts +29 -11
  62. package/frontend-vue/layers/dms-core/shared/types/app-config.ts +17 -0
  63. package/frontend-vue/layers/dms-layout/app/app.config.ts +5 -0
  64. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardHeader.vue +18 -2
  65. package/frontend-vue/layers/dms-layout/app/build/components/layout/Footer.vue +16 -1
  66. package/frontend-vue/layers/dms-layout/app/build/composables/layout/useHeaderActionControls.ts +14 -0
  67. package/frontend-vue/layers/dms-layout/app/components/layout/HStack.vue +16 -0
  68. package/frontend-vue/layers/dms-layout/app/components/layout/VStack.vue +2 -0
  69. package/frontend-vue/layers/dms-layout/app/composables/useFooterLinks.ts +11 -1
  70. package/frontend-vue/layers/dms-layout/app/custom-pages/settings/roles.vue +1 -1
  71. package/frontend-vue/layers/dms-layout/app/emails/EmailAdminInvite.vue +1 -1
  72. package/frontend-vue/layers/dms-layout/app/emails/EmailChangeNotice.vue +13 -7
  73. package/frontend-vue/layers/dms-layout/app/emails/EmailChangeVerification.vue +18 -7
  74. package/frontend-vue/layers/dms-layout/app/emails/EmailExportReady.vue +23 -12
  75. package/frontend-vue/layers/dms-layout/app/emails/EmailResetPassword.vue +18 -7
  76. package/frontend-vue/layers/dms-layout/app/emails/EmailTwoFactor.vue +23 -4
  77. package/frontend-vue/layers/dms-layout/app/emails/EmailUserValidation.vue +18 -6
  78. package/frontend-vue/layers/dms-layout/app/emails/components/EmailOTP.vue +8 -2
  79. package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +40 -0
  80. package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +40 -0
  81. package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldControl.vue +2 -1
  82. package/frontend-vue/layers/dms-ui/app/build/components/form/FormLoadError.vue +11 -3
  83. package/frontend-vue/layers/dms-ui/app/build/composables/actions/useActionTargets.ts +27 -36
  84. package/frontend-vue/layers/dms-ui/app/build/composables/chart/apexTheme.ts +59 -2
  85. package/frontend-vue/layers/dms-ui/app/build/composables/confirm/confirmDialogTexts.ts +27 -4
  86. package/frontend-vue/layers/dms-ui/app/build/composables/confirm/useActionConfirm.ts +10 -2
  87. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +0 -5
  88. package/frontend-vue/layers/dms-ui/app/build/composables/form/formEntryOrder.ts +23 -0
  89. package/frontend-vue/layers/dms-ui/app/build/composables/form/formFooter.ts +19 -15
  90. package/frontend-vue/layers/dms-ui/app/build/composables/form/formRecordLoad.ts +26 -2
  91. package/frontend-vue/layers/dms-ui/app/build/composables/page/pageUrlValues.ts +41 -0
  92. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/registerDefaultFunctions.ts +2 -2
  93. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +3 -5
  94. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewConfig.ts +7 -12
  95. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts +3 -3
  96. package/frontend-vue/layers/dms-ui/app/build/utils/link.ts +7 -3
  97. package/frontend-vue/layers/dms-ui/app/build/utils/urlVariables.ts +32 -9
  98. package/frontend-vue/layers/dms-ui/app/components/activity-feed/ActivityFeed.vue +8 -1
  99. package/frontend-vue/layers/dms-ui/app/components/banner/Banner.vue +9 -4
  100. package/frontend-vue/layers/dms-ui/app/components/blocks/BannerBlock.vue +5 -11
  101. package/frontend-vue/layers/dms-ui/app/components/blocks/NavCardGridBlock.vue +2 -1
  102. package/frontend-vue/layers/dms-ui/app/components/chart/ApexChartHost.vue +32 -1
  103. package/frontend-vue/layers/dms-ui/app/components/check-list/CheckList.vue +1 -1
  104. package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +12 -2
  105. package/frontend-vue/layers/dms-ui/app/components/form/components/Cascader.vue +7 -3
  106. package/frontend-vue/layers/dms-ui/app/components/form/components/File.vue +7 -0
  107. package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +4 -1
  108. package/frontend-vue/layers/dms-ui/app/components/form/components/InputCode.vue +4 -2
  109. package/frontend-vue/layers/dms-ui/app/components/form/components/InputSecret.vue +3 -2
  110. package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +4 -1
  111. package/frontend-vue/layers/dms-ui/app/components/grid/GridRow.vue +14 -6
  112. package/frontend-vue/layers/dms-ui/app/components/grid/columns.ts +52 -3
  113. package/frontend-vue/layers/dms-ui/app/components/key-value-list/KeyValueList.vue +2 -1
  114. package/frontend-vue/layers/dms-ui/app/components/list-row/ListRow.vue +9 -2
  115. package/frontend-vue/layers/dms-ui/app/components/save-bar/InstantSaveBadge.vue +13 -5
  116. package/frontend-vue/layers/dms-ui/app/components/save-bar/SaveBar.vue +1 -1
  117. package/frontend-vue/layers/dms-ui/app/components/stat-group/StatGroup.vue +3 -2
  118. package/frontend-vue/layers/dms-ui/app/components/table-view/CascaderPath.vue +53 -31
  119. package/frontend-vue/layers/dms-ui/app/composables/chart/apexPlotOptions.ts +15 -4
  120. package/frontend-vue/layers/dms-ui/app/composables/chart/apexYRange.ts +123 -0
  121. package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.ts +49 -49
  122. package/frontend-vue/layers/dms-ui/app/composables/chart/useChartTheme.ts +20 -1
  123. package/frontend-vue/layers/dms-ui/app/composables/confirm/types.ts +1 -1
  124. package/frontend-vue/layers/dms-ui/app/composables/form/types/props.ts +2 -0
  125. package/frontend-vue/layers/dms-ui/app/composables/form/useForm.ts +8 -6
  126. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/action-target.ts +1 -1
  127. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/config.ts +3 -5
  128. package/frontend-vue/layers/dms-ui/app/utils/bannerToneIcons.ts +13 -0
  129. package/frontend-vue/layers/dms-ui/app/utils/cascader.ts +26 -0
  130. package/frontend-vue/layers/dms-ui/i18n/locales/ui-en-GB.json +4 -0
  131. package/frontend-vue/layers/dms-ui/i18n/locales/ui-fr-FR.json +4 -0
  132. package/frontend-vue/package.json +1 -1
  133. package/frontend-vue/pnpm-lock.yaml +5 -5
  134. package/frontend-vue/tests/activity-feed-fill-height.test.ts +7 -0
  135. package/frontend-vue/tests/banner-tone-icon.test.ts +55 -0
  136. package/frontend-vue/tests/builtin-emails.test.ts +141 -0
  137. package/frontend-vue/tests/cascader.test.ts +27 -0
  138. package/frontend-vue/tests/chart-auto-y-range.test.ts +25 -9
  139. package/frontend-vue/tests/chart-colors.test.ts +8 -0
  140. package/frontend-vue/tests/chart-resize-after-animation.test.ts +100 -0
  141. package/frontend-vue/tests/chart-type-defaults.test.ts +128 -0
  142. package/frontend-vue/tests/check-list-marker.test.ts +58 -0
  143. package/frontend-vue/tests/confirm-from.test.ts +1 -1
  144. package/frontend-vue/tests/confirm-secondary-action.test.ts +2 -2
  145. package/frontend-vue/tests/context-links.test.ts +2 -2
  146. package/frontend-vue/tests/custom-action-confirm-from.test.ts +2 -2
  147. package/frontend-vue/tests/download-action-target.test.ts +2 -2
  148. package/frontend-vue/tests/file-drop-prompt.test.ts +72 -0
  149. package/frontend-vue/tests/footer-config.test.ts +135 -0
  150. package/frontend-vue/tests/form-actions.test.ts +7 -0
  151. package/frontend-vue/tests/form-enclosing-form.test.ts +0 -21
  152. package/frontend-vue/tests/form-entry-order.test.ts +45 -0
  153. package/frontend-vue/tests/form-footer-kind.test.ts +11 -6
  154. package/frontend-vue/tests/form-load-error.test.ts +38 -23
  155. package/frontend-vue/tests/grid-responsive-columns.test.ts +39 -31
  156. package/frontend-vue/tests/header-actions.test.ts +55 -9
  157. package/frontend-vue/tests/instant-save-badge.test.ts +11 -4
  158. package/frontend-vue/tests/list-row-truncate.test.ts +46 -0
  159. package/frontend-vue/tests/nuxt-ui-aria-labels.test.ts +1 -28
  160. package/frontend-vue/tests/period-live-pause.test.ts +0 -13
  161. package/frontend-vue/tests/period-max-days.test.ts +0 -14
  162. package/frontend-vue/tests/roles-name-errors.test.ts +2 -3
  163. package/frontend-vue/tests/row-side-panel.test.ts +0 -12
  164. package/frontend-vue/tests/save-bar.test.ts +15 -3
  165. package/frontend-vue/tests/save-status-error.test.ts +2 -2
  166. package/frontend-vue/tests/table-cell-wrap.test.ts +0 -12
  167. package/frontend-vue/tests/table-density-menu.test.ts +0 -21
  168. package/frontend-vue/tests/table-live-hold.test.ts +0 -44
  169. package/frontend-vue/tests/table-row-action-errors.test.ts +5 -5
  170. package/frontend-vue/tests/table-row-hover.test.ts +2 -12
  171. package/frontend-vue/tests/table-scroll-area.test.ts +0 -8
  172. package/frontend-vue/tests/table-view-card-props.test.ts +0 -17
  173. package/frontend-vue/tests/table-view-cards-reorder.test.ts +0 -16
  174. package/frontend-vue/tests/table-view-grouped.test.ts +0 -55
  175. package/frontend-vue/tests/table-view-pinned-rows.test.ts +0 -15
  176. package/frontend-vue/tests/table-view-record-link.test.ts +0 -17
  177. package/frontend-vue/tests/table-view-refresh.test.ts +0 -17
  178. package/frontend-vue/tests/table-view-sources.test.ts +1 -1
  179. package/frontend-vue/tests/table-view-url-tab.test.ts +0 -14
  180. package/frontend-vue/tests/template-tokens.test.ts +235 -0
  181. package/frontend-vue/tests/tone.test.ts +0 -19
  182. package/frontend-vue/tests/url-interpolation.test.ts +6 -6
  183. package/package.json +6 -6
  184. package/skills/dms/SKILL.md +2 -1
  185. package/skills/dms-pages/REFERENCE.md +2 -2
  186. package/skills/dms-project/SKILL.md +0 -1
  187. package/dist/test/integration/auth/login.test.js +0 -57
  188. package/dist/test/integration/auth/login.test.js.map +0 -1
  189. package/dist/test/integration/auth/refresh.test.js +0 -59
  190. package/dist/test/integration/auth/refresh.test.js.map +0 -1
  191. package/dist/test/integration/auth/sessions.test.js +0 -58
  192. package/dist/test/integration/auth/sessions.test.js.map +0 -1
  193. package/dist/test/integration/auth/signup.test.js +0 -99
  194. package/dist/test/integration/auth/signup.test.js.map +0 -1
  195. package/dist/test/unit/skills/dms-dev-script.test.d.ts +0 -1
  196. package/dist/test/unit/skills/dms-dev-script.test.js +0 -137
  197. package/dist/test/unit/skills/dms-dev-script.test.js.map +0 -1
  198. package/frontend-vue/tests/form-reload-on-reopen.test.ts +0 -20
  199. package/frontend-vue/tests/icon-aria-hidden.test.ts +0 -29
  200. package/frontend-vue/tests/skeleton-paragraph.test.ts +0 -30
  201. package/frontend-vue/tests/tab-slot-spacing.test.ts +0 -29
  202. package/frontend-vue/tests/table-empty-body-viewport.test.ts +0 -25
  203. package/frontend-vue/tests/table-header-placement.test.ts +0 -21
  204. package/frontend-vue/tests/vue-inventory.test.ts +0 -107
  205. /package/dist/test/{integration/auth/login.test.d.ts → unit/implementations/dms-html-render/built-in-emails.test.d.ts} +0 -0
  206. /package/dist/test/{integration/auth/sessions.test.d.ts → unit/interfaces/dms/hook-handler-results.test.d.ts} +0 -0
  207. /package/dist/test/{integration/auth/refresh.test.d.ts → unit/interfaces/dms-base/query-response-delta.test.d.ts} +0 -0
  208. /package/dist/test/{integration/auth/signup.test.d.ts → unit/utils/email-subjects.test.d.ts} +0 -0
@@ -61,7 +61,7 @@ afterEach(() => {
61
61
  vi.unstubAllGlobals();
62
62
  });
63
63
 
64
- it("shows an empty modal action form's way out and its held submit", async () => {
64
+ it("shows a modal action form's way out and its ready submit", async () => {
65
65
  const { onCancel } = await mount({
66
66
  variant: "band",
67
67
  kind: "action",
@@ -73,12 +73,24 @@ it("shows an empty modal action form's way out and its held submit", async () =>
73
73
  expect(host.firstElementChild!.getAttribute("aria-hidden")).toBeNull();
74
74
  expect(buttons()).toEqual([
75
75
  { label: "dms.button.cancel", disabled: false },
76
- { label: "Invite", disabled: true },
76
+ { label: "Invite", disabled: false },
77
77
  ]);
78
78
  host.querySelector("button")!.click();
79
79
  expect(onCancel).toHaveBeenCalledOnce();
80
80
  });
81
81
 
82
+ it("sends an untouched page action form as it is, its submit alone", async () => {
83
+ await mount({
84
+ variant: "band",
85
+ kind: "action",
86
+ dirty: false,
87
+ saveLabel: "Send",
88
+ });
89
+
90
+ expect(host.firstElementChild!.getAttribute("aria-hidden")).toBeNull();
91
+ expect(buttons()).toEqual([{ label: "Send", disabled: false }]);
92
+ });
93
+
82
94
  it("resets an action form once a value changed, and sends it", async () => {
83
95
  await mount({
84
96
  variant: "band",
@@ -146,7 +158,7 @@ it("gives Cancel and Unsaved changes the same fixed height", async () => {
146
158
  });
147
159
 
148
160
  it("keeps a hidden band's place in its footer", async () => {
149
- await mount({ variant: "band", kind: "action", dirty: false });
161
+ await mount({ variant: "band", kind: "record", dirty: false });
150
162
 
151
163
  expect(anchor().getAttribute("aria-hidden")).toBe("true");
152
164
  expect(classesOf(anchor())).toContain("invisible");
@@ -56,11 +56,11 @@ describe("DmsSaveStatus: a failed save", () => {
56
56
  expect(onRetry).toHaveBeenCalledOnce();
57
57
  });
58
58
 
59
- it("turns the header pill red, its wording unchanged", async () => {
59
+ it("turns the header pill red and says it was not saved", async () => {
60
60
  await mount(() => h(InstantSaveBadge, { state: "error" }));
61
61
  const pill = host.querySelector<HTMLElement>("[data-instant-save-badge]")!;
62
62
 
63
- expect(pill.textContent?.trim()).toBe("Saved instantly");
63
+ expect(pill.textContent?.trim()).toBe("Not saved");
64
64
  expect(pill.className).toContain("text-error");
65
65
  });
66
66
  });
@@ -58,16 +58,4 @@ describe("Table grid cell layout theme", () => {
58
58
  expect(theme().rowCell({ cellWrap })).toContain("py-0");
59
59
  }
60
60
  });
61
-
62
- it("reads each column's policy for its cell too", () => {
63
- expect(tableSource).toMatch(
64
- /uiTable\.rowCell\(\{[^}]*cellWrap: \(cell\.column\.columnDef as TableColumn<T>\)\s*\.cellWrap,?\s*\}\)/,
65
- );
66
- });
67
-
68
- it("reads each column's policy inside the default slot fallback", () => {
69
- expect(tableSource).toMatch(
70
- /uiTable\.rowSpan\(\{\s*cellWrap: \(\s*cell\.column\.columnDef as TableColumn<T>\s*\)\.cellWrap[\s,]*\}\)/,
71
- );
72
- });
73
61
  });
@@ -11,19 +11,9 @@ import {
11
11
  type App,
12
12
  } from "vue";
13
13
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
14
- import { readFileSync } from "node:fs";
15
- import { resolve } from "node:path";
16
14
  import TableMenuDensity from "../layers/dms-ui/app/build/components/table/MenuDensity.vue";
17
15
  import TableMenuRoot from "../layers/dms-ui/app/build/components/table/MenuRoot.vue";
18
16
 
19
- const tableViewSource = readFileSync(
20
- resolve(
21
- __dirname,
22
- "../layers/dms-ui/app/components/table-view/TableView.vue",
23
- ),
24
- "utf8",
25
- );
26
-
27
17
  describe("table density menu", () => {
28
18
  let app: App | undefined;
29
19
 
@@ -115,15 +105,4 @@ describe("table density menu", () => {
115
105
  "dms.table.density_title",
116
106
  );
117
107
  });
118
-
119
- it("keeps the user's density with the table state, the module's as default", () => {
120
- expect(tableViewSource).toMatch(
121
- /defaultDensity = props\.density \?\? DEFAULT_DENSITY/,
122
- );
123
- expect(tableViewSource).toContain(
124
- 'tablePreferences.restore("density", density);',
125
- );
126
- expect(tableViewSource).toMatch(/\["density", density, defaultDensity\]/);
127
- expect(tableViewSource).toContain('v-model:density="density"');
128
- });
129
108
  });
@@ -6,8 +6,6 @@
6
6
  * pagination included), and Show reads the rows once without leaving the
7
7
  * pause.
8
8
  */
9
- import { readFileSync } from "node:fs";
10
- import { resolve } from "node:path";
11
9
  import { createApp, defineComponent, h, nextTick, ref, type App } from "vue";
12
10
  import { refreshPageBlocks } from "../layers/dms-ui/app/utils/blockRefresh";
13
11
  import { afterEach, beforeEach, expect, it, vi } from "vitest";
@@ -43,13 +41,6 @@ const { useTableLiveHold } = await import(
43
41
  );
44
42
 
45
43
  const TOPIC = "api:requests";
46
- const tableViewSource = readFileSync(
47
- resolve(
48
- __dirname,
49
- "../layers/dms-ui/app/components/table-view/TableView.vue",
50
- ),
51
- "utf8",
52
- );
53
44
 
54
45
  let apps: App[] = [];
55
46
  let live: PageLiveController;
@@ -203,23 +194,6 @@ it("reads on a user's refresh while held, and holds from then", async () => {
203
194
  });
204
195
  });
205
196
 
206
- it("wires the table view's rows, row events and banner to the hold", () => {
207
- expect(tableViewSource).toMatch(
208
- /liveHold\?: boolean \| TableViewLiveHoldOptions;/,
209
- );
210
- expect(tableViewSource).toContain(
211
- "isRowOpen: () => openRowId.value !== undefined,",
212
- );
213
- expect(tableViewSource).toContain('ref="tableRoot"');
214
- expect(tableViewSource).toContain("heldQuery: liveHold.heldQuery,");
215
- expect(tableViewSource).toContain("refreshCallback: refreshByUser,");
216
- expect(tableViewSource).toContain('@refresh="refreshByUser"');
217
- expect(tableViewSource).toContain("gateRowEvent(payload.type, ids, event);");
218
- expect(tableViewSource).toMatch(
219
- /<LiveHoldBanner[\s\S]*@show="liveHold.showNew"/,
220
- );
221
- });
222
-
223
197
  it("holds the table alone while a row is open, the page staying live", async () => {
224
198
  const isRowOpen = ref(false);
225
199
  const { refresh, hold } = mountTable({
@@ -338,19 +312,6 @@ it("holds for no search nor sort the options leave off", async () => {
338
312
  expect(hold.reason.value).toBe(null);
339
313
  });
340
314
 
341
- it("tells the hold about the table's search and sort, and the banner why", () => {
342
- expect(tableViewSource).toContain(
343
- "isSearching: () => !!effectiveGlobalFilter.value,",
344
- );
345
- expect(tableViewSource).toContain(
346
- "isSorted: () => !isSameSorting(effectiveSorting.value, defaultSortState),",
347
- );
348
- expect(tableViewSource).toMatch(
349
- /v-if="liveHold\.newCount\.value > 0 \|\| liveHold\.reason\.value"/,
350
- );
351
- expect(tableViewSource).toContain(':reason="liveHold.reason.value"');
352
- });
353
-
354
315
  it("hands the strip the paused page's breakdown and the table's missedLabel", async () => {
355
316
  const isRowOpen = ref(false);
356
317
  const { hold } = mountTable({
@@ -377,8 +338,3 @@ it("hands the strip the paused page's breakdown and the table's missedLabel", as
377
338
  await nextTick();
378
339
  expect(hold.missedDetail.value).toBe(null);
379
340
  });
380
-
381
- it("passes the strip its wording and breakdown", () => {
382
- expect(tableViewSource).toContain(':missed-label="liveHold.missedLabel"');
383
- expect(tableViewSource).toContain(':detail="liveHold.missedDetail.value"');
384
- });
@@ -140,7 +140,7 @@ describe("Table row actions: refusals", () => {
140
140
  label: "Resend",
141
141
  target: {
142
142
  type: "api",
143
- url: "/api/invites/{_id}/resend",
143
+ url: "/api/invites/{{row._id}}/resend",
144
144
  successMessage: "Sent",
145
145
  },
146
146
  confirm: { title: "Resend?", description: "" },
@@ -170,7 +170,7 @@ describe("Table row actions: refusals", () => {
170
170
  target: {
171
171
  type: "api",
172
172
  method: "GET",
173
- url: "/api/invites/{_id}/link",
173
+ url: "/api/invites/{{row._id}}/link",
174
174
  successMessage: "Copied",
175
175
  copy: "url",
176
176
  },
@@ -267,7 +267,7 @@ describe("Table row actions: refusals", () => {
267
267
  label: "Ban",
268
268
  target: {
269
269
  type: "api",
270
- url: "/api/users/{_id}/ban",
270
+ url: "/api/users/{{row._id}}/ban",
271
271
  body: { notify: true, scope: "all" },
272
272
  successMessage: "Banned",
273
273
  },
@@ -293,7 +293,7 @@ describe("Table row actions: refusals", () => {
293
293
  const { deleteRows } = await rowActions();
294
294
 
295
295
  const done = await deleteRows(["t1"], {
296
- confirm: { from: "/api/task/{id}/delete-confirm" },
296
+ confirm: { from: "/api/task/{{row.id}}/delete-confirm" },
297
297
  });
298
298
 
299
299
  expect(done).toBe(true);
@@ -329,7 +329,7 @@ describe("Table row actions: refusals", () => {
329
329
  const { deleteRows } = await rowActions();
330
330
 
331
331
  await deleteRows(["t1", "t2"], {
332
- confirm: { from: "/api/task/{id}/delete-confirm" },
332
+ confirm: { from: "/api/task/{{row.id}}/delete-confirm" },
333
333
  });
334
334
 
335
335
  expect(api).not.toHaveBeenCalledWith("/api/task/t1/delete-confirm");
@@ -95,16 +95,6 @@ describe("row hover affordance", () => {
95
95
  });
96
96
  expect(pinned).toContain("group-data-[presence=true]:bg-elevated");
97
97
  });
98
-
99
- it("feeds the same condition to the row, its pinned cells and the rail", () => {
100
- expect(tableSource).toMatch(
101
- /uiTable\.row\(\{\s*loading,\s*rowClickable: isRowClickable\(row\.original\),/,
102
- );
103
- expect(tableSource).toMatch(
104
- /uiTable\.rowCell\(\{[^}]*rowClickable: isRowClickable\(row\.original\),/,
105
- );
106
- expect(tableSource).toContain("presenceRailBackground(row.original)");
107
- });
108
98
  });
109
99
 
110
100
  describe("resolveRowClickAction", () => {
@@ -164,13 +154,13 @@ describe("rowCustomActions", () => {
164
154
  const stopSelling: CustomRowAction = {
165
155
  id: "stop_selling",
166
156
  label: "Stop selling",
167
- target: { type: "api", url: "/plans/{_id}/stop", method: "POST" },
157
+ target: { type: "api", url: "/plans/{{row._id}}/stop", method: "POST" },
168
158
  rule: { field: "isActive", equals: true },
169
159
  };
170
160
  const remove: CustomRowAction = {
171
161
  id: "delete",
172
162
  label: "Delete",
173
- target: { type: "api", url: "/plans/{_id}", method: "DELETE" },
163
+ target: { type: "api", url: "/plans/{{row._id}}", method: "DELETE" },
174
164
  rule: { field: "workspaces", equals: 0 },
175
165
  };
176
166
 
@@ -34,12 +34,4 @@ describe("Table scroll area", () => {
34
34
  expect(theme().headCell()).not.toContain("sticky");
35
35
  expect(theme().tableRoot()).not.toContain("overflow-y-auto");
36
36
  });
37
-
38
- it("opens the scroll area from `maxHeight` alone", () => {
39
- expect(tableSource).not.toContain("stickyHeader");
40
- expect(tableSource).toMatch(
41
- /hasScrollArea = computed\(\(\) => !!props\.maxHeight\)/,
42
- );
43
- expect(tableSource).toMatch(/maxHeight: props\.maxHeight,/);
44
- });
45
37
  });
@@ -1,8 +1,6 @@
1
1
  // @vitest-environment jsdom
2
2
  import { computed, createApp, defineComponent, h, ref, type App } from "vue";
3
3
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
- import { readFileSync } from "node:fs";
5
- import { resolve } from "node:path";
6
4
  import CardsDisplay from "../layers/dms-ui/app/build/components/table-view/CardsDisplay.vue";
7
5
  import {
8
6
  buildCardProps,
@@ -21,14 +19,6 @@ vi.mock("../layers/dms-ui/app/build/components/table/Empty.vue", () => ({
21
19
  default: { render: () => null },
22
20
  }));
23
21
 
24
- const kanbanBoardSource = readFileSync(
25
- resolve(
26
- __dirname,
27
- "../layers/dms-ui/app/components/table-view/KanbanBoard.vue",
28
- ),
29
- "utf8",
30
- );
31
-
32
22
  const rows = [
33
23
  { _id: "t1", name: "Write the docs" },
34
24
  { _id: "t2", name: "Ship it" },
@@ -79,13 +69,6 @@ describe("table view card props", () => {
79
69
  expect(opened).toEqual([rows[0]]);
80
70
  expect(buildCardProps(rows[1]!, context).selected).toBe(true);
81
71
  });
82
-
83
- it("gives a kanban card the same props, plus its column's value", () => {
84
- expect(kanbanBoardSource).toMatch(
85
- /buildCardProps\(item, props\.cardContext\)/,
86
- );
87
- expect(kanbanBoardSource).toMatch(/groupValue,\n\s*\}\);/);
88
- });
89
72
  });
90
73
 
91
74
  describe("cards display with a custom card", () => {
@@ -11,8 +11,6 @@ import {
11
11
  type PropType,
12
12
  } from "vue";
13
13
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
14
- import { readFileSync } from "node:fs";
15
- import { resolve } from "node:path";
16
14
  import CardsDisplay from "../layers/dms-ui/app/build/components/table-view/CardsDisplay.vue";
17
15
  import { useTableReorder } from "../layers/dms-ui/app/build/composables/table-view/useTableReorder";
18
16
  import { moveRow } from "../layers/dms-ui/app/build/composables/table-view/utils/reorder";
@@ -31,14 +29,6 @@ vi.mock("../layers/dms-ui/app/build/components/table/Empty.vue", () => ({
31
29
  type Row = { _id: string; name: string };
32
30
  type Api = Parameters<typeof useTableReorder>[0]["api"];
33
31
 
34
- const tableViewSource = readFileSync(
35
- resolve(
36
- __dirname,
37
- "../layers/dms-ui/app/components/table-view/TableView.vue",
38
- ),
39
- "utf8",
40
- );
41
-
42
32
  const TaskCard = defineComponent({
43
33
  props: {
44
34
  rowId: String,
@@ -223,12 +213,6 @@ describe("the cards display of a hand-ordered table", () => {
223
213
  });
224
214
 
225
215
  describe("which displays move rows", () => {
226
- it("offers moving on the grid and the cards, never on the board or the groups", () => {
227
- expect(tableViewSource).toMatch(
228
- /ORDERED_DISPLAY_IDS = new Set\(\[TABLE_DISPLAY_ID, CARDS_DISPLAY_ID\]\)/,
229
- );
230
- expect(tableViewSource).toMatch(/reorder: reorderState\.value,/);
231
- });
232
216
 
233
217
  it("draws no handle on a display that does not draw the rows in their order", () => {
234
218
  vi.stubGlobal("computed", computed);
@@ -1,6 +1,4 @@
1
1
  // @vitest-environment jsdom
2
- import { readFileSync } from "node:fs";
3
- import { resolve } from "node:path";
4
2
  import {
5
3
  computed,
6
4
  createApp,
@@ -25,25 +23,6 @@ import {
25
23
  } from "../layers/dms-ui/app/build/composables/table-view/utils/groupedRows";
26
24
  import type { TableViewColumn } from "../layers/dms-ui/app/composables/table-view/types";
27
25
 
28
- const tableSource = readFileSync(
29
- resolve(__dirname, "../layers/dms-ui/app/build/components/table/Table.vue"),
30
- "utf8",
31
- );
32
- const menuViewModeSource = readFileSync(
33
- resolve(
34
- __dirname,
35
- "../layers/dms-ui/app/build/components/table/MenuViewMode.vue",
36
- ),
37
- "utf8",
38
- );
39
- const tableViewSource = readFileSync(
40
- resolve(
41
- __dirname,
42
- "../layers/dms-ui/app/components/table-view/TableView.vue",
43
- ),
44
- "utf8",
45
- );
46
-
47
26
  const LABELS = {
48
27
  today: "Today",
49
28
  yesterday: "Yesterday",
@@ -117,40 +96,6 @@ describe("grouped rows", () => {
117
96
  });
118
97
  });
119
98
 
120
- describe("grouped display wiring", () => {
121
- it("draws a header row before each group of the page", () => {
122
- expect(tableSource).toMatch(
123
- /v-for="\{ kind, id, row, key, header \} in bodyEntries"/,
124
- );
125
- // Drawn by the entry, not by reading the prop when the header renders:
126
- // leaving the grouped display then cannot leave it undefined.
127
- expect(tableSource).toContain('<component :is="header" />');
128
- expect(tableSource).not.toContain("grouping!.header");
129
- expect(tableSource).toContain(':class="uiTable.groupCell()"');
130
- expect(tableSource).toMatch(/if \(key !== currentKey\) \{/);
131
- });
132
-
133
- it("sorts the list on the grouped column while the grouped display shows", () => {
134
- expect(tableViewSource).toMatch(
135
- /isGroupedDisplay\.value && activeGroupedOptions\.value\s*\?\s*groupedSorting\(picked, activeGroupedOptions\.value\)/,
136
- );
137
- expect(tableViewSource).toContain(':grouping="grouping"');
138
- });
139
-
140
- it("lets the user pick the grouped column from the options menu, as on the kanban", () => {
141
- expect(tableViewSource).toContain(
142
- 'v-model:grouped-group-by="groupedGroupBy"',
143
- );
144
- expect(tableViewSource).toContain(
145
- ':grouped-group-by-options="groupedGroupByOptions"',
146
- );
147
- expect(tableViewSource).toMatch(/grouped: activeGroupedOptions,/);
148
- expect(menuViewModeSource).toMatch(
149
- /\[GROUPED_DISPLAY_ID\]: \(shared\) => \(\{\s*options: shared\.groupedGroupByOptions,/,
150
- );
151
- });
152
- });
153
-
154
99
  describe("grouped group-by", () => {
155
100
  const column = (accessorKey: string, typeId: string, options = {}) =>
156
101
  ({
@@ -3,8 +3,6 @@
3
3
  * base language of a languages table stays on top whichever column the user
4
4
  * sorts on, and on the first page.
5
5
  */
6
- import { readFileSync } from "node:fs";
7
- import { resolve } from "node:path";
8
6
  import { describe, expect, it, vi } from "vitest";
9
7
  import {
10
8
  processSourceRows,
@@ -128,16 +126,3 @@ describe("rows a route sorts", () => {
128
126
  expect(ids((await rows.list({}))!.results)).toEqual(ids(LANGUAGES));
129
127
  });
130
128
  });
131
-
132
- it("hands the table view's pinnedRows to its rows, except while grouped", () => {
133
- const source = readFileSync(
134
- resolve(
135
- __dirname,
136
- "../layers/dms-ui/app/components/table-view/TableView.vue",
137
- ),
138
- "utf8",
139
- );
140
- expect(source).toMatch(
141
- /pinnedRows: \(\) => \(isGroupedDisplay\.value \? undefined : props\.pinnedRows\)/,
142
- );
143
- });
@@ -5,8 +5,6 @@
5
5
  * closed meanwhile: the table removes the key in place, so the route still
6
6
  * holds the same value and only the visit tells the link was followed again.
7
7
  */
8
- import { readFileSync } from "node:fs";
9
- import { resolve } from "node:path";
10
8
  import { createApp, defineComponent, h, nextTick, reactive } from "vue";
11
9
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
12
10
  import type { CustomRowAction } from "../layers/dms-ui/app/types/row-action";
@@ -190,18 +188,3 @@ describe("the URL a closed drawer leaves", () => {
190
188
  });
191
189
  });
192
190
  });
193
-
194
- it("wires the record link and the filter chips into the table view", () => {
195
- const source = readFileSync(
196
- resolve(
197
- __dirname,
198
- "../layers/dms-ui/app/components/table-view/TableView.vue",
199
- ),
200
- "utf8",
201
- );
202
- expect(source).toContain("useTableRecordLink<T>({");
203
- expect(source).toContain("onRecordShown: (id) => recordLink?.shown(id),");
204
- expect(source).toContain(
205
- "withoutQueryParam(currentQuery(route.query), param)",
206
- );
207
- });
@@ -5,8 +5,6 @@
5
5
  * asks its blocks to refresh (refreshPageBlocks). The re-read goes through the
6
6
  * same refresh as the toolbar's button, which keeps the rows on screen.
7
7
  */
8
- import { readFileSync } from "node:fs";
9
- import { resolve } from "node:path";
10
8
  import { createApp, defineComponent, h } from "vue";
11
9
  import { afterEach, beforeEach, expect, it, vi } from "vitest";
12
10
  import type { RealtimeTopicHandler } from "../layers/dms-core/app/composables/realtime/useUserRealtime";
@@ -24,14 +22,6 @@ const { useRefreshTriggers } = await import(
24
22
  "../layers/dms-ui/app/build/composables/blocks/useRefreshTriggers"
25
23
  );
26
24
 
27
- const tableViewSource = readFileSync(
28
- resolve(
29
- __dirname,
30
- "../layers/dms-ui/app/components/table-view/TableView.vue",
31
- ),
32
- "utf8",
33
- );
34
-
35
25
  const UPLOADS_TOPIC = "media:uploads";
36
26
  const mounted: Array<{ unmount: () => void }> = [];
37
27
 
@@ -75,10 +65,3 @@ it("refreshes on each event of any of its topics, not on a snapshot", () => {
75
65
  topicHandlers.get(UPLOADS_TOPIC)?.({ entries: [] } as never);
76
66
  expect(block.onRefresh).toHaveBeenCalledTimes(2);
77
67
  });
78
-
79
- it("wires the table view's realtimeTopic and page refresh to its refresh", () => {
80
- expect(tableViewSource).toMatch(/realtimeTopic\?: string \| string\[\];/);
81
- expect(tableViewSource).toMatch(
82
- /const liveGate = useRefreshTriggers\(\s*props\.realtimeTopic,\s*\(\) => void refreshAll\(\),\s*\);/,
83
- );
84
- });
@@ -182,7 +182,7 @@ describe("source tables", () => {
182
182
 
183
183
  describe("reading one row (fetchRowUrl)", () => {
184
184
  const FETCH_URL = "/api/ws/{{params.ws}}/requests";
185
- const ROW_URL = "/api/ws/{{params.ws}}/requests/{id}";
185
+ const ROW_URL = "/api/ws/{{params.ws}}/requests/{{row.id}}";
186
186
 
187
187
  function rowsWith(
188
188
  api: (url: string) => Promise<unknown>,
@@ -6,8 +6,6 @@
6
6
  * remounting the page. A tab the user opens is written back to the URL in
7
7
  * place, under the same key.
8
8
  */
9
- import { readFileSync } from "node:fs";
10
- import { resolve } from "node:path";
11
9
  import { createApp, defineComponent, h, nextTick, reactive, ref } from "vue";
12
10
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
13
11
  import {
@@ -183,15 +181,3 @@ describe("the tab the user opens", () => {
183
181
  expect(activeTabId.value).toBe("all");
184
182
  });
185
183
  });
186
-
187
- it("wires the URL tab into the table view", () => {
188
- const source = readFileSync(
189
- resolve(
190
- __dirname,
191
- "../layers/dms-ui/app/components/table-view/TableView.vue",
192
- ),
193
- "utf8",
194
- );
195
- expect(source).toContain("readTableUrlTab(route.query, urlScope)");
196
- expect(source).toContain("useTableUrlTab({");
197
- });