@antelopejs/dms 0.8.0 → 0.8.2
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.
- package/CHANGELOG.md +28 -0
- package/dist/implementations/dms/resolver-timeout.d.ts +1 -0
- package/dist/implementations/dms/resolver-timeout.js +13 -3
- package/dist/implementations/dms/resolver-timeout.js.map +1 -1
- package/dist/test/antelope.test.js +2 -1
- package/dist/test/antelope.test.js.map +1 -1
- package/dist/test/helpers/db.js +19 -11
- package/dist/test/helpers/db.js.map +1 -1
- package/dist/test/integration/auth/two-factor-codes.test.js +4 -2
- package/dist/test/integration/auth/two-factor-codes.test.js.map +1 -1
- package/dist/test/unit/implementations/dms/dynamic-menu-hardening.test.js +8 -5
- package/dist/test/unit/implementations/dms/dynamic-menu-hardening.test.js.map +1 -1
- package/dist/test/unit/implementations/dms/layout-banners.test.js +20 -7
- package/dist/test/unit/implementations/dms/layout-banners.test.js.map +1 -1
- package/frontend-vue/layers/dms-core/app/utils/url-interpolation.ts +16 -5
- package/frontend-vue/layers/dms-ui/app/components/grid/GridRow.vue +14 -6
- package/frontend-vue/layers/dms-ui/app/components/grid/columns.ts +37 -0
- package/frontend-vue/package.json +1 -1
- package/frontend-vue/pnpm-lock.yaml +5 -5
- package/frontend-vue/tests/form-enclosing-form.test.ts +0 -21
- package/frontend-vue/tests/form-load-error.test.ts +0 -22
- package/frontend-vue/tests/grid-responsive-columns.test.ts +30 -31
- package/frontend-vue/tests/nuxt-ui-aria-labels.test.ts +1 -28
- package/frontend-vue/tests/period-live-pause.test.ts +0 -13
- package/frontend-vue/tests/period-max-days.test.ts +0 -14
- package/frontend-vue/tests/row-side-panel.test.ts +0 -12
- package/frontend-vue/tests/table-cell-wrap.test.ts +0 -12
- package/frontend-vue/tests/table-density-menu.test.ts +0 -21
- package/frontend-vue/tests/table-live-hold.test.ts +0 -44
- package/frontend-vue/tests/table-row-hover.test.ts +0 -10
- package/frontend-vue/tests/table-scroll-area.test.ts +0 -8
- package/frontend-vue/tests/table-view-card-props.test.ts +0 -17
- package/frontend-vue/tests/table-view-cards-reorder.test.ts +0 -16
- package/frontend-vue/tests/table-view-grouped.test.ts +0 -55
- package/frontend-vue/tests/table-view-pinned-rows.test.ts +0 -15
- package/frontend-vue/tests/table-view-record-link.test.ts +0 -17
- package/frontend-vue/tests/table-view-refresh.test.ts +0 -17
- package/frontend-vue/tests/table-view-url-tab.test.ts +0 -14
- package/frontend-vue/tests/tone.test.ts +0 -19
- package/frontend-vue/tests/url-interpolation.test.ts +12 -1
- package/package.json +5 -5
- package/dist/test/integration/auth/login.test.d.ts +0 -1
- package/dist/test/integration/auth/login.test.js +0 -57
- package/dist/test/integration/auth/login.test.js.map +0 -1
- package/dist/test/integration/auth/refresh.test.d.ts +0 -1
- package/dist/test/integration/auth/refresh.test.js +0 -59
- package/dist/test/integration/auth/refresh.test.js.map +0 -1
- package/dist/test/integration/auth/sessions.test.d.ts +0 -1
- package/dist/test/integration/auth/sessions.test.js +0 -58
- package/dist/test/integration/auth/sessions.test.js.map +0 -1
- package/dist/test/integration/auth/signup.test.d.ts +0 -1
- package/dist/test/integration/auth/signup.test.js +0 -99
- package/dist/test/integration/auth/signup.test.js.map +0 -1
- package/dist/test/unit/skills/dms-dev-script.test.d.ts +0 -1
- package/dist/test/unit/skills/dms-dev-script.test.js +0 -137
- package/dist/test/unit/skills/dms-dev-script.test.js.map +0 -1
- package/frontend-vue/tests/form-reload-on-reopen.test.ts +0 -20
- package/frontend-vue/tests/icon-aria-hidden.test.ts +0 -29
- package/frontend-vue/tests/skeleton-paragraph.test.ts +0 -30
- package/frontend-vue/tests/tab-slot-spacing.test.ts +0 -29
- package/frontend-vue/tests/table-empty-body-viewport.test.ts +0 -25
- package/frontend-vue/tests/table-header-placement.test.ts +0 -21
- package/frontend-vue/tests/vue-inventory.test.ts +0 -107
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
* leaves with its component, and focusing a field by id. A page opened on a
|
|
6
6
|
* field's anchor focuses that field.
|
|
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, describe, expect, it, vi } from "vitest";
|
|
12
10
|
import {
|
|
@@ -120,22 +118,3 @@ describe("a field's anchor", () => {
|
|
|
120
118
|
expect(linkedFieldId("#%E0%A4%A", FIELD_IDS)).toBe(undefined);
|
|
121
119
|
});
|
|
122
120
|
});
|
|
123
|
-
|
|
124
|
-
describe("the Form", () => {
|
|
125
|
-
const formSource = readFileSync(
|
|
126
|
-
resolve(__dirname, "../layers/dms-ui/app/components/form/Form.vue"),
|
|
127
|
-
"utf8",
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
it("offers its fields the submit, its validators and the focus", () => {
|
|
131
|
-
expect(formSource).toMatch(
|
|
132
|
-
/provide\(FORM_HANDLE_KEY, \{\s*submit: async \(\) => \{\s*await form\.value\?\.submit\(\);\s*\},\s*registerValidator,\s*revealField,\s*\}\);/,
|
|
133
|
-
);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it("follows the anchor of a field once mounted, a page form only", () => {
|
|
137
|
-
expect(formSource).toMatch(
|
|
138
|
-
/if \(!inFormContainer && !container\) \{\s*const followFieldAnchor = \(\) => \{\s*const fieldId = linkedFieldId\(window\.location\.hash, allFieldIds\.value\);/,
|
|
139
|
-
);
|
|
140
|
-
});
|
|
141
|
-
});
|
|
@@ -173,25 +173,3 @@ describe("the load error a form shows", () => {
|
|
|
173
173
|
}
|
|
174
174
|
});
|
|
175
175
|
});
|
|
176
|
-
|
|
177
|
-
describe("a form whose record did not load", () => {
|
|
178
|
-
const formSource = readFileSync(
|
|
179
|
-
new URL("../layers/dms-ui/app/components/form/Form.vue", import.meta.url),
|
|
180
|
-
"utf8",
|
|
181
|
-
);
|
|
182
|
-
|
|
183
|
-
it("shows the load error instead of its fields", () => {
|
|
184
|
-
expect(formSource).toMatch(
|
|
185
|
-
/<div v-if="loadFailure"[^>]*>\s*<DmsFormLoadError[\s\S]*?<\/div>\s*<div v-else :class="surfaceClasses\.body">/,
|
|
186
|
-
);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it("cannot be saved: no save bar, and a submit is refused", () => {
|
|
190
|
-
expect(formSource).toMatch(
|
|
191
|
-
/<DmsSaveBar\s+v-if="canSave && !isInstant && !loadFailure"/,
|
|
192
|
-
);
|
|
193
|
-
expect(formSource).toMatch(
|
|
194
|
-
/function onSubmit\([^)]*\) \{\s*if \(loadFailure\.value \|\| !runValidators\(\)\) return;/,
|
|
195
|
-
);
|
|
196
|
-
});
|
|
197
|
-
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
1
|
import { describe, expect, it } from "vitest";
|
|
3
2
|
import {
|
|
4
3
|
GRID_TRACKS_VAR,
|
|
4
|
+
gridCollapsedCellStyles,
|
|
5
5
|
gridColumnsTemplate,
|
|
6
6
|
gridResponsiveStyles,
|
|
7
7
|
gridScopeId,
|
|
@@ -10,12 +10,6 @@ import {
|
|
|
10
10
|
const GAP = "1rem";
|
|
11
11
|
const MIN = "240px";
|
|
12
12
|
|
|
13
|
-
const sourceOf = (file: string) =>
|
|
14
|
-
readFileSync(
|
|
15
|
-
new URL(`../layers/dms-ui/app/components/grid/${file}`, import.meta.url),
|
|
16
|
-
"utf8",
|
|
17
|
-
);
|
|
18
|
-
|
|
19
13
|
// Guards the exact-fit case: at the width where the track floor is precisely
|
|
20
14
|
// one Nth of the container, binary floats land a hair under the integer.
|
|
21
15
|
const FIT_EPSILON = 1e-6;
|
|
@@ -104,20 +98,6 @@ describe("grid column template", () => {
|
|
|
104
98
|
});
|
|
105
99
|
});
|
|
106
100
|
|
|
107
|
-
describe("grid components", () => {
|
|
108
|
-
it("drive both the container and its rows from the shared template", () => {
|
|
109
|
-
for (const file of ["Grid.vue", "GridRow.vue"]) {
|
|
110
|
-
const source = sourceOf(file);
|
|
111
|
-
expect(source).toContain("gridColumnsTemplate(");
|
|
112
|
-
expect(source).not.toMatch(/repeat\(\$\{/);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("keeps rows spanning the full grid width", () => {
|
|
117
|
-
expect(sourceOf("GridRow.vue")).toContain('gridColumn: "1 / -1"');
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
|
|
121
101
|
describe("grid responsive styles", () => {
|
|
122
102
|
const scope = gridScopeId("grid-child-row1");
|
|
123
103
|
|
|
@@ -167,15 +147,34 @@ describe("grid responsive styles", () => {
|
|
|
167
147
|
});
|
|
168
148
|
});
|
|
169
149
|
|
|
170
|
-
describe("grid
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
150
|
+
describe("grid collapsed cell styles", () => {
|
|
151
|
+
const scope = gridScopeId("grid-child-row2");
|
|
152
|
+
const cells = `[data-dms-grid="${scope}"] > `;
|
|
153
|
+
|
|
154
|
+
it("takes no track for a cell its block leaves empty, but keeps spacers", () => {
|
|
155
|
+
const [hide] = gridCollapsedCellStyles(scope, 2).split("\n");
|
|
156
|
+
expect(hide).toBe(`${cells}:empty:not(.dms-spacer) { display: none; }`);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("widens the last filled cell over each run of trailing empty cells", () => {
|
|
160
|
+
const rules = gridCollapsedCellStyles(scope, 3).split("\n");
|
|
161
|
+
expect(rules).toHaveLength(3);
|
|
162
|
+
expect(rules[1]).toContain(
|
|
163
|
+
":has(+ :empty:not(.dms-spacer) + style:last-child)",
|
|
164
|
+
);
|
|
165
|
+
expect(rules[1]).toContain(
|
|
166
|
+
`grid-column: span min(2, var(${GRID_TRACKS_VAR}, 2));`,
|
|
167
|
+
);
|
|
168
|
+
expect(rules[2]).toContain(
|
|
169
|
+
":has(+ :empty:not(.dms-spacer) + :empty:not(.dms-spacer) + style:last-child)",
|
|
170
|
+
);
|
|
171
|
+
expect(rules[2]).toContain(
|
|
172
|
+
`grid-column: span min(3, var(${GRID_TRACKS_VAR}, 3));`,
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("gives a single-column row no rules", () => {
|
|
177
|
+
expect(gridCollapsedCellStyles(scope, 1)).toBe("");
|
|
178
|
+
expect(gridCollapsedCellStyles(scope, 0)).toBe("");
|
|
180
179
|
});
|
|
181
180
|
});
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
|
-
import { join, relative } from "node:path";
|
|
3
1
|
import { describe, expect, it } from "vitest";
|
|
4
2
|
import coreEn from "../layers/dms-core/i18n/locales/core-en-GB.json";
|
|
5
3
|
import coreFr from "../layers/dms-core/i18n/locales/core-fr-FR.json";
|
|
@@ -12,13 +10,6 @@ const LABELLED_TAGS = {
|
|
|
12
10
|
} as const;
|
|
13
11
|
const LOCALES = { "en-GB": coreEn, "fr-FR": coreFr };
|
|
14
12
|
|
|
15
|
-
function walk(directory: string): string[] {
|
|
16
|
-
return readdirSync(directory).flatMap((name) => {
|
|
17
|
-
const path = join(directory, name);
|
|
18
|
-
return statSync(path).isDirectory() ? walk(path) : [path];
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
|
|
22
13
|
function lookup(messages: unknown, key: string): unknown {
|
|
23
14
|
return key
|
|
24
15
|
.split(".")
|
|
@@ -29,25 +20,7 @@ function lookup(messages: unknown, key: string): unknown {
|
|
|
29
20
|
}
|
|
30
21
|
|
|
31
22
|
describe("Nuxt UI hard-coded aria-labels", () => {
|
|
32
|
-
const
|
|
33
|
-
.filter((path) => path.endsWith(".vue"))
|
|
34
|
-
.map((path) => ({
|
|
35
|
-
path: relative(process.cwd(), path),
|
|
36
|
-
text: readFileSync(path, "utf8"),
|
|
37
|
-
}));
|
|
38
|
-
|
|
39
|
-
for (const [tag, key] of Object.entries(LABELLED_TAGS)) {
|
|
40
|
-
it(`binds a translated aria-label on every ${tag}`, () => {
|
|
41
|
-
const tagPattern = new RegExp(`<${tag}\\b[^>]*?>`, "gs");
|
|
42
|
-
const offenders = sources.flatMap(({ path, text }) =>
|
|
43
|
-
[...text.matchAll(tagPattern)]
|
|
44
|
-
.filter(([found]) => !found.includes(`:aria-label="t('${key}')"`))
|
|
45
|
-
.map(() => path),
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
expect(offenders).toEqual([]);
|
|
49
|
-
});
|
|
50
|
-
|
|
23
|
+
for (const key of Object.values(LABELLED_TAGS)) {
|
|
51
24
|
it(`translates ${key} in every locale`, () => {
|
|
52
25
|
for (const messages of Object.values(LOCALES)) {
|
|
53
26
|
expect(lookup(messages, key)).toBeTypeOf("string");
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
* so nothing refetches on the clock; a period the user picks still applies at
|
|
6
6
|
* once, and resuming catches the skipped minutes up in one read.
|
|
7
7
|
*/
|
|
8
|
-
import { readFileSync } from "node:fs";
|
|
9
|
-
import { resolve } from "node:path";
|
|
10
8
|
import { createApp, defineComponent, h, ref } from "vue";
|
|
11
9
|
import { afterEach, beforeEach, expect, it, vi } from "vitest";
|
|
12
10
|
import { createPageLiveController } from "../layers/dms-ui/app/build/composables/live/pageLiveController";
|
|
@@ -107,14 +105,3 @@ it("holds the period while paused, but follows the user's pick", async () => {
|
|
|
107
105
|
expect(requests).toHaveLength(3);
|
|
108
106
|
expect(period.state.value.key).not.toBe(heldKey);
|
|
109
107
|
});
|
|
110
|
-
|
|
111
|
-
it("holds the selector's period on the page's pause", () => {
|
|
112
|
-
const source = readFileSync(
|
|
113
|
-
resolve(
|
|
114
|
-
__dirname,
|
|
115
|
-
"../layers/dms-ui/app/components/period/PeriodSelector.vue",
|
|
116
|
-
),
|
|
117
|
-
"utf8",
|
|
118
|
-
);
|
|
119
|
-
expect(source).toContain("holdClock: () => isPagePaused.value,");
|
|
120
|
-
});
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
* from the date fields or a long preset is cut to it.
|
|
6
6
|
*/
|
|
7
7
|
import { CalendarDate } from "@internationalized/date";
|
|
8
|
-
import { readFileSync } from "node:fs";
|
|
9
|
-
import { resolve } from "node:path";
|
|
10
8
|
import { createApp, defineComponent, h, nextTick, type App } from "vue";
|
|
11
9
|
import { afterEach, describe, expect, it } from "vitest";
|
|
12
10
|
import PeriodRangeCalendar from "../layers/dms-ui/app/build/components/period/PeriodRangeCalendar.vue";
|
|
@@ -114,15 +112,3 @@ describe("the range calendar with maxDays", () => {
|
|
|
114
112
|
expect(day(host, "2026-09-30").hasAttribute("data-disabled")).toBe(false);
|
|
115
113
|
});
|
|
116
114
|
});
|
|
117
|
-
|
|
118
|
-
it("hands maxDays to the calendar and cuts the date fields to it", () => {
|
|
119
|
-
const source = readFileSync(
|
|
120
|
-
resolve(
|
|
121
|
-
__dirname,
|
|
122
|
-
"../layers/dms-ui/app/components/period/PeriodSelector.vue",
|
|
123
|
-
),
|
|
124
|
-
"utf8",
|
|
125
|
-
);
|
|
126
|
-
expect(source).toContain(':max-days="maxDays"');
|
|
127
|
-
expect(source).toContain("limitCalendarRange(range, props.maxDays, anchor)");
|
|
128
|
-
});
|
|
@@ -828,16 +828,4 @@ describe("open row highlight", () => {
|
|
|
828
828
|
"data-[open=true]:[&>td:first-child]:shadow-[inset_2px_0_0_0_var(--ui-primary)]",
|
|
829
829
|
);
|
|
830
830
|
});
|
|
831
|
-
|
|
832
|
-
it("marks the open row and wires both pointer events to the click mode", () => {
|
|
833
|
-
expect(tableSource).toContain(
|
|
834
|
-
':data-open="isOpenRow(row.original) || undefined"',
|
|
835
|
-
);
|
|
836
|
-
expect(tableSource).toContain(
|
|
837
|
-
'@click="handleRowPointer($event, row.original)"',
|
|
838
|
-
);
|
|
839
|
-
expect(tableSource).toContain(
|
|
840
|
-
'@dblclick="handleRowPointer($event, row.original)"',
|
|
841
|
-
);
|
|
842
|
-
});
|
|
843
831
|
});
|
|
@@ -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
|
-
});
|
|
@@ -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", () => {
|
|
@@ -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
|
-
});
|