@nexdom/uimed-vue 2.0.0-beta.63 → 2.0.0-beta.64
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/dist/components/inputs/text-field/text-field.vue.d.ts +1 -1
- package/package.json +2 -3
- package/skills/uimed-vue/SKILL.md +113 -0
- package/skills/uimed-vue/references/api/components/autocomplete-field.md +45 -0
- package/skills/uimed-vue/references/api/components/button.md +48 -0
- package/skills/uimed-vue/references/api/components/checkbox.md +36 -0
- package/skills/uimed-vue/references/api/components/data-sets/data-set-item.md +61 -0
- package/skills/uimed-vue/references/api/components/data-sets/data-set.md +67 -0
- package/skills/uimed-vue/references/api/components/date-time-field.md +44 -0
- package/skills/uimed-vue/references/api/components/details.md +33 -0
- package/skills/uimed-vue/references/api/components/dialog.md +70 -0
- package/skills/uimed-vue/references/api/components/form.md +43 -0
- package/skills/uimed-vue/references/api/components/grid/column.md +41 -0
- package/skills/uimed-vue/references/api/components/grid/container.md +32 -0
- package/skills/uimed-vue/references/api/components/grid/row.md +37 -0
- package/skills/uimed-vue/references/api/components/icon-button.md +37 -0
- package/skills/uimed-vue/references/api/components/img.md +34 -0
- package/skills/uimed-vue/references/api/components/link.md +31 -0
- package/skills/uimed-vue/references/api/components/main.md +174 -0
- package/skills/uimed-vue/references/api/components/mask-field.md +60 -0
- package/skills/uimed-vue/references/api/components/page-transition.md +37 -0
- package/skills/uimed-vue/references/api/components/sections/section-content.md +23 -0
- package/skills/uimed-vue/references/api/components/sections/section.md +61 -0
- package/skills/uimed-vue/references/api/components/table.md +35 -0
- package/skills/uimed-vue/references/api/components/text-field.md +42 -0
- package/skills/uimed-vue/references/api/composables/use-confirm.md +96 -0
- package/skills/uimed-vue/references/api/composables/use-dialog.md +95 -0
- package/skills/uimed-vue/references/api/composables/use-run-or-toast.md +58 -0
- package/skills/uimed-vue/references/api/composables/use-toast.md +48 -0
- package/skills/uimed-vue/references/api/composables/use-unsaved-changes.md +106 -0
- package/skills/uimed-vue/references/api/index.md +1 -0
- package/skills/uimed-vue/references/guide/components/autocomplete-field.md +256 -0
- package/skills/uimed-vue/references/guide/components/button.md +155 -0
- package/skills/uimed-vue/references/guide/components/checkbox.md +113 -0
- package/skills/uimed-vue/references/guide/components/data-set.md +370 -0
- package/skills/uimed-vue/references/guide/components/date-time-field.md +202 -0
- package/skills/uimed-vue/references/guide/components/details.md +76 -0
- package/skills/uimed-vue/references/guide/components/dialog.md +269 -0
- package/skills/uimed-vue/references/guide/components/form.md +96 -0
- package/skills/uimed-vue/references/guide/components/icon-button.md +76 -0
- package/skills/uimed-vue/references/guide/components/img.md +128 -0
- package/skills/uimed-vue/references/guide/components/layout.md +323 -0
- package/skills/uimed-vue/references/guide/components/link.md +47 -0
- package/skills/uimed-vue/references/guide/components/main.md +435 -0
- package/skills/uimed-vue/references/guide/components/mask-field.md +256 -0
- package/skills/uimed-vue/references/guide/components/page-transition.md +111 -0
- package/skills/uimed-vue/references/guide/components/section.md +232 -0
- package/skills/uimed-vue/references/guide/components/table.md +102 -0
- package/skills/uimed-vue/references/guide/components/text-field.md +172 -0
- package/skills/uimed-vue/references/guide/composables/use-confirm.md +123 -0
- package/skills/uimed-vue/references/guide/composables/use-dialog.md +102 -0
- package/skills/uimed-vue/references/guide/composables/use-run-or-toast.md +55 -0
- package/skills/uimed-vue/references/guide/composables/use-toast.md +57 -0
- package/skills/uimed-vue/references/guide/composables/use-unsaved-changes.md +129 -0
- package/skills/uimed-vue/references/guide/getting-started.md +135 -0
- package/skills/uimed-vue/references/guide/icons.md +71 -0
- package/skills/uimed-vue/references/guide/index.md +51 -0
- package/skills/uimed-vue/references/guide/migration-v1-to-v2.md +191 -0
- package/skills/uimed-vue/references/guide/unit-testing.md +85 -0
- package/AGENTS.md +0 -274
- package/CLAUDE.md +0 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# O que é UIMed-Vue?
|
|
2
|
+
|
|
3
|
+
É um framework de UI, para [Vuejs](https://vuejs.org/), com diversos componentes e funções reutilizáveis entre eles (_components_ e _composables_).
|
|
4
|
+
|
|
5
|
+
> [!Danger]
|
|
6
|
+
> Construído para atender um design system específico e, portanto, eliminando a necessidade de qualquer esforço para sua estilização.
|
|
7
|
+
>
|
|
8
|
+
> Portanto, evite o uso de CSS ou estilizações paralelas enquanto estiver trabalhando com este framework.
|
|
9
|
+
|
|
10
|
+
## Casos de uso
|
|
11
|
+
|
|
12
|
+
### Componentes
|
|
13
|
+
|
|
14
|
+
São recursos que permitem a elaboração e reutilização de interface gráfica.
|
|
15
|
+
|
|
16
|
+
Os componentes a seguir possuem exemplos práticos de utilização dentro desta documentação.
|
|
17
|
+
|
|
18
|
+
| Recurso | Descrição |
|
|
19
|
+
| ---------------------------------------------------------------------- | ------------------------------------------------------ |
|
|
20
|
+
| [Botões](./components/button.md) | Componente de botão. |
|
|
21
|
+
| [Botões de ícone](./components/icon-button.md) | Componente de botão que exibe apenas um ícone. |
|
|
22
|
+
| [Campos com máscara](./components/mask-field.md) | Componente para campo de texto com máscara. |
|
|
23
|
+
| [Campos com preenchimento automático](./components/autocomplete-field.md) | Componente para autocomplete/combobox. |
|
|
24
|
+
| [Caixas de seleção](./components/checkbox.md) | Componente para caixa de seleção. |
|
|
25
|
+
| [Campos de data e hora](./components/date-time-field.md) | Componente para campo de data e/ou hora. |
|
|
26
|
+
| [Campos de texto](./components/text-field.md) | Componente para campo de texto. |
|
|
27
|
+
| [Componente base](./components/main.md) | Componente raiz. |
|
|
28
|
+
| [Componentes de layout](./components/layout.md) | Componentes para seguir o grid system. |
|
|
29
|
+
| [Agrupador de Conteúdo](./components/section.md) | Componente para agrupar conteúdo. |
|
|
30
|
+
| [Conteúdo Expansível](./components/details.md) | Componente para expandir e recolher conteúdo. |
|
|
31
|
+
| [Formulários](./components/form.md) | Componente de formulário. |
|
|
32
|
+
| [Imagens](./components/img.md) | Componente para apresentação de imagens. |
|
|
33
|
+
| [Janelas modais](./components/dialog.md) | Componente para apresentar conteúdo em janelas modais. |
|
|
34
|
+
| [Links](./components/link.md) | Componente para links de texto. |
|
|
35
|
+
| [Listagens](./components/data-set.md) | Componente para listagem de dados. |
|
|
36
|
+
| [Tabelas](./components/table.md) | Componente para apresentação de dados em tabelas. |
|
|
37
|
+
| [Transição de páginas](./components/page-transition.md) | Componente para animar a troca de páginas. |
|
|
38
|
+
|
|
39
|
+
### Composables
|
|
40
|
+
|
|
41
|
+
São funções que possibilitam a reutilização de recursos entre diversos componentes.
|
|
42
|
+
|
|
43
|
+
As composables a seguir possuem exemplos práticos de utilização dentro desta documentação.
|
|
44
|
+
|
|
45
|
+
| Recurso | Descrição |
|
|
46
|
+
| ---------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
47
|
+
| [Toasts](./composables/use-toast.md) | Composables para apresentar mensagens `toast`. |
|
|
48
|
+
| [RunOrToast](./composables/use-run-or-toast.md) | Composables para executar métodos e apresentar mensagens `toast` em caso de falha. |
|
|
49
|
+
| [Diálogos](./composables/use-dialog.md) | Composables para apresentar diálogos. |
|
|
50
|
+
| [Confirmações](./composables/use-confirm.md) | Composables para executar ações após a confirmação do usuário. |
|
|
51
|
+
| [Alterações não salvas](./composables/use-unsaved-changes.md) | Composables para evitar a perda de alterações não salvas ao sair da página. |
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Migrando da v1 para v2
|
|
2
|
+
|
|
3
|
+
A versão 2 do UIMed-Vue introduz uma mudança significativa: todos os componentes exportados agora possuem um prefixo `U`, padronizando a forma como você importa e utiliza os componentes. Além disso, alguns componentes receberam nomes mais descritivos internamente.
|
|
4
|
+
|
|
5
|
+
## Mapa de Renomeação
|
|
6
|
+
|
|
7
|
+
| Nome anterior | Novo nome |
|
|
8
|
+
| ------------- | ----------------- |
|
|
9
|
+
| `Btn` | `UButton` |
|
|
10
|
+
| `Frm` | `UForm` |
|
|
11
|
+
| `Root` | `UMain` |
|
|
12
|
+
| `ContentSet` | `USection` |
|
|
13
|
+
| `Content` | `USectionContent` |
|
|
14
|
+
| `Container` | `UContainer` |
|
|
15
|
+
| `Row` | `URow` |
|
|
16
|
+
| `Column` | `UColumn` |
|
|
17
|
+
| `TextField` | `UTextField` |
|
|
18
|
+
|
|
19
|
+
## Antes e Depois
|
|
20
|
+
|
|
21
|
+
### Importações
|
|
22
|
+
|
|
23
|
+
**Antes (v1):**
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
import { Root, Btn, Row, Column, TextField } from "@nexdom/uimed-vue/components";
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Depois (v2):**
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
import { UMain, UButton, URow, UColumn, UTextField } from "@nexdom/uimed-vue/components";
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Template
|
|
36
|
+
|
|
37
|
+
**Antes (v1):**
|
|
38
|
+
|
|
39
|
+
```vue
|
|
40
|
+
<template>
|
|
41
|
+
<root>
|
|
42
|
+
<row>
|
|
43
|
+
<column>
|
|
44
|
+
<btn @click="handleClick">Clique aqui</btn>
|
|
45
|
+
</column>
|
|
46
|
+
</row>
|
|
47
|
+
</root>
|
|
48
|
+
</template>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Depois (v2):**
|
|
52
|
+
|
|
53
|
+
```vue
|
|
54
|
+
<template>
|
|
55
|
+
<u-main>
|
|
56
|
+
<u-row>
|
|
57
|
+
<u-column>
|
|
58
|
+
<u-button @click="handleClick">Clique aqui</u-button>
|
|
59
|
+
</u-column>
|
|
60
|
+
</u-row>
|
|
61
|
+
</u-main>
|
|
62
|
+
</template>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Notificações do menu superior
|
|
66
|
+
|
|
67
|
+
A propriedade `date` das notificações do menu superior (`appBar.notifications`) foi substituída por `when`, um texto livre apresentado exatamente como informado. Assim, a aplicação passa a decidir como indicar quando a notificação foi gerada (exemplo: `"10:30"`, `"Ontem"` ou `"Há 5 minutos"`).
|
|
68
|
+
|
|
69
|
+
**Antes (v1):**
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
const appBar = {
|
|
73
|
+
title: "Minha aplicação",
|
|
74
|
+
notifications: [{ title: "Nova versão", date: new Date() }],
|
|
75
|
+
};
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Depois (v2):**
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
const appBar = {
|
|
82
|
+
title: "Minha aplicação",
|
|
83
|
+
notifications: [{ title: "Nova versão", when: "10:30" }],
|
|
84
|
+
};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Para manter a formatação apresentada na v1 (horário para notificações de hoje, "Ontem" para as de ontem e a data completa para as demais), utilize as funções de [`@nexdom/shared`](https://nexdom-healthtech.github.io/shared/):
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { formatDateTime, toPeriodInterval } from "@nexdom/shared/utils";
|
|
91
|
+
|
|
92
|
+
function formatNotificationWhen(date: Date) {
|
|
93
|
+
const interval = toPeriodInterval(date);
|
|
94
|
+
const wasThisMonth = interval.months === 0 && interval.years === 0;
|
|
95
|
+
|
|
96
|
+
if (wasThisMonth && interval.days === 0) return formatDateTime(date, "HH:mm");
|
|
97
|
+
if (wasThisMonth && interval.days === 1) return "Ontem";
|
|
98
|
+
|
|
99
|
+
return formatDateTime(date, "DD/MM/YYYY");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const appBar = {
|
|
103
|
+
title: "Minha aplicação",
|
|
104
|
+
notifications: [{ title: "Nova versão", when: formatNotificationWhen(new Date()) }],
|
|
105
|
+
};
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Alinhamento vertical das colunas
|
|
109
|
+
|
|
110
|
+
Na v1, as colunas de uma `Row` eram esticadas até a altura da coluna mais alta da linha. Na v2, a prop `alignY` da `URow` controla esse alinhamento e o padrão passou a ser `"start"`: cada coluna mantém a altura do próprio conteúdo e fica alinhada ao topo. Para manter o comportamento da v1, como em cartões lado a lado que devem ter a mesma altura, utilize `align-y="stretch"` (veja [Alinhamento vertical das colunas](./components/layout.md#alinhamento-vertical-das-colunas)).
|
|
111
|
+
|
|
112
|
+
**Antes (v1):**
|
|
113
|
+
|
|
114
|
+
```vue
|
|
115
|
+
<template>
|
|
116
|
+
<row>
|
|
117
|
+
<column cols="6">
|
|
118
|
+
<content-set title="Consultas" full-height>
|
|
119
|
+
<!-- conteúdo curto -->
|
|
120
|
+
</content-set>
|
|
121
|
+
</column>
|
|
122
|
+
<column cols="6">
|
|
123
|
+
<content-set title="Exames" full-height>
|
|
124
|
+
<!-- conteúdo mais longo -->
|
|
125
|
+
</content-set>
|
|
126
|
+
</column>
|
|
127
|
+
</row>
|
|
128
|
+
</template>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Depois (v2):**
|
|
132
|
+
|
|
133
|
+
```vue
|
|
134
|
+
<template>
|
|
135
|
+
<u-row align-y="stretch">
|
|
136
|
+
<u-column cols="6">
|
|
137
|
+
<u-section title="Consultas" full-height>
|
|
138
|
+
<!-- conteúdo curto -->
|
|
139
|
+
</u-section>
|
|
140
|
+
</u-column>
|
|
141
|
+
<u-column cols="6">
|
|
142
|
+
<u-section title="Exames" full-height>
|
|
143
|
+
<!-- conteúdo mais longo -->
|
|
144
|
+
</u-section>
|
|
145
|
+
</u-column>
|
|
146
|
+
</u-row>
|
|
147
|
+
</template>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Menu de navegação
|
|
151
|
+
|
|
152
|
+
Na v2, o ícone (`icon`) é obrigatório nos itens e grupos do primeiro nível do menu de navegação (`navigationMenu`). Os itens dentro de um grupo continuam sem ícone. Os nomes disponíveis estão em [Ícones](./icons.md).
|
|
153
|
+
|
|
154
|
+
Em telas a partir de 1145px, o menu deixa de ser aberto pelo botão "Menu de navegação" do menu superior, que não é mais exibido: ele fica sempre visível à esquerda do conteúdo, recolhido em uma faixa de 56px com os ícones, e se expande sobre o conteúdo com o mouse, com o foco do teclado ou pelo atalho `Ctrl + K` (veja [Menu de navegação](./components/main.md#menu-de-navegacao)). O conteúdo da página fica 56px mais estreito, e testes automatizados que clicavam no botão para abrir o menu passam a encontrá-lo apenas em telas menores. Em telas menores, o menu continua sendo aberto pelo botão.
|
|
155
|
+
|
|
156
|
+
**Antes (v1):**
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
const navigationMenu = {
|
|
160
|
+
items: [
|
|
161
|
+
{ description: "Início", route: "/" },
|
|
162
|
+
{ description: "Cadastros", items: [{ description: "Pacientes", route: "/pacientes" }] },
|
|
163
|
+
],
|
|
164
|
+
};
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Depois (v2):**
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
const navigationMenu = {
|
|
171
|
+
items: [
|
|
172
|
+
{ description: "Início", route: "/", icon: "home" },
|
|
173
|
+
{
|
|
174
|
+
description: "Cadastros",
|
|
175
|
+
icon: "folder",
|
|
176
|
+
items: [{ description: "Pacientes", route: "/pacientes" }],
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
};
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Dicas de Migração
|
|
183
|
+
|
|
184
|
+
1. Procure por todas as importações antigas e substitua pelos novos nomes com o prefixo `U`
|
|
185
|
+
2. Atualize todos os templates para usar as novas tags em kebab-case (ex: `<u-button>` em vez de `<btn>`)
|
|
186
|
+
3. Os tipos de props não são mais exportados pela biblioteca. Utilize `ComponentProps`, de [`vue-component-type-helpers`](https://www.npmjs.com/package/vue-component-type-helpers), para obtê-los a partir do componente (veja [Tipagem de props](./getting-started.md#tipagem-de-props))
|
|
187
|
+
4. Substitua a propriedade `date` das notificações do menu superior por `when`, informando o texto já formatado (veja [Notificações do menu superior](#notificacoes-do-menu-superior))
|
|
188
|
+
5. Adicione `align-y="stretch"` às `URow` cujas colunas precisam ter a mesma altura (veja [Alinhamento vertical das colunas](#alinhamento-vertical-das-colunas))
|
|
189
|
+
6. Informe um `icon` em todos os itens e grupos do primeiro nível do menu de navegação, e revise as telas e os testes que contavam com o botão do menu em telas largas (veja [Menu de navegação](#menu-de-navegacao))
|
|
190
|
+
|
|
191
|
+
Para mais informações sobre cada componente, consulte a [Documentação da API](../api/index.md).
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Testes Unitários
|
|
2
|
+
|
|
3
|
+
Testes unitários com o UIMed-Vue são muito simples, pois este framework já vem com plugins que atendem as ferramentas que serão listadas a seguir.
|
|
4
|
+
|
|
5
|
+
## Usando Vitest
|
|
6
|
+
|
|
7
|
+
Para realizar as configurações, atualize seu arquivo `vite.config.ts` da seguinte maneira:
|
|
8
|
+
|
|
9
|
+
```ts [vite.config.ts]
|
|
10
|
+
import { defineConfig } from "vite";
|
|
11
|
+
import { vitestServerPluginUimed } from "@nexdom/uimed-vue/plugins"; // [!code ++]
|
|
12
|
+
|
|
13
|
+
export default defineConfig({
|
|
14
|
+
test: {
|
|
15
|
+
server: vitestServerPluginUimed(), // [!code ++]
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Escrevendo os testes
|
|
21
|
+
|
|
22
|
+
Uma vez que as etapas mencionadas anteriormente foram realizadas, utilize o recurso de testes unitários do uimed-vue para montar os seus componentes `wrapper` através do [`@vue/test-utils`](https://test-utils.vuejs.org/).
|
|
23
|
+
|
|
24
|
+
```ts [hello-world.test.ts]
|
|
25
|
+
import { mount } from "@vue/test-utils";
|
|
26
|
+
import { vueTestUtilsPluginUimed } from "@nexdom/uimed-vue/unit-test"; // [!code ++]
|
|
27
|
+
import HelloWorld from "../hello-world.vue";
|
|
28
|
+
|
|
29
|
+
describe("HelloWorld", () => {
|
|
30
|
+
it("displays message", () => {
|
|
31
|
+
const wrapper = mount(HelloWorld, {
|
|
32
|
+
global: {
|
|
33
|
+
plugins: [vueTestUtilsPluginUimed()], // [!code ++]
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Verifica se o wrapper/componente contém o texto esperado.
|
|
38
|
+
expect(wrapper.text()).toContain("Olá, Mundo!");
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
O plugin de testes usa o mesmo idioma do `createUimed()`, então os testes veem os mesmos textos em português da aplicação, como os anúncios para leitores de tela e os rótulos da paginação.
|
|
44
|
+
|
|
45
|
+
## Testando código que usa diálogos
|
|
46
|
+
|
|
47
|
+
Os diálogos das composables [`useDialog`](./composables/use-dialog.md) e [`useConfirm`](./composables/use-confirm.md) são exibidos pelo [Componente base](./components/main.md). Em um teste que monta apenas o seu componente, sem o `UMain`, nenhum diálogo é exibido e a `Promise` de `dialog` ou `confirm` não é resolvida.
|
|
48
|
+
|
|
49
|
+
Nos testes unitários, substitua as composables por versões simuladas que respondem pelo usuário:
|
|
50
|
+
|
|
51
|
+
```ts [delete-patient.test.ts]
|
|
52
|
+
import { mount } from "@vue/test-utils";
|
|
53
|
+
import { computed } from "vue";
|
|
54
|
+
import { vueTestUtilsPluginUimed } from "@nexdom/uimed-vue/unit-test";
|
|
55
|
+
import DeletePatient from "../delete-patient.vue";
|
|
56
|
+
|
|
57
|
+
vi.mock(import("@nexdom/uimed-vue/composables"), async (importOriginal) => ({
|
|
58
|
+
...(await importOriginal()),
|
|
59
|
+
// Simula o usuário confirmando: executa a ação como o `confirm` faria
|
|
60
|
+
useConfirm: () => ({
|
|
61
|
+
confirm: vi.fn((_options, action, ...params) => action(...params)),
|
|
62
|
+
isRunning: computed(() => false),
|
|
63
|
+
}),
|
|
64
|
+
// Simula o usuário escolhendo a ação de valor "stay"
|
|
65
|
+
useDialog: () => ({ dialog: vi.fn().mockResolvedValue("stay") }),
|
|
66
|
+
}));
|
|
67
|
+
|
|
68
|
+
describe("DeletePatient", () => {
|
|
69
|
+
it("deletes the patient once confirmed", async () => {
|
|
70
|
+
const wrapper = mount(DeletePatient, {
|
|
71
|
+
global: {
|
|
72
|
+
plugins: [vueTestUtilsPluginUimed()],
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
await wrapper.find("button").trigger("click");
|
|
77
|
+
|
|
78
|
+
// Verifique aqui o efeito da ação confirmada.
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Para simular um cancelamento, faça o `confirm` simulado resolver `false` sem executar a ação, e o `dialog` simulado resolver `undefined`.
|
|
84
|
+
|
|
85
|
+
Para verificar o diálogo em si, prefira testes E2E, que exercitam a aplicação completa, com o `UMain` montado.
|
package/AGENTS.md
DELETED
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
# AGENTS.md
|
|
2
|
-
|
|
3
|
-
## Project
|
|
4
|
-
|
|
5
|
-
`@nexdom/uimed-vue` is a Vue 3 UI component library for NEXDOM applications, built on top of [Vuetify](https://vuetifyjs.com/) and [Material Design 3](https://m3.material.io/). It's published to npm and documented via VitePress at https://nexdom-healthtech.github.io/uimed-vue/.
|
|
6
|
-
|
|
7
|
-
Tooling is built around [Vite+](https://github.com/voidzero-dev/vite-plus) (`vp`/`vpr`/`vpx` CLIs), not raw `vite`/`vitest`/`eslint` invocations.
|
|
8
|
-
|
|
9
|
-
## Using this library in an app
|
|
10
|
-
|
|
11
|
-
These steps are for developers _consuming_ `@nexdom/uimed-vue` in a Vue application (not for contributing to this repo — see the rest of this document for that).
|
|
12
|
-
|
|
13
|
-
### Install
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
vp add @nexdom/uimed-vue
|
|
17
|
-
# But, if you're not using Vite+ yet...
|
|
18
|
-
npm i @nexdom/uimed-vue
|
|
19
|
-
# Or
|
|
20
|
-
pnpm add @nexdom/uimed-vue
|
|
21
|
-
# Or
|
|
22
|
-
yarn add @nexdom/uimed-vue
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
Peer dependencies (`@mdi/js`, `@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
|
-
|
|
27
|
-
### Setup
|
|
28
|
-
|
|
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
|
-
|
|
31
|
-
```ts
|
|
32
|
-
// vite.config.ts
|
|
33
|
-
import { vitePluginUimed } from "@nexdom/uimed-vue/plugins";
|
|
34
|
-
|
|
35
|
-
// ...
|
|
36
|
-
|
|
37
|
-
plugins: [vue(), vitePluginUimed()];
|
|
38
|
-
|
|
39
|
-
// ...
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Register the plugin in the Vue app:
|
|
43
|
-
|
|
44
|
-
```ts
|
|
45
|
-
// main.js or main.ts
|
|
46
|
-
import { createApp } from "vue";
|
|
47
|
-
import { createUimed } from "@nexdom/uimed-vue";
|
|
48
|
-
|
|
49
|
-
import App from "./App.vue";
|
|
50
|
-
|
|
51
|
-
const uimed = createUimed();
|
|
52
|
-
|
|
53
|
-
createApp(App).use(uimed).mount("#app");
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### Usage
|
|
57
|
-
|
|
58
|
-
Place the `UMain` component at the top of `App.vue`, then add other components as needed:
|
|
59
|
-
|
|
60
|
-
```vue
|
|
61
|
-
<!-- App.vue -->
|
|
62
|
-
<template>
|
|
63
|
-
<u-main>
|
|
64
|
-
<!-- ... -->
|
|
65
|
-
</u-main>
|
|
66
|
-
</template>
|
|
67
|
-
|
|
68
|
-
<script setup lang="ts">
|
|
69
|
-
import { UMain } from "@nexdom/uimed-vue/components";
|
|
70
|
-
</script>
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Never write CSS, classes or any kind of styling. Always use component props.
|
|
74
|
-
|
|
75
|
-
The library doesn't export prop types. Derive them with `ComponentProps` from `vue-component-type-helpers`:
|
|
76
|
-
|
|
77
|
-
```ts
|
|
78
|
-
import type { ComponentProps } from "vue-component-type-helpers";
|
|
79
|
-
import { UMain, USection } from "@nexdom/uimed-vue/components";
|
|
80
|
-
|
|
81
|
-
type MainProps = ComponentProps<typeof UMain>;
|
|
82
|
-
type AppBar = NonNullable<MainProps["appBar"]>;
|
|
83
|
-
type NavigationMenu = NonNullable<MainProps["navigationMenu"]>;
|
|
84
|
-
type SectionAction = NonNullable<ComponentProps<typeof USection>["actions"]>[number];
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
Available entry points: `@nexdom/uimed-vue` (root), `@nexdom/uimed-vue/components`, `@nexdom/uimed-vue/composables`, `@nexdom/uimed-vue/plugins`, `@nexdom/uimed-vue/unit-test` (test helpers, e.g. `vueTestUtilsPluginUimed()` for mounting components with Vuetify in Vitest).
|
|
88
|
-
|
|
89
|
-
Full component/composable reference lives in the [docs](https://nexdom-healthtech.github.io/uimed-vue/).
|
|
90
|
-
|
|
91
|
-
## Contributing to this repo
|
|
92
|
-
|
|
93
|
-
The sections below are for developers working _on_ this library itself.
|
|
94
|
-
|
|
95
|
-
### Dev environment setup
|
|
96
|
-
|
|
97
|
-
This project expects to be opened inside its devcontainer (VSCode). If commands below fail with missing shims/commands, run:
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
vp env doctor # diagnose missing parts
|
|
101
|
-
vp env setup # create shims like vpr and vpx
|
|
102
|
-
vp install # install dependencies
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
On Windows, prefer VSCode's "Dev Containers: Clone Repository in Container Volume" over opening a folder cloned on the host:
|
|
106
|
-
|
|
107
|
-
- A host clone with `core.autocrlf=true` checks files out with CRLF, and `vpr check` then reports formatting issues on every file. `.gitattributes` enforces LF, but clones made before it was added need `git rm -rq --cached . && git reset --hard` to be re-normalized (commit or stash local changes first, since `reset --hard` discards them).
|
|
108
|
-
- A host folder bind-mounted into the container is roughly 10x slower (unit tests alone go from seconds to minutes), which makes Stryker's initial test run time out.
|
|
109
|
-
|
|
110
|
-
## Commands
|
|
111
|
-
|
|
112
|
-
- `vpr check` — lint, formatter, and type-check (builds the library first, since the type-check reads `dist`)
|
|
113
|
-
- `vp test --coverage` — unit tests with coverage (Vitest, jsdom, 100% coverage threshold enforced)
|
|
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. A running `vitepress preview` keeps serving the build it started with, so restart it after rebuilding the docs
|
|
116
|
-
- `vpr depcruise` — architecture/dependency rules (dependency-cruiser)
|
|
117
|
-
- `vp pack` / `vpr build` — build the library. It publishes one module per source file (`unbundle` in `vite.config.ts`), and the entry points import them instead of copying their code, so the apps' bundlers can split the components by the route that uses them
|
|
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
|
-
|
|
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
|
-
|
|
122
|
-
### Focused runs while iterating
|
|
123
|
-
|
|
124
|
-
The full suite takes minutes; while working on a single component or composable, scope each step to it (`button` below is an example) and run the full suite only before opening a PR:
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
# Unit tests of one or more folders
|
|
128
|
-
vp test src/components/button
|
|
129
|
-
|
|
130
|
-
# Same, with coverage limited to the files you changed. Without `--coverage.include`, files that
|
|
131
|
-
# are only imported (e.g. the children of `main.vue`) count as uncovered and fail the 100% threshold
|
|
132
|
-
vp test src/components/button --coverage --coverage.include="src/components/button/**"
|
|
133
|
-
|
|
134
|
-
# Mutation tests of specific files. List several files in a single comma-separated `--mutate`
|
|
135
|
-
# (repeating the flag keeps only the last one)
|
|
136
|
-
vpx stryker run --mutate "src/components/button/button.vue,src/composables/button/button.ts"
|
|
137
|
-
|
|
138
|
-
# One E2E spec
|
|
139
|
-
vpr test:e2e e2e/components/button.spec.ts
|
|
140
|
-
```
|
|
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
|
-
|
|
148
|
-
## Adding a new component
|
|
149
|
-
|
|
150
|
-
Use an existing component (e.g. `button`) as the reference, and deliver all of the following in the same PR:
|
|
151
|
-
|
|
152
|
-
1. `src/components/<name>/<name>.vue` (following the pattern in [Code conventions](#code-conventions)), `types.ts` and `__tests__/<name>.test.ts`.
|
|
153
|
-
2. The `U`-prefixed export in `src/components/index.ts`.
|
|
154
|
-
3. A usage guide at `docs/guide/components/<name>.md`, with `<demo>` examples (and a `<playground>` section when the component has configurable props), and an API reference at `docs/api/components/<name>.md` (props, events and slots tables). Both in Portuguese. In a demo's template inside a `.md` file, don't put an attribute with a template literal after one with an arrow function in the same tag: the Markdown type-check (`vue-tsc`) reads the `>` of `=>` as the end of the tag and fails with `TS1003`. Order the attributes so the template literal comes first.
|
|
155
|
-
4. Both pages registered in the sidebar at `docs/.vitepress/config.ts`.
|
|
156
|
-
5. `e2e/components/<name>.spec.ts` covering the guide's interactive examples, an "accessibility" check (see [Testing conventions](#testing-conventions)) and a "UI consistency" screenshot check (plus accessible snapshots where relevant). Commit the generated files under `__snapshots__`/`__screenshot__`.
|
|
157
|
-
6. The full CI sequence passing locally.
|
|
158
|
-
|
|
159
|
-
## Adding a new composable
|
|
160
|
-
|
|
161
|
-
Use an existing composable (e.g. `use-toast`) as the reference, and deliver in the same PR:
|
|
162
|
-
|
|
163
|
-
1. `src/composables/<group>/<name>.ts` and `__tests__/<name>.test.ts`, plus the types in the group's `types.ts`. If it relies on an internal component (as `useToast` relies on the internal `Toast` rendered by `UMain`), that component follows the component layout but isn't exported.
|
|
164
|
-
2. The export in `src/composables/index.ts`, and its assertion in `src/composables/__tests__/index.test.ts`.
|
|
165
|
-
3. A usage guide at `docs/guide/composables/<name>.md` and an API reference at `docs/api/composables/<name>.md`, both in Portuguese, registered in both sidebars at `docs/.vitepress/config.ts` and listed in `docs/api/index.md` and in the table of `docs/guide/index.md`. If it relies on `UMain` (like `useToast`), also mention it in `UMain`'s guide and API pages.
|
|
166
|
-
4. `e2e/composables/<name>.spec.ts` covering the guide's examples, an "accessibility" check (see [Testing conventions](#testing-conventions)) and a "UI consistency" screenshot check.
|
|
167
|
-
5. The full CI sequence passing locally.
|
|
168
|
-
|
|
169
|
-
## Code conventions
|
|
170
|
-
|
|
171
|
-
- All `src` code is written in English. `docs` content is written in Portuguese (aimed at Brazilian users), even though file/dir names stay in English.
|
|
172
|
-
- `docs` must not mention Vuetify
|
|
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`).
|
|
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.
|
|
176
|
-
- It also limits Vuetify's utility classes to the families the components use, without breakpoint and print variants, and its color classes to black, white and transparent, since the utility stylesheet blocks the apps' first render. Using a class from another family (e.g. `ga-2` or `overflow-hidden`) means enabling its key in `$utilities` there; the unit tests of `src/__tests__/plugins.test.ts` fail when a class used in `src` isn't generated. The theme's colors (`bg-primary`, `text-primary-text`, ...) don't depend on it.
|
|
177
|
-
- 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. The one accepted layout margin is `mt-auto` on `v-card-actions` (as in `USection`), which pushes the actions to the bottom of a full-height card without setting any spacing value.
|
|
178
|
-
- 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. Props covered by the shared composables below are the exception: they always go through them.
|
|
179
|
-
- Reuse the shared internal composables for props that recur across components, so every component maps them to Vuetify the same way, instead of mapping them again:
|
|
180
|
-
- `fullWidth`/`fullHeight`: `useFullWidth`/`useFullHeight` (`src/composables/dimensions/dimensions.ts`), applying Vuetify's `w-100`/`h-100` classes even where the component has `width`/`height` props.
|
|
181
|
-
- `alignX`/`alignY`: `useAlignX`/`useAlignY` (`src/composables/alignment/alignment.ts`), with the `AlignX`/`AlignY` types from the group's `types.ts`.
|
|
182
|
-
- `color`: `useVuetifyColor` (`src/composables/colors/use-vuetify-color.ts`), with the `ColorVariant` type from the group's `types.ts`. Where the color is the text's instead of a fill (e.g. `UButton`'s `ghost` variant), use `useVuetifyTextColor` (`use-vuetify-text-color.ts`) instead: most fills are too light to reach WCAG AA's contrast as text, so each color has a text shade in the theme, darker than its fill (primary's fill is dark enough to be its own shade).
|
|
183
|
-
|
|
184
|
-
A new prop that does the same as one of these takes its name and type. When a second component needs a mapping that only one component has, move it into a shared composable first.
|
|
185
|
-
|
|
186
|
-
- 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.
|
|
187
|
-
- Path aliases: `@/*` → `src/*`, `@e2e/*` → `e2e/*`.
|
|
188
|
-
- A prop or option that points to a navigation destination is typed `RouteLocationRaw` (from `vue-router`) and named `route` (e.g. the items of `UNavigationMenu`), unless the name says which destination it is (e.g. `help` in `UAppBar`). Don't split it into `to`/`href` props: convert it with `useRouteProps`/`routeToProps` (`src/composables/navigation/use-route-props.ts`), which binds strings starting with `http` as `href` and anything else as the router's `to`.
|
|
189
|
-
- Every public component follows this pattern to block access to internals and give it an editor-hover description:
|
|
190
|
-
|
|
191
|
-
```vue
|
|
192
|
-
<script lang="ts">
|
|
193
|
-
/**
|
|
194
|
-
* A short description of the component, shown when hovering it in the
|
|
195
|
-
* editor.
|
|
196
|
-
*/
|
|
197
|
-
export default {
|
|
198
|
-
inheritAttrs: false,
|
|
199
|
-
};
|
|
200
|
-
</script>
|
|
201
|
-
|
|
202
|
-
<script setup lang="ts">
|
|
203
|
-
// component's implementation
|
|
204
|
-
</script>
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
- Component layout: `src/components/<name>/<name>.vue`, `types.ts` for prop/option types, `__tests__/<name>.test.ts` for unit tests. Components that belong to a group live in the group's folder (e.g. the internal `src/components/dialogs/toast.vue`).
|
|
208
|
-
- Composables live in a group folder: `src/composables/<group>/<name>.ts`, with `types.ts` and `__tests__/` in the group folder (e.g. `src/composables/dialogs/use-toast.ts`). A composable (`use-*.ts`) must depend on `vue` or `vue-router`, directly or through other modules (enforced by dependency-cruiser's `composables-use-vue` rule).
|
|
209
|
-
- Library-wide configuration constants that aren't components or composables live in `src/consts/<name>.ts` (e.g. `src/consts/locale.ts`, the language set by `createUimed()` and `vueTestUtilsPluginUimed()`), with their types in `src/consts/types.ts`.
|
|
210
|
-
- Icons are drawn as inline SVGs with the paths of `@mdi/js`, imported one by one so the apps' bundles only take the icons used: there's no icon font, so never use `mdi-*` classes. Every icon is in the internal `icons` map of `src/consts/icons.ts`, and every name in it is public (the `Icon` type in `src/consts/types.ts`):
|
|
211
|
-
- To add an icon, add it to that map and to the list on the "Ícones" guide page (`docs/guide/icons.md`), in the same order, and check that `@mdi/js` exports it: the unit test of `src/consts/icons.ts` checks every path, and the page's E2E spec fails when the page and the map list different icons. Names are camelCase, like the Vuetify aliases they must match (e.g. `checkboxOn`/`checkboxOff`). Reuse an existing name before adding one with the same drawing.
|
|
212
|
-
- Components use the map for their own icons too (`icons.help`, `icons.close`), never `$` aliases nor `mdi-*` strings.
|
|
213
|
-
- The map is also Vuetify's alias set (`createUimed()` and `vueTestUtilsPluginUimed()` pass it as `aliases`), since Vuetify's components draw some icons by alias on their own (e.g. `$clear` in clearable fields, `$prev`/`$next` in the pagination). It only has the aliases the library's components render, so a component that renders a new one (a new Vuetify component or prop) needs it added to the map, named exactly as the alias; without it, the icon is drawn empty, without a warning. Add each state that renders an alias to `src/consts/__tests__/icon-aliases.test.ts`, which fails on icons drawn without a path of `@mdi/js`.
|
|
214
|
-
- Pure helpers that don't use Vue live in `src/utils/<name>.ts`, with tests in `src/utils/__tests__/`. Generic helpers that any TypeScript project could use (e.g. `isDeepEqual`) belong in `@nexdom/shared` instead.
|
|
215
|
-
- No module in `src` has side effects at its top level (code that must run just because the module is imported, such as registering something globally), except the CSS imports of `src/index.ts`: `package.json` declares `"sideEffects": ["*.css"]`, so the apps' bundlers drop or move to another chunk any other module whose exports aren't used. A new side effect means adding its built file (e.g. `./dist/...`) to `sideEffects`.
|
|
216
|
-
- Public components/composables are re-exported from `src/components/index.ts` and `src/composables/index.ts`. Prop types stay internal (`types.ts` is not re-exported); consumers derive them with `ComponentProps` (see [Usage](#usage)).
|
|
217
|
-
- Every publicly exported component must use the `U` prefix on its export identifier (e.g., `UButton`, `UMain`), while internal file names and component names remain unprefixed.
|
|
218
|
-
- Use [JSDoc](https://jsdoc.app/about-getting-started) on every method/prop/type intended to be part of the public API — it's the primary documentation surface and supports markdown/code examples.
|
|
219
|
-
- Known workarounds (see CONTRIBUTING.md before touching related config): `stryker-vue-ignorer` patches a Stryker/Vue macro-hoisting issue; `vue-tsc` is used for type-check instead of Vite+'s built-in one due to an oxlint/Vue support gap; the internal `SkeletonLoader` wraps Vuetify's skeleton loader so its loading announcement gets valid ARIA attributes in server rendering; the navigation menu's rail (`useNavigationRail`) dispatches synthetic `mouseenter`/`mouseleave` events on Vuetify's navigation drawer, which only expands its rail on mouse events, to expand it on keyboard focus and keep it expanded when the mouse leaves with the focus inside. All are meant to be removed once fixed upstream — don't build further on top of them without checking if they're still needed.
|
|
220
|
-
|
|
221
|
-
## Testing conventions
|
|
222
|
-
|
|
223
|
-
- Unit tests use Vitest + `@vue/test-utils`, with `vueTestUtilsPluginUimed()` from `@/unit-test.ts` to mount a Vuetify instance.
|
|
224
|
-
- Global unit test setup (`src/__tests__/setup.ts`) stubs `visualViewport`, uses fake timers, and silences `console.error/warn/log`.
|
|
225
|
-
- With fake timers in jsdom, Vuetify transitions (e.g. of `VDialog`, `VMenu`) don't finish on their own. Emit the transition events on the Vuetify component (`wrapper.findComponent(VDialog).vm.$emit("afterLeave")`) or advance the timers with `await vi.runAllTimersAsync()`.
|
|
226
|
-
- 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.
|
|
227
|
-
- 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.
|
|
228
|
-
- Every E2E spec has an "accessibility" `describe` that calls `expectNoA11yViolations(page)` from `e2e/utils.ts` at least once. It runs axe against WCAG 2.2 AA and its best practices on the page's demos and overlays, in both of the docs' color schemes (the demos keep the library's light theme on a background of their own in dark mode), and fails on any violation. Check each state that renders something new, too: open overlays (menus, dialogs, toasts) and every color of a variant whose text takes the color (as `button.spec.ts` does for `ghost`). Contrast is the most common failure: the theme's colors and text shades must keep 4.5:1, which `src/__tests__/index.test.ts` checks. Don't make a check pass by disabling rules or excluding elements: fix the cause, or stop and ask. The helper only disables the landmark rules that fail because of the docs' layout, with a comment saying why.
|
|
229
|
-
- 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.
|
|
230
|
-
- 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.
|
|
231
|
-
- 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.
|
|
232
|
-
- 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.
|
|
233
|
-
- Never run Stryker at the same time as another test command: they share build output and Stryker fails with `ENOENT`.
|
|
234
|
-
- 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.
|
|
235
|
-
- In E2E, a paused `page.clock` also freezes transitions, so overlays never finish leaving. Use `pauseClock(page)` from `e2e/utils.ts` 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`). Don't call `page.clock.pauseAt` with the test runner's time (`new Date()`, `Date.now()`): the page clock can be ahead of it, and the pause then fails with "Cannot fast-forward to the past". `pauseClock` pauses from the page's own time instead.
|
|
236
|
-
|
|
237
|
-
## Public API design
|
|
238
|
-
|
|
239
|
-
The public API is hard to change once released, and every prop, event or option is something consumers can misuse and maintainers must keep. Keep it minimal:
|
|
240
|
-
|
|
241
|
-
- Only add what the issue asks for. Anything beyond it (an extra prop, event, option or variant) is a separate decision the maintainers must approve explicitly, not a default.
|
|
242
|
-
- Behaviors meant to be consistent across the library aren't per-instance options. For example, every dialog closes with `Esc`, a click outside it or the browser's back button; a flag to block that stays internal.
|
|
243
|
-
- Reuse existing contracts before creating new ones: `v-model` for open/closed state instead of extra lifecycle events such as `afterLeave`, the `actions` shape of `USection` for buttons, the option names of existing composables.
|
|
244
|
-
|
|
245
|
-
## Library documentation and dependencies
|
|
246
|
-
|
|
247
|
-
The stack is newer than most AI models' training data (Vue 3.5, Vuetify 4, Vue Router 5, Vite+ 0.2, Vitest 4, TypeScript 6, VitePress 2 alpha, Stryker 10, Playwright 1.62), so don't rely on memory for library APIs.
|
|
248
|
-
|
|
249
|
-
- Before using an API this repo doesn't use yet, or implementing something from scratch, look it up in the version declared in `package.json`. The project's `.mcp.json` provides two servers for that:
|
|
250
|
-
- `context7`, with these library IDs: Vue `/websites/vuejs`, Vuetify `/websites/vuetifyjs_en`, Vue Router `/websites/router_vuejs`, Vite+ `/websites/viteplus_dev`, Vitest `/vitest-dev/vitest`, Vue Test Utils `/vuejs/test-utils`, Playwright `/microsoft/playwright`, VitePress `/vuejs/vitepress`, Stryker `/stryker-mutator/stryker-js`.
|
|
251
|
-
- `vuetify`, Vuetify's own server, for component and composable APIs and release notes. Its tools that create or update bins, links, playgrounds or bug reports publish content outside the repo and are denied in `.claude/settings.json`.
|
|
252
|
-
- Queries to both servers leave your machine: describe what you need in generic terms and never include source code or business rules.
|
|
253
|
-
- Before adding a dependency, check whether Vuetify, `@nexdom/shared` or the current dependencies already cover the need. If not, confirm with the requester, check the package's docs, maintenance and license, and declare runtime dependencies as `peerDependencies` (enforced by dependency-cruiser's `use-peer-deps` rule).
|
|
254
|
-
- Vuetify is an internal detail: consumers use the library's props, and `docs` never mention Vuetify.
|
|
255
|
-
|
|
256
|
-
## AI agents
|
|
257
|
-
|
|
258
|
-
Besides this file, the repo ships Claude Code subagents in `.claude/agents/`:
|
|
259
|
-
|
|
260
|
-
- `issue-planner`: turns an issue into an API proposal plus open questions, before any code is written.
|
|
261
|
-
- `issue-implementer`: implements an issue end to end (source, tests, docs, E2E).
|
|
262
|
-
- `code-reviewer`: reviews a branch or PR against these conventions, without changing code.
|
|
263
|
-
- `dependency-updater`: evaluates and applies dependency updates, such as Dependabot PRs.
|
|
264
|
-
|
|
265
|
-
Issues and PRs live in `nexdom-healthtech/uimed-vue`. Pass `-R nexdom-healthtech/uimed-vue` to `gh`, since clones from forks have issues disabled, and read issues with `gh issue view <number> -R nexdom-healthtech/uimed-vue --json title,body,comments` (`--comments` prints nothing in non-interactive shells).
|
|
266
|
-
|
|
267
|
-
To resolve an issue, run `/resolve-issue <number>` (`.claude/skills/resolve-issue/`). It chains planner, implementer and reviewer, asks you about open questions and waits for your approval of the public API before any code is written.
|
|
268
|
-
|
|
269
|
-
## Git workflow
|
|
270
|
-
|
|
271
|
-
- Trunk-based development. `main` is the only long-lived branch (`beta`/`alpha` exist only for pre-release/prototype work — see CONTRIBUTING.md for when to use them). `main`, `beta`, `alpha` are all protected against direct pushes.
|
|
272
|
-
- Short-lived branches are named after their intention (e.g. `fix-some-method-behavior`) and are deleted after merging back via PR.
|
|
273
|
-
- Commit messages MUST follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) — enforced by commitlint in CI. Recent examples: `feat(components): ...`, `fix(composables): ...`, `test(e2e): ...`, `docs(root): ...`, `chore: ...`.
|
|
274
|
-
- Releases are automated via `semantic-release` (conventional-commits preset) — don't hand-edit versions/changelogs.
|
package/CLAUDE.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
@AGENTS.md
|