@antelopejs/dms 0.7.5 → 0.7.6
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 +21 -0
- package/dist/implementations/dms-base/export-jobs.d.ts +4 -1
- package/dist/implementations/dms-base/export-jobs.js +10 -6
- package/dist/implementations/dms-base/export-jobs.js.map +1 -1
- package/dist/implementations/dms-base/search-route.js +20 -11
- package/dist/implementations/dms-base/search-route.js.map +1 -1
- package/dist/test/attachment-host/capabilities.d.ts +2 -0
- package/dist/test/attachment-host/capabilities.js +11 -0
- package/dist/test/attachment-host/capabilities.js.map +1 -1
- package/dist/test/integration/table-view-capabilities.test.js +23 -5
- package/dist/test/integration/table-view-capabilities.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/data-types/field-types.test.js +29 -0
- package/dist/test/unit/interfaces/dms-base/data-types/field-types.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/display-blocks-catalog.test.js +14 -0
- package/dist/test/unit/interfaces/dms-base/display-blocks-catalog.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/export-jobs-binding.test.d.ts +1 -0
- package/dist/test/unit/interfaces/dms-base/export-jobs-binding.test.js +49 -0
- package/dist/test/unit/interfaces/dms-base/export-jobs-binding.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms-base/table-view-hidden-columns.test.d.ts +1 -0
- package/dist/test/unit/interfaces/dms-base/table-view-hidden-columns.test.js +105 -0
- package/dist/test/unit/interfaces/dms-base/table-view-hidden-columns.test.js.map +1 -0
- package/dist/test/unit/request-tenant-guards.test.js +38 -0
- package/dist/test/unit/request-tenant-guards.test.js.map +1 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/accounts.vue +9 -3
- package/frontend-vue/layers/dms-core/i18n/locales/core-en-GB.json +6 -0
- package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +6 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/CommandPaletteAssistantAnswer.vue +1 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardHeader.vue +2 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/Footer.vue +1 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/PageSkeleton.vue +13 -4
- package/frontend-vue/layers/dms-layout/app/build/components/layout/RolePreviewBar.vue +9 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/members/MemberRolePicker.vue +5 -1
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/profile/ProfilePersonalInfo.vue +1 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/profile/ProfileSummaryRow.vue +4 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/RoleEditor.vue +1 -1
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityEmail.vue +4 -1
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityPassword.vue +4 -1
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecuritySessions.vue +1 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityTwoFactor.vue +19 -4
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/theme/AppearanceDeveloper.vue +1 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/theme/CssVariablesList.vue +6 -3
- package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +20 -5
- package/frontend-vue/layers/dms-ui/app/build/components/chart/internal/ChartSkeleton.vue +4 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/drawer/DynamicDrawer.vue +10 -2
- package/frontend-vue/layers/dms-ui/app/build/components/containers/modal/DynamicModal.vue +7 -2
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormSectionNav.vue +87 -40
- package/frontend-vue/layers/dms-ui/app/build/components/skeleton/RowSkeleton.vue +8 -1
- package/frontend-vue/layers/dms-ui/app/build/components/stat/StatCell.vue +54 -13
- package/frontend-vue/layers/dms-ui/app/build/components/table/Pagination.vue +9 -2
- package/frontend-vue/layers/dms-ui/app/build/components/table/Table.vue +8 -0
- package/frontend-vue/layers/dms-ui/app/build/components/table/Tabs.vue +1 -0
- package/frontend-vue/layers/dms-ui/app/build/components/table/Views.vue +1 -0
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardGridSkeleton.vue +22 -5
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/ExpandedRowDetail.vue +1 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +3 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formFooter.ts +3 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formSections.ts +25 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts +20 -2
- package/frontend-vue/layers/dms-ui/app/build/utils/characterCount.ts +8 -0
- package/frontend-vue/layers/dms-ui/app/components/activity-feed/ActivityFeed.vue +4 -1
- package/frontend-vue/layers/dms-ui/app/components/blocks/CodeBlock.vue +3 -0
- package/frontend-vue/layers/dms-ui/app/components/blocks/NavCardGridBlock.vue +13 -5
- package/frontend-vue/layers/dms-ui/app/components/card/NavCard.vue +7 -1
- package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +10 -3
- package/frontend-vue/layers/dms-ui/app/components/confirm/ConfirmModal.vue +8 -0
- package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +4 -3
- package/frontend-vue/layers/dms-ui/app/components/form/components/Cascader.vue +6 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/Display.vue +5 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputNumber.vue +27 -3
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputText.vue +24 -2
- package/frontend-vue/layers/dms-ui/app/components/form/components/RichText.vue +13 -4
- package/frontend-vue/layers/dms-ui/app/components/form/components/Textarea.vue +27 -2
- package/frontend-vue/layers/dms-ui/app/components/key-value-list/KeyValueList.vue +8 -4
- package/frontend-vue/layers/dms-ui/app/components/kpi/KpiCard.vue +12 -4
- package/frontend-vue/layers/dms-ui/app/components/meter/MeterBlock.vue +15 -4
- package/frontend-vue/layers/dms-ui/app/components/save-bar/SaveBar.vue +69 -42
- package/frontend-vue/layers/dms-ui/app/components/tab/Tab.vue +15 -1
- package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +13 -3
- package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +45 -9
- package/frontend-vue/layers/dms-ui/app/composables/form/types/field.ts +2 -0
- package/frontend-vue/layers/dms-ui/app/composables/tab/types/props.ts +5 -0
- package/frontend-vue/tests/confirm-dialog-fields.test.ts +12 -0
- package/frontend-vue/tests/drawer-target-direction.test.ts +1 -0
- package/frontend-vue/tests/field-suffix-count.test.ts +120 -0
- package/frontend-vue/tests/form-section-nav-groups.test.ts +116 -0
- package/frontend-vue/tests/nav-card-badge.test.ts +5 -1
- package/frontend-vue/tests/nuxt-ui-aria-labels.test.ts +58 -0
- package/frontend-vue/tests/page-fill-height.test.ts +2 -2
- package/frontend-vue/tests/save-bar.test.ts +49 -0
- package/frontend-vue/tests/stat-group-detail-lines.test.ts +63 -0
- package/frontend-vue/tests/tab-fit.test.ts +99 -0
- package/frontend-vue/tests/table-cell-sublines.test.ts +15 -0
- package/frontend-vue/tests/table-cell-wrap.test.ts +24 -2
- package/frontend-vue/tests/table-row-action-errors.test.ts +26 -0
- package/frontend-vue/tests/top-list-card-actions.test.ts +91 -0
- package/package.json +2 -2
- package/skills/dms/SKILL.md +11 -1
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
/**
|
|
3
|
+
* `fit` draws a horizontal pill list as wide as its tabs: the list stops
|
|
4
|
+
* taking the column's width and the triggers stop growing to share it. A
|
|
5
|
+
* link list, a vertical one and a list without `fit` keep their classes.
|
|
6
|
+
*/
|
|
7
|
+
import * as vue from "vue";
|
|
8
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
9
|
+
import { AxeOrientation } from "../layers/dms-core/app/types/orientation";
|
|
10
|
+
import { Color } from "../layers/dms-core/app/types/color";
|
|
11
|
+
import { Size } from "../layers/dms-core/app/types/size";
|
|
12
|
+
import { TabVariant } from "../layers/dms-ui/app/composables/tab/types/props";
|
|
13
|
+
|
|
14
|
+
vi.mock("../layers/dms-ui/app/composables/chart/useChartFetch", () => ({
|
|
15
|
+
useChartFetch: () => ({ data: vue.ref(null) }),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
interface TabsUi {
|
|
19
|
+
list?: unknown;
|
|
20
|
+
trigger?: unknown;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let received: TabsUi = {};
|
|
24
|
+
let app: vue.App | undefined;
|
|
25
|
+
|
|
26
|
+
const Tabs = vue.defineComponent({
|
|
27
|
+
props: { ui: { type: Object, default: () => ({}) } },
|
|
28
|
+
setup: (props) => () => {
|
|
29
|
+
received = props.ui as TabsUi;
|
|
30
|
+
return vue.h("div");
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const classesOf = (value: unknown): string =>
|
|
35
|
+
[value].flat(Infinity).filter(Boolean).join(" ");
|
|
36
|
+
|
|
37
|
+
async function mount(props: Record<string, unknown>): Promise<TabsUi> {
|
|
38
|
+
const { default: Tab } = await import(
|
|
39
|
+
"../layers/dms-ui/app/components/tab/Tab.vue"
|
|
40
|
+
);
|
|
41
|
+
app = vue.createApp({
|
|
42
|
+
render: () =>
|
|
43
|
+
vue.h(Tab, {
|
|
44
|
+
items: [
|
|
45
|
+
{ label: "Activity", slot: "activity" },
|
|
46
|
+
{ label: "Logins", slot: "logins" },
|
|
47
|
+
],
|
|
48
|
+
componentId: "tabs",
|
|
49
|
+
pageId: "dashboard",
|
|
50
|
+
...props,
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
53
|
+
app.component("UTabs", Tabs);
|
|
54
|
+
app.mount(document.createElement("div"));
|
|
55
|
+
await vue.nextTick();
|
|
56
|
+
return received;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
beforeEach(() => {
|
|
60
|
+
for (const name of ["computed", "ref", "useTemplateRef"] as const) {
|
|
61
|
+
vi.stubGlobal(name, vue[name]);
|
|
62
|
+
}
|
|
63
|
+
vi.stubGlobal("AxeOrientation", AxeOrientation);
|
|
64
|
+
vi.stubGlobal("Color", Color);
|
|
65
|
+
vi.stubGlobal("Size", Size);
|
|
66
|
+
vi.stubGlobal("useI18n", () => ({ locale: vue.ref("en-US"), t: String }));
|
|
67
|
+
vi.stubGlobal("useTranslation", () => ({ processI18n: String }));
|
|
68
|
+
vi.stubGlobal("useWatch", () => ({ state: vue.ref({}) }));
|
|
69
|
+
vi.stubGlobal("useTab", () => ({ activeTab: vue.ref("0") }));
|
|
70
|
+
vi.stubGlobal("useTabShortcuts", () => {});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
afterEach(() => {
|
|
74
|
+
app?.unmount();
|
|
75
|
+
vi.unstubAllGlobals();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe("Tab fit", () => {
|
|
79
|
+
it("hugs a horizontal pill list to its tabs", async () => {
|
|
80
|
+
const ui = await mount({ fit: true });
|
|
81
|
+
expect(classesOf(ui.list)).toContain("w-fit");
|
|
82
|
+
expect(classesOf(ui.trigger)).toContain("grow-0");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("keeps the full width by default", async () => {
|
|
86
|
+
const ui = await mount({});
|
|
87
|
+
expect(classesOf(ui.list)).not.toContain("w-fit");
|
|
88
|
+
expect(classesOf(ui.trigger)).toBe("");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it.each([
|
|
92
|
+
{ variant: TabVariant.link },
|
|
93
|
+
{ orientation: AxeOrientation.vertical },
|
|
94
|
+
])("leaves %o as it was", async (props) => {
|
|
95
|
+
const ui = await mount({ fit: true, ...props });
|
|
96
|
+
expect(classesOf(ui.list)).not.toContain("w-fit");
|
|
97
|
+
expect(classesOf(ui.trigger)).toBe("");
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -448,6 +448,21 @@ describe("identity sub-line", () => {
|
|
|
448
448
|
expect(sub?.className).toContain("text-muted");
|
|
449
449
|
});
|
|
450
450
|
|
|
451
|
+
it("reads each row's tile icon from iconField, falling back to icon", async () => {
|
|
452
|
+
const identity = await identityFormatter();
|
|
453
|
+
const options = { iconField: "icon", icon: "i-ph-puzzle-piece" };
|
|
454
|
+
const tileOf = (row: Record<string, unknown>) =>
|
|
455
|
+
render(() =>
|
|
456
|
+
(identity.default as Formatter)("Exports", "en", options, row),
|
|
457
|
+
)
|
|
458
|
+
.querySelector("[data-icon]")
|
|
459
|
+
?.getAttribute("data-icon");
|
|
460
|
+
|
|
461
|
+
expect(tileOf({ icon: "i-ph-export" })).toBe("i-ph-export");
|
|
462
|
+
expect(tileOf({ icon: "" })).toBe("i-ph-puzzle-piece");
|
|
463
|
+
expect(tileOf({})).toBe("i-ph-puzzle-piece");
|
|
464
|
+
});
|
|
465
|
+
|
|
451
466
|
it("draws an empty issuer as its translated empty label and icon", async () => {
|
|
452
467
|
const identity = await identityFormatter();
|
|
453
468
|
const cell = render(() =>
|
|
@@ -38,8 +38,30 @@ describe("Table grid cell layout theme", () => {
|
|
|
38
38
|
|
|
39
39
|
it("preserves pinned cell and table layout classes", () => {
|
|
40
40
|
expect(theme({ cellWrap: true }).table()).toBe(theme().table());
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
const pinned = theme().rowCell({ pinned: "left" }).split(" ");
|
|
42
|
+
const wrapped = theme()
|
|
43
|
+
.rowCell({ pinned: "left", cellWrap: true })
|
|
44
|
+
.split(" ");
|
|
45
|
+
expect(wrapped.filter((name: string) => !pinned.includes(name))).toEqual([
|
|
46
|
+
"py-2.5",
|
|
47
|
+
]);
|
|
48
|
+
expect(pinned.filter((name: string) => !wrapped.includes(name))).toEqual([
|
|
49
|
+
"py-0",
|
|
50
|
+
]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("pads only a wrapped cell, so its lines clear the row's hairlines", () => {
|
|
54
|
+
expect(theme().rowCell({ cellWrap: true })).toContain("py-2.5");
|
|
55
|
+
expect(theme().rowCell({ cellWrap: true })).toContain("h-11");
|
|
56
|
+
expect(theme().rowCell({ cellWrap: true })).not.toContain("py-0");
|
|
57
|
+
for (const cellWrap of [undefined, false]) {
|
|
58
|
+
expect(theme().rowCell({ cellWrap })).toContain("py-0");
|
|
59
|
+
}
|
|
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*\}\)/,
|
|
43
65
|
);
|
|
44
66
|
});
|
|
45
67
|
|
|
@@ -73,6 +73,11 @@ function fetchError(status: number | undefined, data?: unknown) {
|
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
const shownRows = [
|
|
77
|
+
{ _id: "t1", name: "Write the report" },
|
|
78
|
+
{ _id: "t2", name: "Review the draft" },
|
|
79
|
+
];
|
|
80
|
+
|
|
76
81
|
async function rowActions() {
|
|
77
82
|
const { useTableRowActions } = await import(
|
|
78
83
|
"../layers/dms-ui/app/build/composables/table-view/useTableViewRowActions"
|
|
@@ -84,6 +89,7 @@ async function rowActions() {
|
|
|
84
89
|
pageId: "tasks",
|
|
85
90
|
formComponents: {},
|
|
86
91
|
refreshCallback: refresh,
|
|
92
|
+
rowNavigation: { rows: () => shownRows, rowIdKey: "_id" },
|
|
87
93
|
});
|
|
88
94
|
}
|
|
89
95
|
|
|
@@ -298,6 +304,26 @@ describe("Table row actions: refusals", () => {
|
|
|
298
304
|
});
|
|
299
305
|
});
|
|
300
306
|
|
|
307
|
+
it("words a fixed dialog with the fields of the one row it deletes", async () => {
|
|
308
|
+
api.mockResolvedValue(1);
|
|
309
|
+
const { deleteRows } = await rowActions();
|
|
310
|
+
|
|
311
|
+
await deleteRows(["t1"], { confirm: { title: "Delete {name}?" } });
|
|
312
|
+
|
|
313
|
+
expect(dialogs[0]!.options.title).toBe("Delete Write the report?");
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it("words a fixed dialog with the count of the rows it deletes", async () => {
|
|
317
|
+
api.mockResolvedValue(2);
|
|
318
|
+
const { deleteRows } = await rowActions();
|
|
319
|
+
|
|
320
|
+
await deleteRows(["t1", "t2"], {
|
|
321
|
+
confirm: { title: "Delete {count} tasks?" },
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
expect(dialogs[0]!.options.title).toBe("Delete 2 tasks?");
|
|
325
|
+
});
|
|
326
|
+
|
|
301
327
|
it("keeps the generic confirmation for several rows of a `from` dialog", async () => {
|
|
302
328
|
api.mockResolvedValue(2);
|
|
303
329
|
const { deleteRows } = await rowActions();
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A TopListCard's head carries link buttons ("Catalogue →") after its period
|
|
3
|
+
* badge, quiet like an ActivityFeed's; a card without them draws its head as
|
|
4
|
+
* before.
|
|
5
|
+
*/
|
|
6
|
+
import { createSSRApp, defineComponent, h, ref } from "vue";
|
|
7
|
+
import { renderToString } from "vue/server-renderer";
|
|
8
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
9
|
+
import TopListCard from "../layers/dms-ui/app/components/top-list/TopListCard.vue";
|
|
10
|
+
|
|
11
|
+
vi.mock("../layers/dms-ui/app/composables/chart/useChartFetch", () => ({
|
|
12
|
+
useChartFetch: () => ({
|
|
13
|
+
data: ref({ items: [{ id: "pro", title: "Pro", value: 12 }] }),
|
|
14
|
+
isLoading: ref(false),
|
|
15
|
+
}),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
const Slot = defineComponent({
|
|
19
|
+
setup:
|
|
20
|
+
(_, { slots }) =>
|
|
21
|
+
() =>
|
|
22
|
+
h("div", slots.default?.()),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const Button = defineComponent({
|
|
26
|
+
props: { label: String, to: String, variant: String, color: String },
|
|
27
|
+
setup: (props) => () =>
|
|
28
|
+
h(
|
|
29
|
+
"a",
|
|
30
|
+
{
|
|
31
|
+
"data-button": "",
|
|
32
|
+
href: props.to,
|
|
33
|
+
"data-variant": props.variant,
|
|
34
|
+
"data-color": props.color,
|
|
35
|
+
},
|
|
36
|
+
props.label,
|
|
37
|
+
),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
async function render(props: Record<string, unknown>): Promise<string> {
|
|
41
|
+
const app = createSSRApp({
|
|
42
|
+
render: () =>
|
|
43
|
+
h(TopListCard, {
|
|
44
|
+
title: "Top plans",
|
|
45
|
+
componentId: "plans",
|
|
46
|
+
pageId: "dashboard",
|
|
47
|
+
...props,
|
|
48
|
+
}),
|
|
49
|
+
});
|
|
50
|
+
for (const name of ["DmsCard", "UBadge", "UIcon", "USkeleton"]) {
|
|
51
|
+
app.component(name, Slot);
|
|
52
|
+
}
|
|
53
|
+
app.component("UButton", Button);
|
|
54
|
+
app.component("DmsTopListRow", Slot);
|
|
55
|
+
return renderToString(app);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
vi.stubGlobal("useI18n", () => ({ locale: ref("en-US"), t: String }));
|
|
60
|
+
vi.stubGlobal("useTranslation", () => ({
|
|
61
|
+
processI18n: (text: string) => (text.startsWith("$") ? `t(${text})` : text),
|
|
62
|
+
}));
|
|
63
|
+
vi.stubGlobal("useComponentEvent", () => ({}));
|
|
64
|
+
vi.stubGlobal("useWatch", () => ({ state: ref({}) }));
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
afterEach(() => vi.unstubAllGlobals());
|
|
68
|
+
|
|
69
|
+
describe("TopListCard actions", () => {
|
|
70
|
+
it("draws its link buttons in the head, quiet and translated", async () => {
|
|
71
|
+
const html = await render({
|
|
72
|
+
actions: [
|
|
73
|
+
{
|
|
74
|
+
label: "$dashboard.catalogue",
|
|
75
|
+
to: "/plans",
|
|
76
|
+
icon: "i-ph-arrow-right",
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
});
|
|
80
|
+
const button = /<a data-button[^>]*href="\/plans"[^>]*>([^<]*)<\/a>/.exec(
|
|
81
|
+
html,
|
|
82
|
+
);
|
|
83
|
+
expect(button?.[1]).toBe("t($dashboard.catalogue)");
|
|
84
|
+
expect(button?.[0]).toContain('data-variant="ghost"');
|
|
85
|
+
expect(button?.[0]).toContain('data-color="neutral"');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("draws no button without actions", async () => {
|
|
89
|
+
expect(await render({})).not.toContain("data-button");
|
|
90
|
+
});
|
|
91
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/dms",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.6",
|
|
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.
|
|
40
|
+
"@antelopejs/interface-dms": ">=0.5.3 <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",
|
package/skills/dms/SKILL.md
CHANGED
|
@@ -48,9 +48,19 @@ For the framework underneath (the core, `ajs` CLI, interfaces as versioned contr
|
|
|
48
48
|
banners (`RegisterLayoutBanner({ resolve })` → `{ text, actions }`) take it; never format text server-side.
|
|
49
49
|
A table cell's secondary line is a row field holding one (or `{ text, tone }`), named by
|
|
50
50
|
`StatusPillDisplay({ subField })`, `IdentityDisplay({ subtitleField })` or
|
|
51
|
-
`TwoLineDisplay({ subField })`; `IdentityDisplay({ emptyLabel, emptyIcon })` draws an empty row
|
|
51
|
+
`TwoLineDisplay({ subField })`; `IdentityDisplay({ emptyLabel, emptyIcon })` draws an empty row,
|
|
52
|
+
and `IdentityDisplay({ iconField })` reads each row's tile icon from a row field.
|
|
52
53
|
`StatusPillDisplay({ toneField })` reads the pill's tone from a row field (an HTTP status class),
|
|
53
54
|
and a `PillsDisplay` item may be `{ label, tone?, variant: "soft" | "outline" }`, styled per row.
|
|
55
|
+
A `StatGroup` detail and a `NavCardGrid` card title take up to two lines before their ellipsis.
|
|
56
|
+
A unit after a number field is `NumberType({ suffix: "$…days" })`, a character counter on a
|
|
57
|
+
text field `StringType({ maxLength, showCount: true })` — no custom `inputComponent` for either.
|
|
58
|
+
A `TableView` over a shared controller hides a column on one page with
|
|
59
|
+
`columns: { hidden: ["workspace"] }` (as `isVisible: false`, for that table view only) — no view
|
|
60
|
+
just to hide it.
|
|
61
|
+
A long sectioned `Form` groups its side navigation with `FormSection.navGroup` ("Billing", "Legal").
|
|
62
|
+
`TopListCard({ actions })` puts link buttons in its head ("Catalogue →"), and `Tab({ fit: true })`
|
|
63
|
+
keeps a pill tab list to its tabs' width.
|
|
54
64
|
A `KeyValueList` row takes `copy: true` (a copy button for its shown value) or `copyValue`
|
|
55
65
|
(the text to copy instead, e.g. a full URL behind a short label).
|
|
56
66
|
A table ordered by hand (`reorder: { field }`) moves its rows on the grid and its cards on
|