@antelopejs/dms 0.7.20 → 0.7.21

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 (22) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/test/unit/interfaces/dms-base/table-view-row-menu-label.test.d.ts +1 -0
  3. package/dist/test/unit/interfaces/dms-base/table-view-row-menu-label.test.js +27 -0
  4. package/dist/test/unit/interfaces/dms-base/table-view-row-menu-label.test.js.map +1 -0
  5. package/dist/test/unit/interfaces/dms-base/table-view-source-reorder.test.js +11 -0
  6. package/dist/test/unit/interfaces/dms-base/table-view-source-reorder.test.js.map +1 -1
  7. package/frontend-vue/layers/dms-ui/app/build/components/table/Table.vue +13 -3
  8. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardsDisplay.vue +1 -1
  9. package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowNavigation.ts +14 -0
  10. package/frontend-vue/layers/dms-ui/app/build/composables/actions/useActionTargets.ts +63 -7
  11. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableUrlTab.ts +42 -12
  12. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewConfig.ts +3 -2
  13. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/recordLink.ts +10 -11
  14. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/views.ts +29 -0
  15. package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +14 -6
  16. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/config.ts +2 -0
  17. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/display.ts +6 -0
  18. package/frontend-vue/tests/row-side-panel.test.ts +93 -2
  19. package/frontend-vue/tests/table-view-bulk-drawer.test.ts +114 -0
  20. package/frontend-vue/tests/table-view-cards-reread.test.ts +77 -0
  21. package/frontend-vue/tests/table-view-url-tab.test.ts +84 -2
  22. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.7.21
4
+
5
+ [compare changes](https://github.com/AntelopeJS/dms/compare/v0.7.20...v0.7.21)
6
+
7
+ ### 🚀 Enhancements
8
+
9
+ - **table-view:** Let a row's panel, drawer or modal show another row in place ([#252](https://github.com/AntelopeJS/dms/pull/252))
10
+ - **table-view:** Let a table view drop the built-in table display ([#255](https://github.com/AntelopeJS/dms/pull/255))
11
+
12
+ ### 🩹 Fixes
13
+
14
+ - **table-view:** Keep an empty list on screen while it is read again ([#250](https://github.com/AntelopeJS/dms/pull/250))
15
+ - **table-view:** Serve the row menu label to the browser ([#253](https://github.com/AntelopeJS/dms/pull/253))
16
+ - **table-view:** Keep the tab the user opens in the URL ([#254](https://github.com/AntelopeJS/dms/pull/254))
17
+
18
+ ### 🏡 Chore
19
+
20
+ - **release:** @antelopejs/interface-dms v0.5.13 ([6b58111b](https://github.com/AntelopeJS/dms/commit/6b58111b))
21
+
22
+ ### ❤️ Contributors
23
+
24
+ - Antony Rizzitelli <rizzitelli.antony@pm.me>
25
+
3
26
  ## v0.7.20
4
27
 
5
28
  [compare changes](https://github.com/AntelopeJS/dms/compare/v0.7.19...v0.7.20)
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const chai_1 = require("chai");
4
+ const request_filter_1 = require("@antelopejs/interface-dms/base/table-view/internal/request-filter");
5
+ const ALL_GRANTED = {
6
+ add: true,
7
+ edit: true,
8
+ delete: true,
9
+ view: true,
10
+ archive: true,
11
+ restore: true,
12
+ viewArchived: true,
13
+ export: true,
14
+ };
15
+ describe("[unit] interfaces/dms-base/table-view row menu label", () => {
16
+ it("serves the table's menuLabel to the browser", () => {
17
+ const served = (0, request_filter_1.adaptRowActions)({
18
+ rowActions: { menuLabel: "$features.row_menu" },
19
+ archiveMode: false,
20
+ grants: ALL_GRANTED,
21
+ custom: undefined,
22
+ controllerRules: undefined,
23
+ });
24
+ (0, chai_1.expect)(served.menuLabel).to.equal("$features.row_menu");
25
+ });
26
+ });
27
+ //# sourceMappingURL=table-view-row-menu-label.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-view-row-menu-label.test.js","sourceRoot":"","sources":["../../../../../src/test/unit/interfaces/dms-base/table-view-row-menu-label.test.ts"],"names":[],"mappings":";;AAAA,+BAA8B;AAC9B,sGAG2E;AAE3E,MAAM,WAAW,GAAoB;IACnC,GAAG,EAAE,IAAI;IACT,IAAI,EAAE,IAAI;IACV,MAAM,EAAE,IAAI;IACZ,IAAI,EAAE,IAAI;IACV,OAAO,EAAE,IAAI;IACb,OAAO,EAAE,IAAI;IACb,YAAY,EAAE,IAAI;IAClB,MAAM,EAAE,IAAI;CACb,CAAC;AAEF,QAAQ,CAAC,sDAAsD,EAAE,GAAG,EAAE;IACpE,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,MAAM,GAAG,IAAA,gCAAe,EAAC;YAC7B,UAAU,EAAE,EAAE,SAAS,EAAE,oBAAoB,EAAE;YAC/C,WAAW,EAAE,KAAK;YAClB,MAAM,EAAE,WAAW;YACnB,MAAM,EAAE,SAAS;YACjB,eAAe,EAAE,SAAS;SAC3B,CAAC,CAAC;QAEH,IAAA,aAAM,EAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -171,6 +171,17 @@ describe("[unit] interfaces/dms-base — table view sources and reorder", () =>
171
171
  (0, chai_1.expect)(() => sourceOf({ pageSize: 0 })).to.throw(/pageSize/);
172
172
  (0, chai_1.expect)(() => sourceOf({ displays: [{ id: "plan-cards" }] })).to.throw(/named "<module>:<id>"/);
173
173
  (0, chai_1.expect)(() => sourceOf({ defaultDisplay: "cards" })).to.throw(/not declared in displays/);
174
+ (0, chai_1.expect)(() => sourceOf({ tableDisplay: false, displays: [{ id: "cards" }] })).to.throw(/drops the table display without a defaultDisplay/);
175
+ (0, chai_1.expect)(() => sourceOf({
176
+ tableDisplay: false,
177
+ displays: [{ id: "cards" }],
178
+ defaultDisplay: "table",
179
+ })).to.throw(/not declared in displays/);
180
+ (0, chai_1.expect)(sourceOf({
181
+ tableDisplay: false,
182
+ displays: [{ id: "cards" }],
183
+ defaultDisplay: "cards",
184
+ }).tableDisplay).to.equal(false);
174
185
  (0, chai_1.expect)(() => sourceOf({ card: { fields: ["status"] } })).to.throw(/unknown column "status"/);
175
186
  const both = [
176
187
  {
@@ -1 +1 @@
1
- {"version":3,"file":"table-view-source-reorder.test.js","sourceRoot":"","sources":["../../../../../src/test/unit/interfaces/dms-base/table-view-source-reorder.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"table-view-source-reorder.test.js","sourceRoot":"","sources":["../../../../../src/test/unit/interfaces/dms-base/table-view-source-reorder.test.ts"],"names":[],"mappings":"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"}
@@ -256,6 +256,12 @@ export interface TableProps<T> {
256
256
  data?: T[] | null;
257
257
  columns?: TableColumn<T>[];
258
258
  loading?: boolean;
259
+ /**
260
+ * No answer has arrived yet. Without it, a load with no row listed counts
261
+ * as the first one, which also draws the skeleton over a re-read of an
262
+ * empty list.
263
+ */
264
+ firstPageLoading?: boolean;
259
265
  /**
260
266
  * Message (i18n key) of a failed data fetch; switches the empty-table panel
261
267
  * to its error state so a refused query never reads as "no data".
@@ -713,7 +719,11 @@ const DEFAULT_CAPABILITIES = resolveDisplayCapabilities({
713
719
  columnManagement: true,
714
720
  });
715
721
 
716
- const props = defineProps<TableProps<T>>();
722
+ // Undefined, not the `false` an absent boolean prop casts to: the table then
723
+ // judges the first load by itself.
724
+ const props = withDefaults(defineProps<TableProps<T>>(), {
725
+ firstPageLoading: undefined,
726
+ });
717
727
  const emits = defineEmits<TableEmits<T>>();
718
728
 
719
729
  const appConfig = useDmsAppConfig() as DmsAppConfig & {
@@ -827,7 +837,7 @@ const paginationState = defineModel<PaginationState>("pagination", {
827
837
  // The first page is on its way: skeleton rows (and placeholders for the
828
838
  // counts) hold the list's place instead of its empty state.
829
839
  const isFirstPageLoading = computed(
830
- () => !!props.loading && !props.data?.length,
840
+ () => props.firstPageLoading ?? (!!props.loading && !props.data?.length),
831
841
  );
832
842
  const skeletonRowCount = computed(() =>
833
843
  Math.min(
@@ -1654,7 +1664,7 @@ defineShortcuts({
1654
1664
  </template>
1655
1665
  <!-- First load: skeleton rows keep the column rhythm instead of
1656
1666
  flashing the empty state. -->
1657
- <template v-else-if="loading">
1667
+ <template v-else-if="isFirstPageLoading">
1658
1668
  <tr
1659
1669
  v-for="rowIndex in skeletonRowCount"
1660
1670
  :key="`skeleton-${rowIndex}`"
@@ -113,7 +113,7 @@ const cardReorder = (index: number): TableViewCardReorder | undefined =>
113
113
  <!-- v2 cards display: an auto-fill grid inside the table card, sharing
114
114
  its selection and its pagination footer. -->
115
115
  <CardGridSkeleton
116
- v-if="props.context.loading && props.context.items.length === 0"
116
+ v-if="props.context.firstPageLoading"
117
117
  :count="props.context.pagination.pageSize"
118
118
  :fields="fieldColumns.length"
119
119
  />
@@ -18,8 +18,22 @@ export interface RowNavigation {
18
18
  export interface RowNavigationSource {
19
19
  rows: () => Data[];
20
20
  rowIdKey: string;
21
+ /**
22
+ * Reads one row by id (the table's `get` route or `fetchRowUrl`): what a
23
+ * container's `showRow` falls back to for a row the table does not list.
24
+ */
25
+ getRow?: (id: string) => Promise<Data | undefined>;
21
26
  }
22
27
 
28
+ /**
29
+ * What a drawer, modal or side panel opened on a row receives to show
30
+ * another row of the same table in its place (`showRow` prop): the row
31
+ * itself, or its id, read from the rows shown or else from the table's route.
32
+ * The table then treats it as the open row: its highlight, its position, its
33
+ * `?record=`. It rejects when the row could not be read.
34
+ */
35
+ export type ShowRow = (row: Data | string) => Promise<void>;
36
+
23
37
  const rowIdOf = (row: Data, rowIdKey: string): string =>
24
38
  String(get(row, rowIdKey));
25
39
 
@@ -25,10 +25,12 @@ import {
25
25
  type RowNavigation,
26
26
  type RowNavigationSource,
27
27
  rowNavigation,
28
+ type ShowRow,
28
29
  } from "./rowNavigation";
29
30
  import {
30
31
  clearWrittenRecord,
31
32
  isDeepLinkAction,
33
+ resolveDeepLinkedRow,
32
34
  writeRecordId,
33
35
  type WrittenRecord,
34
36
  } from "../table-view/utils/recordLink";
@@ -37,6 +39,7 @@ import {
37
39
  closeRowSidePanelOf,
38
40
  openRowSidePanel,
39
41
  rowSidePanelOwnerKey,
42
+ useRowSidePanelContent,
40
43
  } from "#dms-layout/app/build/composables/layout/rowSidePanel";
41
44
  import type { TableUrlScope } from "../table-view/utils/views";
42
45
  import { useActionConfirm } from "../confirm/useActionConfirm";
@@ -145,6 +148,18 @@ interface SidePanelTargetRun extends TargetRunExtras {
145
148
  missingId?: string;
146
149
  }
147
150
 
151
+ /**
152
+ * `show`, run only while the row side panel still shows `session`: a row read
153
+ * after the panel closed, or moved to another run, leaves it alone.
154
+ */
155
+ function whileShown<V>(session: symbol, show: (value: V) => void) {
156
+ return (value: V): void => {
157
+ if (useRowSidePanelContent().value?.session === session) show(value);
158
+ };
159
+ }
160
+
161
+ const NO_ROW_ROUTE = async (): Promise<undefined> => undefined;
162
+
148
163
  const NOT_FOUND_TITLE_KEY = "side_panel.not_found.title";
149
164
  const NOT_FOUND_DESCRIPTION_KEY = "side_panel.not_found.description";
150
165
 
@@ -339,6 +354,31 @@ export function useActionTargets(config: ActionTargetsConfig) {
339
354
  config.onRecordShown?.(id);
340
355
  };
341
356
  };
357
+ // `showRow` given an id: the listed row, else the one the table's route
358
+ // reads. A row its route answered 404 for goes to `showMissing` when the
359
+ // container has a state for it; any other row not found rejects. Only the
360
+ // latest call applies: a read that resolves after a later call is dropped.
361
+ const rowShower = (
362
+ show: (row: Data) => void,
363
+ showMissing?: (id: string) => void,
364
+ ): ShowRow => {
365
+ let latest = 0;
366
+ return async (rowOrId) => {
367
+ const call = ++latest;
368
+ if (typeof rowOrId !== "string") return show(rowOrId);
369
+ const source = config.rowNavigation;
370
+ const found = await resolveDeepLinkedRow(rowOrId, {
371
+ listed: source?.rows(),
372
+ idOf: rowIdOf,
373
+ getRow: source?.getRow ?? NO_ROW_ROUTE,
374
+ });
375
+ if (call !== latest) return;
376
+ if (found.row) return show(found.row);
377
+ if (found.isMissing && showMissing) return showMissing(rowOrId);
378
+ throw new Error(`The row "${rowOrId}" could not be read`);
379
+ };
380
+ };
381
+
342
382
  const rowRecorder = (deepLink?: boolean) => {
343
383
  const recordId = recordRecorder(deepLink);
344
384
  return (row?: Data): void => recordId(rowIdOf(row));
@@ -382,6 +422,10 @@ export function useActionTargets(config: ActionTargetsConfig) {
382
422
  componentId: config.componentId,
383
423
  containerId: run.containerId,
384
424
  selection: run.selection,
425
+ showRow: rowShower(
426
+ whileShown(session, showRow),
427
+ whileShown(session, showMissing),
428
+ ),
385
429
  onSuccessCallback: () => {
386
430
  closeRowSidePanel(session);
387
431
  config.refreshCallback?.();
@@ -389,7 +433,7 @@ export function useActionTargets(config: ActionTargetsConfig) {
389
433
  },
390
434
  onClose: () => recordId(undefined),
391
435
  };
392
- const showRow = (row?: Data) => {
436
+ function showRow(row?: Data) {
393
437
  openRowSidePanel({
394
438
  ...panel,
395
439
  rowTitle: rowTitleOf(run.target, row),
@@ -399,10 +443,13 @@ export function useActionTargets(config: ActionTargetsConfig) {
399
443
  showRow,
400
444
  });
401
445
  recordId(rowIdOf(row));
402
- };
403
- if (!run.missingId) return showRow(run.rowData);
404
- openRowSidePanel({ ...panel, notFound: notFoundOf(run.target), showRow });
405
- recordId(run.missingId);
446
+ }
447
+ function showMissing(id: string) {
448
+ openRowSidePanel({ ...panel, notFound: notFoundOf(run.target), showRow });
449
+ recordId(id);
450
+ }
451
+ if (run.missingId) return showMissing(run.missingId);
452
+ showRow(run.rowData);
406
453
  };
407
454
 
408
455
  // A container opened on a listed row steps through the rows shown (its
@@ -415,6 +462,12 @@ export function useActionTargets(config: ActionTargetsConfig) {
415
462
  const source = config.rowNavigation;
416
463
  let instance: ContainerInstance | undefined;
417
464
  let navigation: RowNavigation | undefined;
465
+ // Set once the container closes: a row read that resolves later neither
466
+ // writes `?record=` back nor patches the closed container.
467
+ let closed = false;
468
+ const showRowById = rowShower((row) => {
469
+ if (!closed) showRow(row);
470
+ });
418
471
  const optionsFor = (row?: Data) => {
419
472
  navigation =
420
473
  row && source ? rowNavigation(source, row, showRow) : undefined;
@@ -425,6 +478,7 @@ export function useActionTargets(config: ActionTargetsConfig) {
425
478
  containerId: run.containerId,
426
479
  rowData: row,
427
480
  navigation,
481
+ showRow: showRowById,
428
482
  selection: run.selection,
429
483
  onSuccessCallback: () => {
430
484
  instance?.close();
@@ -436,14 +490,16 @@ export function useActionTargets(config: ActionTargetsConfig) {
436
490
  function showRow(row: Data) {
437
491
  instance?.patch({
438
492
  componentOptions: optionsFor(row),
439
- componentKey: String(get(row, source!.rowIdKey)),
493
+ componentKey: rowIdOf(row),
440
494
  });
441
495
  recordRow(row);
442
496
  }
443
497
  instance = openContainer(run, component, optionsFor(run.rowData));
444
498
  recordRow(run.rowData);
445
- const stopSteps = navigation ? listenRowSteps(() => navigation) : undefined;
499
+ // `showRow` may move an unlisted row's container onto a listed one.
500
+ const stopSteps = source ? listenRowSteps(() => navigation) : undefined;
446
501
  const settle = () => {
502
+ closed = true;
447
503
  stopSteps?.();
448
504
  recordRow(undefined);
449
505
  };
@@ -1,5 +1,11 @@
1
- import { watch, type Ref } from "vue";
2
- import { readTableUrlKey, type TableUrlScope } from "./utils/views";
1
+ import { router as inertiaRouter } from "@inertiajs/vue3";
2
+ import { onBeforeUnmount, onMounted, watch, type Ref } from "vue";
3
+ import { queryOfUrl } from "./utils/urlFilters";
4
+ import {
5
+ readTableUrlKey,
6
+ type TableUrlScope,
7
+ writeTableUrlKey,
8
+ } from "./utils/views";
3
9
 
4
10
  const TAB_URL_KEY = "tab";
5
11
 
@@ -30,18 +36,42 @@ export function readTableUrlTab(
30
36
  }
31
37
 
32
38
  /**
33
- * Follows the URL's tab after arrival: a link or a `router.replace` naming
34
- * another tab of the same page switches to it without remounting the page.
35
- * A URL dropping the key, or naming a tab the table cannot open in place (an
36
- * unknown id, a link tab), leaves the open tab alone.
39
+ * Keeps the open tab and the URL in step after arrival. A tab the user opens
40
+ * is written to the URL in place (no visit, no history entry), under the key
41
+ * it is read from, so a reload or a shared link opens it again. A link or a
42
+ * `router.replace` naming another tab of the same page switches to it without
43
+ * remounting the page. A URL dropping the key, or naming a tab the table
44
+ * cannot open in place (an unknown id, a link tab), leaves the open tab alone.
37
45
  */
38
46
  export function useTableUrlTab(options: TableUrlTabOptions): void {
47
+ const { activeTabId, urlScope } = options;
48
+
49
+ const follow = (query: Record<string, unknown>) => {
50
+ const tabId = readTableUrlTab(query, urlScope);
51
+ if (!tabId || tabId === activeTabId.value) return;
52
+ if (!options.isOpenable(tabId)) return;
53
+ activeTabId.value = tabId;
54
+ };
55
+
56
+ // The tab is written in place, so the route keeps the value of the last
57
+ // visit: a link back to that tab leaves it unchanged, and only the visit
58
+ // tells it was followed. A `router.replace`, which Inertia does not
59
+ // announce, still moves the route.
39
60
  watch(
40
- () => readTableUrlTab(options.route.query, options.urlScope),
41
- (tabId) => {
42
- if (!tabId || tabId === options.activeTabId.value) return;
43
- if (!options.isOpenable(tabId)) return;
44
- options.activeTabId.value = tabId;
45
- },
61
+ () => readTableUrlTab(options.route.query, urlScope),
62
+ () => follow(options.route.query),
46
63
  );
64
+ let stopFollowingVisits: (() => void) | undefined;
65
+ onMounted(() => {
66
+ stopFollowingVisits = inertiaRouter.on("navigate", (event) =>
67
+ follow(queryOfUrl(event.detail.page.url)),
68
+ );
69
+ });
70
+ onBeforeUnmount(() => stopFollowingVisits?.());
71
+
72
+ watch(activeTabId, (tabId) => {
73
+ if (options.isOpenable(tabId)) {
74
+ writeTableUrlKey(urlScope, TAB_URL_KEY, tabId);
75
+ }
76
+ });
47
77
  }
@@ -103,10 +103,11 @@ export const useTableViewConfig = <T extends Data>(
103
103
  );
104
104
 
105
105
  // Config-derived (SSR-safe; the registry is client-only): the implicit `table`
106
- // display plus explicit `displays`. Built-in defaults
106
+ // display (unless `tableDisplay` is false) plus explicit `displays`. Built-in defaults
107
107
  // (selfManagedData/capabilities) are merged per id.
108
108
  const resolvedDisplays: TableViewDisplayConfig[] = (() => {
109
- const list: TableViewDisplayConfig[] = [{ id: TABLE_DISPLAY_ID }];
109
+ const list: TableViewDisplayConfig[] =
110
+ config.tableDisplay === false ? [] : [{ id: TABLE_DISPLAY_ID }];
110
111
  for (const display of config.displays ?? []) {
111
112
  if (display.id === TABLE_DISPLAY_ID) continue;
112
113
  list.push(display);
@@ -1,5 +1,10 @@
1
1
  import type { CustomRowAction } from "../../../../types/row-action";
2
- import { readTableUrlKey, type TableUrlScope } from "./views";
2
+ import {
3
+ readTableUrlKey,
4
+ tableUrlKey,
5
+ type TableUrlScope,
6
+ writeTableUrlKey,
7
+ } from "./views";
3
8
  import { readFetchNotFound } from "../../../utils/fetchNotFound";
4
9
 
5
10
  /** Query key of the row a `deepLink` action has open. */
@@ -7,9 +12,7 @@ export const RECORD_URL_KEY = "record";
7
12
 
8
13
  /** The key this table writes the open row under: short on a sole table. */
9
14
  export function recordUrlKey(scope: TableUrlScope): string {
10
- return scope.isSoleTableView || !scope.tableId
11
- ? RECORD_URL_KEY
12
- : `${scope.tableId}.${RECORD_URL_KEY}`;
15
+ return tableUrlKey(scope, RECORD_URL_KEY);
13
16
  }
14
17
 
15
18
  /** The row the URL names for this table, if any. */
@@ -29,13 +32,9 @@ export function writeRecordId(
29
32
  scope: TableUrlScope,
30
33
  id?: string,
31
34
  ): WrittenRecord | undefined {
32
- if (typeof window === "undefined") return undefined;
33
- const url = new URL(window.location.href);
34
- const key = recordUrlKey(scope);
35
- if (id) url.searchParams.set(key, id);
36
- else url.searchParams.delete(key);
37
- window.history.replaceState(window.history.state, "", url);
38
- return id ? { pathname: url.pathname, key, id } : undefined;
35
+ const url = writeTableUrlKey(scope, RECORD_URL_KEY, id);
36
+ if (!url || !id) return undefined;
37
+ return { pathname: url.pathname, key: recordUrlKey(scope), id };
39
38
  }
40
39
 
41
40
  /**
@@ -183,6 +183,35 @@ export interface UrlKeyReading {
183
183
  ignoredShortKey?: boolean;
184
184
  }
185
185
 
186
+ /**
187
+ * The key this table writes `key` (`tab`, `record`) under: the short one on
188
+ * its page's only table, else prefixed with its id (`content.tab`).
189
+ */
190
+ export function tableUrlKey(scope: TableUrlScope, key: string): string {
191
+ return scope.isSoleTableView || !scope.tableId
192
+ ? key
193
+ : `${scope.tableId}.${key}`;
194
+ }
195
+
196
+ /**
197
+ * Writes (or, without a value, removes) `key` for this table in the browser's
198
+ * URL in place: no visit, no history entry. Returns the URL written, or
199
+ * `undefined` on a server render.
200
+ */
201
+ export function writeTableUrlKey(
202
+ scope: TableUrlScope,
203
+ key: string,
204
+ value?: string,
205
+ ): URL | undefined {
206
+ if (typeof window === "undefined") return undefined;
207
+ const url = new URL(window.location.href);
208
+ const scopedKey = tableUrlKey(scope, key);
209
+ if (value) url.searchParams.set(scopedKey, value);
210
+ else url.searchParams.delete(scopedKey);
211
+ window.history.replaceState(window.history.state, "", url);
212
+ return url;
213
+ }
214
+
186
215
  /**
187
216
  * The value the URL gives `key` (`view`, `tab`) for this table: under its
188
217
  * prefixed key (`content.view`) or, when it is its page's only table, the
@@ -267,6 +267,11 @@ const kanbanGroupByOptions = kanbanEligibleColumns.map((column) => ({
267
267
  const { displays: registeredDisplays, getById } = useTableViewDisplays();
268
268
 
269
269
  const offeredDisplayIds = new Set(resolvedDisplays.map((d) => d.id));
270
+ // Where a stale or unavailable choice lands: the grid, unless the table view
271
+ // drops it, then its default display.
272
+ const fallbackDisplayId = offeredDisplayIds.has(TABLE_DISPLAY_ID)
273
+ ? TABLE_DISPLAY_ID
274
+ : defaultDisplay;
270
275
  const resolvedDisplay = (id: string): TableViewDisplayConfig | undefined =>
271
276
  resolvedDisplays.find((d) => d.id === id);
272
277
  const optionsForDisplay = (id: string): Record<string, unknown> | undefined =>
@@ -293,7 +298,7 @@ const activeDisplayId = ref<string>(
293
298
  ? defaultDisplay
294
299
  : offeredDisplayIds.has(persistedDisplay)
295
300
  ? persistedDisplay
296
- : TABLE_DISPLAY_ID,
301
+ : fallbackDisplayId,
297
302
  );
298
303
 
299
304
  /** A display the backend config renders by itself, with no client plugin. */
@@ -587,7 +592,7 @@ const {
587
592
  ),
588
593
  display: offeredDisplayIds.has(defaultDisplay)
589
594
  ? defaultDisplay
590
- : TABLE_DISPLAY_ID,
595
+ : fallbackDisplayId,
591
596
  density: props.density ?? DEFAULT_DENSITY,
592
597
  displays: offeredDisplayIds,
593
598
  },
@@ -1296,6 +1301,7 @@ const {
1296
1301
  rowNavigation: {
1297
1302
  rows: () => (shownResults.value ?? []) as Data[],
1298
1303
  rowIdKey: props.rowIdKey ?? ROW_ID_DEFAULT_KEY,
1304
+ getRow: (id) => tableRows.getRow(id) as Promise<Data | undefined>,
1299
1305
  },
1300
1306
  recordScope: urlScope,
1301
1307
  onRecordShown: (id) => recordLink?.shown(id),
@@ -1854,6 +1860,7 @@ const displayContext = computed(
1854
1860
  items: shownResults.value ?? EMPTY_ITEMS,
1855
1861
  columns: props.columns,
1856
1862
  loading: isListLoading.value,
1863
+ firstPageLoading: isFirstPageLoading.value,
1857
1864
  selection: { ids: selectedIds.value, ...displaySelection },
1858
1865
  pagination: {
1859
1866
  pageIndex: paginationState.value.pageIndex,
@@ -2070,14 +2077,14 @@ onMounted(() => {
2070
2077
  checkUrlParameter();
2071
2078
  void loadRelationQuickFilters();
2072
2079
  void refreshLinkTabCounts();
2073
- // The registry is client-only; once hydrated, degrade to the table display if
2074
- // the active id is no longer available (e.g. kanban with no eligible column).
2080
+ // The registry is client-only; once hydrated, degrade to the fallback display
2081
+ // if the active id is no longer available (e.g. kanban with no eligible column).
2075
2082
  if (
2076
- activeDisplayId.value !== TABLE_DISPLAY_ID &&
2083
+ activeDisplayId.value !== fallbackDisplayId &&
2077
2084
  !isConfigRenderedDisplay(activeDisplayId.value) &&
2078
2085
  !availableDisplays.value.some((d) => d.id === activeDisplayId.value)
2079
2086
  ) {
2080
- activeDisplayId.value = TABLE_DISPLAY_ID;
2087
+ activeDisplayId.value = fallbackDisplayId;
2081
2088
  }
2082
2089
  });
2083
2090
  </script>
@@ -2123,6 +2130,7 @@ onMounted(() => {
2123
2130
  :tabs="tabsWithCount"
2124
2131
  :initial-column-visibility="initialVisibility"
2125
2132
  :loading="isListLoading"
2133
+ :first-page-loading="isFirstPageLoading"
2126
2134
  :load-error="listLoadError"
2127
2135
  :data="shownResults || []"
2128
2136
  :pagination-options="{ manualPagination: true, rowCount: data?.total }"
@@ -296,6 +296,8 @@ export interface TableViewConfig<T extends Data>
296
296
  tabs?: TableViewTab[];
297
297
  /** Displays this table view offers, beyond the implicit built-in `table`. */
298
298
  displays?: TableViewDisplayConfig[];
299
+ /** Whether the built-in `table` display is offered. Defaults to `true`. */
300
+ tableDisplay?: boolean;
299
301
  /** Display shown by default when the user has no saved preference. Defaults to "table". */
300
302
  defaultDisplay?: string;
301
303
  /** Expandable rows: caret column + detail band. */
@@ -214,7 +214,13 @@ export interface TableViewDisplayContext<T = Record<string, unknown>> {
214
214
  items: T[];
215
215
  /** Full column metadata (types, labels, accessor keys) — for field rendering. */
216
216
  columns: TableViewColumn[];
217
+ /** The shared query is in flight: a first read or any re-read. */
217
218
  loading: boolean;
219
+ /**
220
+ * No answer has arrived yet: draw a skeleton. A re-read keeps the rows,
221
+ * or the empty state, on screen; swapping either for a skeleton flashes.
222
+ */
223
+ firstPageLoading: boolean;
218
224
 
219
225
  selection: TableViewDisplaySelection;
220
226
  pagination: TableViewDisplayPagination;
@@ -188,7 +188,12 @@ interface Mounted {
188
188
  showTable: Ref<boolean>;
189
189
  }
190
190
 
191
- function mountPanel(shownRows: Ref<Row[]> = ref(rows)): Mounted {
191
+ type ReadRow = (id: string) => Promise<Row | undefined>;
192
+
193
+ function mountPanel(
194
+ shownRows: Ref<Row[]> = ref(rows),
195
+ getRow?: ReadRow,
196
+ ): Mounted {
192
197
  let targets: ReturnType<typeof useActionTargets> | undefined;
193
198
  const showTable = ref(true);
194
199
  const Table = defineComponent({
@@ -201,7 +206,11 @@ function mountPanel(shownRows: Ref<Row[]> = ref(rows)): Mounted {
201
206
  refreshCallback: () => {
202
207
  refreshes += 1;
203
208
  },
204
- rowNavigation: { rows: () => shownRows.value, rowIdKey: "_id" },
209
+ rowNavigation: {
210
+ rows: () => shownRows.value,
211
+ rowIdKey: "_id",
212
+ getRow,
213
+ },
205
214
  recordScope: { tableId: "requests", isSoleTableView: true },
206
215
  });
207
216
  return () => h("table");
@@ -480,6 +489,88 @@ describe("deep link to a row the table does not list", () => {
480
489
  });
481
490
  });
482
491
 
492
+ describe("showing another row from the panel", () => {
493
+ const older: Row = { _id: "r9", name: "Older" };
494
+ const notFoundError = () =>
495
+ Object.assign(new Error("Not Found"), { statusCode: 404 });
496
+ const showRow = (rowOrId: Row | string) =>
497
+ (
498
+ useRowSidePanelContent().value?.componentOptions.showRow as (
499
+ rowOrId: Row | string,
500
+ ) => Promise<void>
501
+ )(rowOrId);
502
+
503
+ it("moves the highlight, the position and ?record= onto a listed row", async () => {
504
+ const { host, targets } = mountPanel();
505
+ targets.handleCustomRowAction(panelAction(), rows[0]);
506
+ await settle();
507
+ await showRow("r3");
508
+ await settle();
509
+ expect(shownName(host)).toBe("Third");
510
+ expect(position(host)).toBe("side_panel.position(3/3)");
511
+ expect(useRowSidePanelContent().value?.rowKey).toBe("r3");
512
+ expect(window.location.search).toBe("?record=r3");
513
+ await showRow(rows[1]!);
514
+ await settle();
515
+ expect(position(host)).toBe("side_panel.position(2/3)");
516
+ });
517
+
518
+ it("reads an unlisted row by id, shown without a position", async () => {
519
+ const getRow = vi.fn(async () => older);
520
+ const { host, targets } = mountPanel(ref(rows), getRow);
521
+ targets.handleCustomRowAction(panelAction(), rows[0]);
522
+ await settle();
523
+ await showRow("r9");
524
+ await settle();
525
+ expect(getRow).toHaveBeenCalledWith("r9");
526
+ expect(shownName(host)).toBe("Older");
527
+ expect(position(host)).toBeUndefined();
528
+ expect(useRowSidePanelContent().value?.rowKey).toBe("r9");
529
+ expect(window.location.search).toBe("?record=r9");
530
+ });
531
+
532
+ it("says a row its route answers 404 for is gone", async () => {
533
+ const { host, targets } = mountPanel(ref(rows), async () => {
534
+ throw notFoundError();
535
+ });
536
+ targets.handleCustomRowAction(panelAction(), rows[0]);
537
+ await settle();
538
+ await showRow("gone");
539
+ await settle();
540
+ expect(query(host, "[data-dms-row-side-panel-not-found]")).not.toBeNull();
541
+ expect(useRowSidePanelContent().value?.rowKey).toBeUndefined();
542
+ expect(window.location.search).toBe("?record=gone");
543
+ });
544
+
545
+ it("rejects a row it cannot read, keeping the row it shows", async () => {
546
+ const { host, targets } = mountPanel();
547
+ targets.handleCustomRowAction(panelAction(), rows[0]);
548
+ await settle();
549
+ await expect(showRow("r9")).rejects.toThrow("r9");
550
+ await settle();
551
+ expect(shownName(host)).toBe("First");
552
+ expect(window.location.search).toBe("?record=r1");
553
+ });
554
+
555
+ it("leaves the panel shut when it closed while the row was read", async () => {
556
+ let answer!: (row: Row) => void;
557
+ const { host, targets } = mountPanel(
558
+ ref(rows),
559
+ () => new Promise((resolve) => (answer = resolve)),
560
+ );
561
+ targets.handleCustomRowAction(panelAction(), rows[0]);
562
+ await settle();
563
+ const shown = showRow("r9");
564
+ closeRowSidePanel();
565
+ answer(older);
566
+ await shown;
567
+ await settle();
568
+ expect(useRowSidePanelContent().value).toBeNull();
569
+ expect(query(host, "[data-dms-side-panel]")).toBeNull();
570
+ expect(window.location.search).toBe("");
571
+ });
572
+ });
573
+
483
574
  describe("side panel on a missing row", () => {
484
575
  const notFound = (host: ParentNode) =>
485
576
  query(host, "[data-dms-row-side-panel-not-found]");
@@ -313,6 +313,120 @@ describe("action targets on a selection and on a listed row", () => {
313
313
  expect(drawer!.patches).toHaveLength(1);
314
314
  });
315
315
 
316
+ it("shows another row in a row's drawer by id, read when unlisted", async () => {
317
+ window.history.replaceState(null, "", "/runs");
318
+ const rows = [{ _id: "r1" }, { _id: "r2" }];
319
+ const older = { _id: "r9" };
320
+ const getRow = vi.fn(async (id: string) =>
321
+ id === "r9" ? older : undefined,
322
+ );
323
+ const targets = mountTargets({
324
+ rowNavigation: { rows: () => rows, rowIdKey: "_id", getRow },
325
+ recordScope: { tableId: "runs", isSoleTableView: true },
326
+ });
327
+ targets.handleCustomRowAction(
328
+ {
329
+ label: "Details",
330
+ deepLink: true,
331
+ target: { type: "drawer", component: { componentName: "RunDrawer" } },
332
+ },
333
+ rows[0],
334
+ );
335
+ await vi.waitFor(() => expect(drawers).toHaveLength(1));
336
+ const [drawer] = drawers;
337
+ const options = drawer!.options.componentOptions as Record<string, unknown>;
338
+ const showRow = options.showRow as (row: object | string) => Promise<void>;
339
+
340
+ await showRow("r9");
341
+ expect(drawer!.patches[0]).toMatchObject({ componentKey: "r9" });
342
+ const patched = drawer!.patches[0]!.componentOptions as Record<
343
+ string,
344
+ unknown
345
+ >;
346
+ expect(patched.rowData).toBe(older);
347
+ expect(patched.navigation).toBeUndefined();
348
+ expect(window.location.search).toBe("?record=r9");
349
+
350
+ await showRow("r2");
351
+ expect(getRow).toHaveBeenCalledTimes(1);
352
+ expect(drawer!.patches[1]).toMatchObject({ componentKey: "r2" });
353
+ expect(window.location.search).toBe("?record=r2");
354
+
355
+ await expect(showRow("r404")).rejects.toThrow("r404");
356
+ expect(drawer!.patches).toHaveLength(2);
357
+ drawer!.settle();
358
+ });
359
+
360
+ const deferredRows = () => {
361
+ const pending = new Map<string, (row: object) => void>();
362
+ const getRow = vi.fn(
363
+ (id: string) =>
364
+ new Promise<object | undefined>((resolve) => pending.set(id, resolve)),
365
+ );
366
+ return { getRow, resolve: (id: string) => pending.get(id)!({ _id: id }) };
367
+ };
368
+
369
+ const openDeepLinkedDrawer = (getRow: (id: string) => Promise<unknown>) => {
370
+ window.history.replaceState(null, "", "/runs");
371
+ const rows = [{ _id: "r1" }];
372
+ const targets = mountTargets({
373
+ rowNavigation: { rows: () => rows, rowIdKey: "_id", getRow },
374
+ recordScope: { tableId: "runs", isSoleTableView: true },
375
+ onRecordShown: vi.fn(),
376
+ });
377
+ targets.handleCustomRowAction(
378
+ {
379
+ label: "Details",
380
+ deepLink: true,
381
+ target: { type: "drawer", component: { componentName: "RunDrawer" } },
382
+ },
383
+ rows[0],
384
+ );
385
+ return targets;
386
+ };
387
+
388
+ const showRowOf = (drawer: (typeof drawers)[number]) =>
389
+ (drawer.options.componentOptions as Record<string, unknown>).showRow as (
390
+ row: object | string,
391
+ ) => Promise<void>;
392
+
393
+ it("drops a row read that resolves after its drawer closed", async () => {
394
+ const rows = deferredRows();
395
+ openDeepLinkedDrawer(rows.getRow);
396
+ await vi.waitFor(() => expect(drawers).toHaveLength(1));
397
+ const [drawer] = drawers;
398
+ const shown = showRowOf(drawer!)("r9");
399
+
400
+ drawer!.settle();
401
+ await vi.waitFor(() => expect(window.location.search).toBe(""));
402
+ rows.resolve("r9");
403
+ await shown;
404
+
405
+ expect(window.location.search).toBe("");
406
+ expect(drawer!.patches).toHaveLength(0);
407
+ expect(drawers).toHaveLength(1);
408
+ });
409
+
410
+ it("applies only the latest of two rows read at once", async () => {
411
+ const rows = deferredRows();
412
+ openDeepLinkedDrawer(rows.getRow);
413
+ await vi.waitFor(() => expect(drawers).toHaveLength(1));
414
+ const [drawer] = drawers;
415
+ const showRow = showRowOf(drawer!);
416
+ const first = showRow("r8");
417
+ const second = showRow("r9");
418
+
419
+ rows.resolve("r9");
420
+ await second;
421
+ rows.resolve("r8");
422
+ await first;
423
+
424
+ expect(drawer!.patches).toHaveLength(1);
425
+ expect(drawer!.patches[0]).toMatchObject({ componentKey: "r9" });
426
+ expect(window.location.search).toBe("?record=r9");
427
+ drawer!.settle();
428
+ });
429
+
316
430
  it("hands a bulk drawer the selection instead of a row", async () => {
317
431
  const targets = mountTargets();
318
432
  targets.handleBulkCustomAction(
@@ -0,0 +1,77 @@
1
+ // @vitest-environment jsdom
2
+ import { computed, createApp, h, reactive, ref, type App } from "vue";
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
+ import CardsDisplay from "../layers/dms-ui/app/build/components/table-view/CardsDisplay.vue";
5
+ import type { TableViewDisplayContext } from "../layers/dms-ui/app/composables/table-view/types/display";
6
+
7
+ vi.mock("../layers/dms-ui/app/build/components/table/Pagination.vue", () => ({
8
+ default: { render: () => null },
9
+ }));
10
+ vi.mock("../layers/dms-ui/app/build/components/table/Empty.vue", () => ({
11
+ default: { render: () => h("p", { "data-test": "empty" }) },
12
+ }));
13
+ vi.mock(
14
+ "../layers/dms-ui/app/build/components/table-view/CardGridSkeleton.vue",
15
+ () => ({ default: { render: () => h("p", { "data-test": "skeleton" }) } }),
16
+ );
17
+
18
+ interface ReadState {
19
+ loading: boolean;
20
+ firstPageLoading: boolean;
21
+ }
22
+
23
+ function createContext(state: ReadState) {
24
+ return reactive({
25
+ items: [],
26
+ columns: [],
27
+ ...state,
28
+ rowIdKey: "_id",
29
+ labelKey: "name",
30
+ options: {},
31
+ pagination: { pageIndex: 0, pageSize: 10, total: 0 },
32
+ selection: { isSelected: () => false, toggle: () => {} },
33
+ actions: { canAdd: false, open: () => {} },
34
+ }) as unknown as TableViewDisplayContext;
35
+ }
36
+
37
+ describe("the cards display of an empty list", () => {
38
+ let app: App | undefined;
39
+ let container: HTMLElement;
40
+
41
+ const mount = (context: TableViewDisplayContext) => {
42
+ app = createApp({ render: () => h(CardsDisplay, { context }) });
43
+ container = document.createElement("div");
44
+ document.body.append(container);
45
+ app.mount(container);
46
+ };
47
+ const shown = (test: string) =>
48
+ container.querySelector(`[data-test="${test}"]`) !== null;
49
+
50
+ beforeEach(() => {
51
+ vi.stubGlobal("computed", computed);
52
+ vi.stubGlobal("h", h);
53
+ vi.stubGlobal("useI18n", () => ({ locale: ref("en"), t: (t: string) => t }));
54
+ vi.stubGlobal("useTranslation", () => ({ processI18n: (t: string) => t }));
55
+ vi.stubGlobal("useDataTypes", () => ({ getDataType: () => undefined }));
56
+ vi.stubGlobal("resolveDmsComponent", () => undefined);
57
+ });
58
+
59
+ afterEach(() => {
60
+ app?.unmount();
61
+ app = undefined;
62
+ container.remove();
63
+ vi.unstubAllGlobals();
64
+ });
65
+
66
+ it("draws the skeleton until the first answer arrives", () => {
67
+ mount(createContext({ loading: true, firstPageLoading: true }));
68
+ expect(shown("skeleton")).toBe(true);
69
+ expect(shown("empty")).toBe(false);
70
+ });
71
+
72
+ it("keeps the empty state on screen while the list is read again", () => {
73
+ mount(createContext({ loading: true, firstPageLoading: false }));
74
+ expect(shown("empty")).toBe(true);
75
+ expect(shown("skeleton")).toBe(false);
76
+ });
77
+ });
@@ -3,18 +3,32 @@
3
3
  * A table view opens the tab its URL names (`?<tableId>.tab=`, or `?tab=` on
4
4
  * a page with a single table view), on arrival and afterwards: a link or a
5
5
  * `router.replace` naming another tab switches to it in place, without
6
- * remounting the page.
6
+ * remounting the page. A tab the user opens is written back to the URL in
7
+ * place, under the same key.
7
8
  */
8
9
  import { readFileSync } from "node:fs";
9
10
  import { resolve } from "node:path";
10
11
  import { createApp, defineComponent, h, nextTick, reactive, ref } from "vue";
11
- import { afterEach, describe, expect, it } from "vitest";
12
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
12
13
  import {
13
14
  readTableUrlTab,
14
15
  useTableUrlTab,
15
16
  } from "../layers/dms-ui/app/build/composables/table-view/useTableUrlTab";
16
17
  import type { TableUrlScope } from "../layers/dms-ui/app/build/composables/table-view/utils/views";
17
18
 
19
+ type NavigateListener = (event: { detail: { page: { url: string } } }) => void;
20
+ const navigateListeners = new Set<NavigateListener>();
21
+
22
+ vi.mock("@inertiajs/vue3", () => ({
23
+ router: {
24
+ on: (type: string, listener: NavigateListener) => {
25
+ if (type !== "navigate") return () => {};
26
+ navigateListeners.add(listener);
27
+ return () => navigateListeners.delete(listener);
28
+ },
29
+ },
30
+ }));
31
+
18
32
  const OPENABLE_TABS = new Set(["all", "active", "archived"]);
19
33
  const mounted: Array<{ unmount: () => void }> = [];
20
34
 
@@ -39,10 +53,23 @@ function mountTable(scope: TableUrlScope, query: Record<string, string>) {
39
53
  return { route, activeTabId };
40
54
  }
41
55
 
56
+ beforeEach(() => {
57
+ window.history.replaceState(null, "", "/runs");
58
+ });
59
+
42
60
  afterEach(() => {
43
61
  mounted.splice(0).forEach((app) => app.unmount());
44
62
  });
45
63
 
64
+ /** A visit of the page already shown: Inertia announces its URL. */
65
+ async function visit(url: string) {
66
+ window.history.pushState(null, "", url);
67
+ navigateListeners.forEach((listener) =>
68
+ listener({ detail: { page: { url } } }),
69
+ );
70
+ await nextTick();
71
+ }
72
+
46
73
  describe("the tab a URL names", () => {
47
74
  it("opens on arrival", () => {
48
75
  const { activeTabId } = mountTable(
@@ -102,6 +129,61 @@ describe("the tab a URL names", () => {
102
129
  });
103
130
  });
104
131
 
132
+ describe("the tab the user opens", () => {
133
+ it("is written to the short key in place on a single table view", async () => {
134
+ window.history.replaceState(null, "", "/runs?page=2");
135
+ const { activeTabId } = mountTable(
136
+ { tableId: "runs", isSoleTableView: true },
137
+ {},
138
+ );
139
+ const entries = window.history.length;
140
+ activeTabId.value = "archived";
141
+ await nextTick();
142
+ expect(window.location.search).toBe("?page=2&tab=archived");
143
+ expect(window.history.length).toBe(entries);
144
+ activeTabId.value = "all";
145
+ await nextTick();
146
+ expect(window.location.search).toBe("?page=2&tab=all");
147
+ });
148
+
149
+ it("is written to the table's own key on a page with several tables", async () => {
150
+ window.history.replaceState(null, "", "/runs?jobs.tab=late");
151
+ const { activeTabId } = mountTable({ tableId: "runs" }, {});
152
+ activeTabId.value = "active";
153
+ await nextTick();
154
+ expect(window.location.search).toBe("?jobs.tab=late&runs.tab=active");
155
+ });
156
+
157
+ it("leaves the URL alone for a tab it cannot open in place", async () => {
158
+ const { activeTabId } = mountTable({ tableId: "runs" }, {});
159
+ activeTabId.value = "docs";
160
+ await nextTick();
161
+ expect(window.location.search).toBe("");
162
+ });
163
+
164
+ it("gives way to a link back to the tab the page was opened on", async () => {
165
+ window.history.replaceState(null, "", "/runs?runs.tab=active");
166
+ const { route, activeTabId } = mountTable(
167
+ { tableId: "runs" },
168
+ { "runs.tab": "active" },
169
+ );
170
+ activeTabId.value = "archived";
171
+ await nextTick();
172
+ expect(window.location.search).toBe("?runs.tab=archived");
173
+ // The route still holds the arrival's tab: only the visit tells.
174
+ await visit("/runs?runs.tab=active");
175
+ expect(route.query["runs.tab"]).toBe("active");
176
+ expect(activeTabId.value).toBe("active");
177
+ });
178
+
179
+ it("stops following visits once the table is gone", async () => {
180
+ const { activeTabId } = mountTable({ tableId: "runs" }, {});
181
+ mounted.splice(0).forEach((app) => app.unmount());
182
+ await visit("/runs?runs.tab=archived");
183
+ expect(activeTabId.value).toBe("all");
184
+ });
185
+ });
186
+
105
187
  it("wires the URL tab into the table view", () => {
106
188
  const source = readFileSync(
107
189
  resolve(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antelopejs/dms",
3
- "version": "0.7.20",
3
+ "version": "0.7.21",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/AntelopeJS/dms.git"
@@ -37,7 +37,7 @@
37
37
  "@antelopejs/interface-data-api": ">=0.2.0 <1.0.0",
38
38
  "@antelopejs/interface-database": ">=0.1.8 <1.0.0",
39
39
  "@antelopejs/interface-database-decorators": ">=0.1.7 <1.0.0",
40
- "@antelopejs/interface-dms": ">=0.5.12 <0.6.0",
40
+ "@antelopejs/interface-dms": ">=0.5.13 <0.6.0",
41
41
  "@antelopejs/interface-email": ">=0.0.5 <1.0.0",
42
42
  "@antelopejs/interface-file-storage": ">=0.1.3 <1.0.0",
43
43
  "archiver": "^7.0.1",