@nexdom/uimed-vue 2.0.0-beta.20 → 2.0.0-beta.22
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/AGENTS.md +14 -4
- package/dist/components.d.ts +23 -5
- package/dist/components.js +20 -5
- package/dist/index.js +1 -4
- package/dist/plugins.d.ts +15 -4
- package/dist/plugins.js +54 -3
- package/dist/styles/settings.scss +6 -0
- package/package.json +2 -3
package/AGENTS.md
CHANGED
|
@@ -22,11 +22,11 @@ pnpm add @nexdom/uimed-vue
|
|
|
22
22
|
yarn add @nexdom/uimed-vue
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Peer dependencies (`@
|
|
25
|
+
Peer dependencies (`@mdi/font`, `@nexdom/shared`, `resize-observer-polyfill`, `sass-embedded`, `vite-plus`, `vue`, `vue-component-type-helpers`, `vue-router`, `vuetify`) must be installed alongside it — most package managers install these automatically, but confirm versions match what's declared in `package.json`'s `peerDependencies`.
|
|
26
26
|
|
|
27
27
|
### Setup
|
|
28
28
|
|
|
29
|
-
Add the Vite plugin:
|
|
29
|
+
Add the Vite plugin, which compiles the components' styles with the library's font and Sass settings (without it, components lose the Unimed Slab font):
|
|
30
30
|
|
|
31
31
|
```ts
|
|
32
32
|
// vite.config.ts
|
|
@@ -112,12 +112,12 @@ On Windows, prefer VSCode's "Dev Containers: Clone Repository in Container Volum
|
|
|
112
112
|
- `vpr check` — lint, formatter, and type-check (builds the library first, since the type-check reads `dist`)
|
|
113
113
|
- `vp test --coverage` — unit tests with coverage (Vitest, jsdom, 100% coverage threshold enforced)
|
|
114
114
|
- `vpr test:mutations` — mutation tests (Stryker; thresholds: high 100, low 100, break 100). It starts one test runner per CPU core; on machines with limited memory, run `vpx stryker run --concurrency 4` instead
|
|
115
|
-
- `vpr test:e2e` — E2E tests (Playwright, runs against the built docs preview site). Outside CI it reuses any server already on port 4173, which is also `vpr docs:dev`'s port: stop the dev server first, or the tests run against it instead of the built site
|
|
115
|
+
- `vpr test:e2e` — E2E tests (Playwright, runs against the built docs preview site). Outside CI it reuses any server already on port 4173, which is also `vpr docs:dev`'s port: stop the dev server first, or the tests run against it instead of the built site. A running `vitepress preview` keeps serving the build it started with, so restart it after rebuilding the docs
|
|
116
116
|
- `vpr depcruise` — architecture/dependency rules (dependency-cruiser)
|
|
117
117
|
- `vp pack` / `vpr build` — build the library
|
|
118
118
|
- `vpr docs` / `vpr docs:dev` — run docs site (imports the lib from `dist`, not `src` — run `vpr dev` in a second terminal to keep `dist` updated while iterating)
|
|
119
119
|
|
|
120
|
-
CI (`.github/workflows/ci.yml`) runs, in order: commitlint on PR commits, `vp pack`, `vpr check`, `vpr depcruise`, `vp test --coverage`, `vpr test:mutations`, `vpr test:e2e
|
|
120
|
+
CI (`.github/workflows/ci.yml`) runs, in order: commitlint on PR commits, `vp pack`, `vpr check`, `vpr depcruise`, `vp test --coverage`, `vpr test:mutations`, `vpr test:e2e`, and then the SonarQube analysis (`vpr sonar`), whose quality gate fails the PR on any issue or unreviewed security hotspot in `src/` (see [Static analysis](#static-analysis-sonarqube)). Match this locally before opening a PR.
|
|
121
121
|
|
|
122
122
|
### Focused runs while iterating
|
|
123
123
|
|
|
@@ -139,6 +139,12 @@ vpx stryker run --mutate "src/components/button/button.vue,src/composables/butto
|
|
|
139
139
|
vpr test:e2e e2e/components/button.spec.ts
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
+
### Static analysis (SonarQube)
|
|
143
|
+
|
|
144
|
+
The company's SonarQube runs only in CI, after the E2E tests. Write code that avoids the rules that have already failed PRs here: use `globalThis` instead of `window`; move inner functions that don't use anything from the outer function out of it (S7721); don't use deprecated APIs (e.g. `event.returnValue`); don't write template comments that look like code; don't add non-null assertions or casts that don't change the type (S4325); no regular expressions with super-linear backtracking, such as two overlapping quantifiers (S5852); and prefer native elements to `role` attributes (S6819).
|
|
145
|
+
|
|
146
|
+
Only if CI's SonarQube step fails and its dashboard doesn't show why, reproduce the analysis with a local SonarQube Community in the server's version (`sonarqube:26.3.0.120487-community`) and `vpx @sonar/scan` pointed at it. The goal is 0 issues and no new security hotspots in the files the PR changed.
|
|
147
|
+
|
|
142
148
|
## Adding a new component
|
|
143
149
|
|
|
144
150
|
Use an existing component (e.g. `button`) as the reference, and deliver all of the following in the same PR:
|
|
@@ -166,6 +172,7 @@ Use an existing composable (e.g. `use-toast`) as the reference, and deliver in t
|
|
|
166
172
|
- `docs` must not mention Vuetify
|
|
167
173
|
- A new component/composable must be listed in its guide page (`docs/guide/…`), its API page (`docs/api/…`), both sidebars in `docs/.vitepress/config.ts`, the guide index table (`docs/guide/index.md`) and the API index list (`docs/api/index.md`).
|
|
168
174
|
- Never write CSS or `<style>` blocks, and expose styling to consumers only through props.
|
|
175
|
+
- Adjustments that Vuetify exposes as Sass variables (e.g. a font family or a line height) go in `src/styles/settings.scss`, which `vitePluginUimed()` compiles into Vuetify's stylesheets in the apps. That file only configures variables: still no CSS rules.
|
|
169
176
|
- Don't add spacing to Vuetify components: no padding or margin utility classes (`pa-*`, `pt-6`, `px-4`, `ma-2`, ...) and no spacing props overriding the defaults. Vuetify already applies Material Design 3 spacing, and overriding it breaks what it handles on its own, such as button paddings and the scrollbar of scrollable content. If the default spacing looks wrong, stop and ask instead of adjusting it.
|
|
170
177
|
- Prefer a Vuetify component's own props to building its parts by hand (e.g. `title`/`text` on `v-card` instead of `v-card-item` > `v-card-title` > `v-card-text`). A non-spacing utility class is acceptable only when no prop covers the need (e.g. `text-wrap` on a card title), with a comment saying why.
|
|
171
178
|
- Keep components thin: a component wires props, slots and events to Vuetify. Logic beyond that (focus management, queues, DOM lookups, comparisons) goes into an internal composable with its own unit tests.
|
|
@@ -204,6 +211,9 @@ Use an existing composable (e.g. `use-toast`) as the reference, and deliver in t
|
|
|
204
211
|
- Coverage threshold is 100%; mutation testing threshold is 100% (break at 100). Don't add code paths without covering tests. Mutants that don't compile are reported as compile errors and don't count toward the score.
|
|
205
212
|
- E2E tests (Playwright, `e2e/`) run against the built docs preview (`http://localhost:4173/uimed-vue/`). Snapshots/screenshots live under `__snapshots__`/`__screenshot__` next to each spec.
|
|
206
213
|
- Before keeping a new screenshot as a baseline, open it and check it against how plain Vuetify renders the same component: overflowing content shows its scrollbar, buttons and cards keep their native paddings, nothing touches the edges or gets clipped. A screenshot that only "looks fine" isn't enough: name what you checked in your report.
|
|
214
|
+
- Generate and update screenshots only on Linux, as CI does: in the Dev Container, or in the Playwright Docker image of the version in `package.json` (`mcr.microsoft.com/playwright:v<version>-noble`). Screenshots taken on Windows or macOS render fonts differently and fail in CI.
|
|
215
|
+
- Update baselines with `--update-snapshots=changed`, scoped to the spec you changed. `--update-snapshots=all` also rewrites accessible snapshots (`.aria.yml`) that didn't need to change.
|
|
216
|
+
- Keep temporary files (scripts, specs, configs) outside the repo, e.g. in the system's temp folder. Vitest collects test files from every folder except the excluded ones in `vite.config.ts`, so a temporary spec in a gitignored folder such as `reports/` still runs, and fails, with the unit tests.
|
|
207
217
|
- Never run Stryker at the same time as another test command: they share build output and Stryker fails with `ENOENT`.
|
|
208
218
|
- Unit tests that mount several apps at once (e.g. nested dialogs) need a distinct `global.config.idPrefix` per mount, since `useId()` restarts in each app.
|
|
209
219
|
- In E2E, a paused `page.clock` also freezes transitions, so overlays never finish leaving. Use `page.clock.pauseAt` to hold time-based work (e.g. a demo action with `setTimeout`), `page.clock.fastForward` to complete it, then `page.clock.resume()` before expecting the overlay to be hidden (see `e2e/composables/use-confirm.spec.ts`).
|
package/dist/components.d.ts
CHANGED
|
@@ -651,6 +651,22 @@ type __VLS_WithSlots$8<T, S> = T & {
|
|
|
651
651
|
* Props exposed by the {@link Row} component.
|
|
652
652
|
*/
|
|
653
653
|
type RowProps = {
|
|
654
|
+
/**
|
|
655
|
+
* Horizontal alignment of the row's columns on each line.
|
|
656
|
+
* Available options are:
|
|
657
|
+
* - `"start"`: aligns the columns to the start of the line;
|
|
658
|
+
* - `"center"`: centers the columns horizontally;
|
|
659
|
+
* - `"end"`: aligns the columns to the end of the line.
|
|
660
|
+
*
|
|
661
|
+
* `"start"` and `"end"` follow the text direction. It only has a visible effect when the
|
|
662
|
+
* columns of a line don't fill it, e.g. with `cols="auto"` or sizes adding up to less than 12.
|
|
663
|
+
* When the columns wrap into several lines, each line is aligned on its own.
|
|
664
|
+
*
|
|
665
|
+
* Columns sit side by side only from the `sm` breakpoint up. Below it, each column takes a
|
|
666
|
+
* whole line, so the alignment has no visible effect.
|
|
667
|
+
* @default "start"
|
|
668
|
+
*/
|
|
669
|
+
alignX?: RowAlignX;
|
|
654
670
|
/**
|
|
655
671
|
* Vertical alignment of the row's columns, relative to the tallest column on the same line.
|
|
656
672
|
* Available options are:
|
|
@@ -666,7 +682,7 @@ type RowProps = {
|
|
|
666
682
|
* whole line, so the alignment has no visible effect.
|
|
667
683
|
* @default "start"
|
|
668
684
|
*/
|
|
669
|
-
|
|
685
|
+
alignY?: RowAlignY;
|
|
670
686
|
/**
|
|
671
687
|
* Renders the row as a list (`<ul>`), without the list's default indentation
|
|
672
688
|
* and bullets, so assistive technologies announce its columns as the items
|
|
@@ -680,7 +696,8 @@ type RowProps = {
|
|
|
680
696
|
*/
|
|
681
697
|
dataTestid?: string;
|
|
682
698
|
};
|
|
683
|
-
type
|
|
699
|
+
type RowAlignX = "start" | "center" | "end";
|
|
700
|
+
type RowAlignY = "start" | "center" | "end" | "stretch";
|
|
684
701
|
//#endregion
|
|
685
702
|
//#region src/components/grid/row/row.vue.d.ts
|
|
686
703
|
/**
|
|
@@ -690,7 +707,7 @@ type RowAlign = "start" | "center" | "end" | "stretch";
|
|
|
690
707
|
* ```vue
|
|
691
708
|
* <template>
|
|
692
709
|
* <u-container>
|
|
693
|
-
* <u-row align="center">
|
|
710
|
+
* <u-row align-x="center" align-y="center">
|
|
694
711
|
* <!-- uimed-column -->
|
|
695
712
|
* </u-row>
|
|
696
713
|
* </u-container>
|
|
@@ -701,7 +718,8 @@ type RowAlign = "start" | "center" | "end" | "stretch";
|
|
|
701
718
|
*/
|
|
702
719
|
declare const _default$12: typeof __VLS_export$9;
|
|
703
720
|
declare const __VLS_export$9: __VLS_WithSlots$7<import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<RowProps, {}, {}, {}, {}, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {}, string, import("vue", { with: { "resolution-mode": "import" } }).PublicProps, Readonly<RowProps> & Readonly<{}>, {
|
|
704
|
-
|
|
721
|
+
alignX: RowAlignX;
|
|
722
|
+
alignY: RowAlignY;
|
|
705
723
|
}, {}, {}, {}, string, import("vue", { with: { "resolution-mode": "import" } }).ComponentProvideOptions, false, {}, any>, {
|
|
706
724
|
default?: (props: {}) => any;
|
|
707
725
|
}>;
|
|
@@ -888,7 +906,7 @@ type SectionProps = {
|
|
|
888
906
|
/**
|
|
889
907
|
* Makes the section occupy 100% of its parent's height.
|
|
890
908
|
*
|
|
891
|
-
* Inside a `URow`, set its `
|
|
909
|
+
* Inside a `URow`, set its `alignY` to `"stretch"` so the section takes the height of the
|
|
892
910
|
* tallest column on the same line.
|
|
893
911
|
* @default false
|
|
894
912
|
*/
|
package/dist/components.js
CHANGED
|
@@ -1470,7 +1470,12 @@ var row_default = /* @__PURE__ */ defineComponent({
|
|
|
1470
1470
|
inheritAttrs: false,
|
|
1471
1471
|
__name: "row",
|
|
1472
1472
|
props: {
|
|
1473
|
-
|
|
1473
|
+
alignX: {
|
|
1474
|
+
type: String,
|
|
1475
|
+
required: false,
|
|
1476
|
+
default: "start"
|
|
1477
|
+
},
|
|
1478
|
+
alignY: {
|
|
1474
1479
|
type: String,
|
|
1475
1480
|
required: false,
|
|
1476
1481
|
default: "start"
|
|
@@ -1486,19 +1491,29 @@ var row_default = /* @__PURE__ */ defineComponent({
|
|
|
1486
1491
|
},
|
|
1487
1492
|
setup(__props) {
|
|
1488
1493
|
const props = __props;
|
|
1489
|
-
const
|
|
1494
|
+
const rowAlignXToClass = {
|
|
1495
|
+
start: "justify-start",
|
|
1496
|
+
center: "justify-center",
|
|
1497
|
+
end: "justify-end"
|
|
1498
|
+
};
|
|
1499
|
+
const rowAlignYToClass = {
|
|
1490
1500
|
start: "align-start",
|
|
1491
1501
|
center: "align-center",
|
|
1492
1502
|
end: "align-end",
|
|
1493
1503
|
stretch: "align-stretch"
|
|
1494
1504
|
};
|
|
1495
|
-
const
|
|
1505
|
+
const alignXClass = computed(() => rowAlignXToClass[props.alignX]);
|
|
1506
|
+
const alignYClass = computed(() => rowAlignYToClass[props.alignY]);
|
|
1496
1507
|
const tag = computed(() => props.list ? "ul" : void 0);
|
|
1497
1508
|
const listClasses = computed(() => props.list ? "pa-0 ma-0" : void 0);
|
|
1498
1509
|
return (_ctx, _cache) => {
|
|
1499
1510
|
return openBlock(), createBlock(unref(VRow), {
|
|
1500
1511
|
tag: tag.value,
|
|
1501
|
-
class: normalizeClass([
|
|
1512
|
+
class: normalizeClass([
|
|
1513
|
+
alignXClass.value,
|
|
1514
|
+
alignYClass.value,
|
|
1515
|
+
listClasses.value
|
|
1516
|
+
]),
|
|
1502
1517
|
"data-testid": props.dataTestid
|
|
1503
1518
|
}, {
|
|
1504
1519
|
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
|
|
@@ -2077,7 +2092,7 @@ var data_set_default = /* @__PURE__ */ defineComponent({
|
|
|
2077
2092
|
}, null, 8, ["modelValue"])) : createCommentVNode("v-if", true)]),
|
|
2078
2093
|
default: withCtx(({ items: pageItems }) => [createCommentVNode(" Stretched, so the items of the same line (e.g. full-height cards) share its height "), createVNode(row_default, {
|
|
2079
2094
|
list: "",
|
|
2080
|
-
align: "stretch"
|
|
2095
|
+
"align-y": "stretch"
|
|
2081
2096
|
}, {
|
|
2082
2097
|
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(pageItems, (pageItem, index) => {
|
|
2083
2098
|
return openBlock(), createBlock(column_default, {
|
package/dist/index.js
CHANGED
package/dist/plugins.d.ts
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { UserConfig } from "vite-plus";
|
|
1
|
+
import { Plugin, UserConfig } from "vite-plus";
|
|
3
2
|
//#region src/plugins.d.ts
|
|
4
3
|
/**
|
|
5
|
-
* Vite plugin
|
|
4
|
+
* Vite plugin that compiles the components' styles with the library's font and Sass settings.
|
|
5
|
+
* Required: without it, the components lose the Unimed Slab font. Needs `sass-embedded`, a peer
|
|
6
|
+
* dependency.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* // vite.config.ts
|
|
11
|
+
* import { vitePluginUimed } from "@nexdom/uimed-vue/plugins";
|
|
12
|
+
*
|
|
13
|
+
* export default defineConfig({
|
|
14
|
+
* plugins: [vue(), vitePluginUimed()],
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
6
17
|
*/
|
|
7
|
-
export declare function vitePluginUimed():
|
|
18
|
+
export declare function vitePluginUimed(): Plugin[];
|
|
8
19
|
/**
|
|
9
20
|
* Vitest Server plugin for UIMed.
|
|
10
21
|
*/
|
package/dist/plugins.js
CHANGED
|
@@ -1,10 +1,61 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
2
3
|
//#region src/plugins.ts
|
|
4
|
+
/** Matches the ids of CSS files, with or without a query */
|
|
5
|
+
const stylesheet = /\.css(?:\?|$)/;
|
|
6
|
+
/** Path of Vuetify's stylesheets folder, inside the `node_modules` folder where it's installed */
|
|
7
|
+
const vuetifyStylesheets = "/node_modules/vuetify/lib/";
|
|
3
8
|
/**
|
|
4
|
-
*
|
|
9
|
+
* Matches the queries that import a stylesheet's file as is (`?raw`) or its URL (`?url`), which
|
|
10
|
+
* Vite doesn't compile. The URL still serves the compiled stylesheet, which Vite loads separately
|
|
11
|
+
*/
|
|
12
|
+
const uncompiledStylesheetQuery = /[?&](?:raw|url)\b/;
|
|
13
|
+
/**
|
|
14
|
+
* Vite plugin that compiles the components' styles with the library's font and Sass settings.
|
|
15
|
+
* Required: without it, the components lose the Unimed Slab font. Needs `sass-embedded`, a peer
|
|
16
|
+
* dependency.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* // vite.config.ts
|
|
21
|
+
* import { vitePluginUimed } from "@nexdom/uimed-vue/plugins";
|
|
22
|
+
*
|
|
23
|
+
* export default defineConfig({
|
|
24
|
+
* plugins: [vue(), vitePluginUimed()],
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
5
27
|
*/
|
|
6
28
|
function vitePluginUimed() {
|
|
7
|
-
|
|
29
|
+
const settings = new URL("./styles/settings.scss", import.meta.url);
|
|
30
|
+
let compiler;
|
|
31
|
+
return [{
|
|
32
|
+
name: "uimed:vuetify-styles",
|
|
33
|
+
enforce: "pre",
|
|
34
|
+
load: {
|
|
35
|
+
filter: { id: {
|
|
36
|
+
include: stylesheet,
|
|
37
|
+
exclude: uncompiledStylesheetQuery
|
|
38
|
+
} },
|
|
39
|
+
async handler(id) {
|
|
40
|
+
const file = id.split("?")[0].replaceAll("\\", "/");
|
|
41
|
+
const folder = file.lastIndexOf(vuetifyStylesheets);
|
|
42
|
+
if (folder === -1) return null;
|
|
43
|
+
const nodeModules = `${file.slice(0, folder)}/node_modules`;
|
|
44
|
+
const source = [".sass", ".scss"].map((extension) => `${file.slice(0, -4)}${extension}`).find((path) => existsSync(path));
|
|
45
|
+
if (!source) return null;
|
|
46
|
+
compiler ??= import("sass-embedded").then((sass) => sass.initAsyncCompiler());
|
|
47
|
+
const sass = await compiler;
|
|
48
|
+
const entry = `@use "${settings.href}";\n@use "${pathToFileURL(source).href}";`;
|
|
49
|
+
const { css } = await sass.compileStringAsync(entry, { loadPaths: [nodeModules] });
|
|
50
|
+
return css;
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
async closeBundle() {
|
|
54
|
+
const current = compiler;
|
|
55
|
+
compiler = void 0;
|
|
56
|
+
await (await current)?.dispose();
|
|
57
|
+
}
|
|
58
|
+
}];
|
|
8
59
|
}
|
|
9
60
|
/**
|
|
10
61
|
* Vitest Server plugin for UIMed.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
@use "vuetify/settings" with (
|
|
2
|
+
$body-font-family: var(--v-font-body, "Unimed Slab", sans-serif),
|
|
3
|
+
// Unimed Slab's ascent and descent take 1.26em, more than the subtitles' default 1rem line
|
|
4
|
+
// height, which clipped its descenders and accented capitals
|
|
5
|
+
$list-item-subtitle-line-height: normal
|
|
6
|
+
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexdom/uimed-vue",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.22",
|
|
4
4
|
"description": "A ui framework for NEXDOM applications.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lib",
|
|
@@ -75,7 +75,6 @@
|
|
|
75
75
|
"sass-embedded": "^1.103.1",
|
|
76
76
|
"stryker-vue-ignorer": "github:davidlandais/stryker-vue-ignorer",
|
|
77
77
|
"typescript": "^6.0.3",
|
|
78
|
-
"vite-plugin-vuetify": "^2.1.3",
|
|
79
78
|
"vite-plus": "^1.0.0",
|
|
80
79
|
"vitepress": "2.0.0-alpha.19",
|
|
81
80
|
"vitepress-plugin-group-icons": "^1.7.6",
|
|
@@ -90,7 +89,7 @@
|
|
|
90
89
|
"@mdi/font": "^7.4.47",
|
|
91
90
|
"@nexdom/shared": "^2.1.1",
|
|
92
91
|
"resize-observer-polyfill": "^1.5.1",
|
|
93
|
-
"
|
|
92
|
+
"sass-embedded": "^1.103.1",
|
|
94
93
|
"vite-plus": "^1.0.0",
|
|
95
94
|
"vue": "^3.5.40",
|
|
96
95
|
"vue-component-type-helpers": "^3.3.11",
|