@1doc/1ds-react 0.0.1 → 2.7.1
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 +667 -0
- package/README.md +138 -6
- package/package.json +43 -10
- package/src/index.ts +161 -16
- package/src/teste/AttachmentCard.test.tsx +161 -0
- package/src/teste/Chart.test.tsx +233 -0
- package/src/teste/Combobox.test.tsx +251 -0
- package/src/teste/DataTable.test.tsx +70 -0
- package/src/teste/DatePicker.test.tsx +86 -0
- package/src/teste/Drawer.test.tsx +265 -0
- package/src/teste/Dropdown.test.tsx +217 -0
- package/src/teste/Input.test.tsx +64 -0
- package/src/teste/Kanban.test.tsx +333 -0
- package/src/teste/MultiSelect.test.tsx +73 -0
- package/src/teste/Podium.test.tsx +107 -0
- package/src/teste/Popover.test.tsx +221 -0
- package/src/teste/SegmentedControl.test.tsx +183 -0
- package/src/teste/Timeline.test.tsx +100 -0
- package/src/teste/Tree.test.tsx +74 -0
- package/src/teste/setup.ts +7 -0
- package/src/utils/bandeiras.ts +22 -0
- package/src/utils/chart.ts +682 -0
- package/src/utils/density.ts +27 -0
- package/src/utils/overlay-stack.ts +26 -0
- package/src/utils/theme.ts +63 -47
- package/src/utils/toast.ts +89 -0
- package/vitest.config.ts +28 -0
- package/dist/components/Alert/Alert.css +0 -188
- package/dist/components/Alert/Alert.d.ts +0 -65
- package/dist/components/Alert/Alert.js +0 -56
- package/dist/components/Avatar/Avatar.css +0 -88
- package/dist/components/Avatar/Avatar.d.ts +0 -34
- package/dist/components/Avatar/Avatar.js +0 -16
- package/dist/components/Avatar/AvatarGroup.d.ts +0 -12
- package/dist/components/Avatar/AvatarGroup.js +0 -8
- package/dist/components/Badge/Badge.css +0 -155
- package/dist/components/Badge/Badge.d.ts +0 -46
- package/dist/components/Badge/Badge.js +0 -17
- package/dist/components/Button/Button.css +0 -199
- package/dist/components/Button/Button.d.ts +0 -23
- package/dist/components/Button/Button.js +0 -62
- package/dist/components/Checkbox/Checkbox.css +0 -122
- package/dist/components/Checkbox/Checkbox.d.ts +0 -21
- package/dist/components/Checkbox/Checkbox.js +0 -49
- package/dist/components/Divider/Divider.css +0 -81
- package/dist/components/Divider/Divider.d.ts +0 -18
- package/dist/components/Divider/Divider.js +0 -26
- package/dist/components/Grid/Grid.css +0 -154
- package/dist/components/Grid/Grid.d.ts +0 -21
- package/dist/components/Grid/Grid.js +0 -53
- package/dist/components/Icon/Icon.css +0 -41
- package/dist/components/Icon/Icon.d.ts +0 -12
- package/dist/components/Icon/Icon.js +0 -29
- package/dist/components/Input/Input.css +0 -190
- package/dist/components/Input/Input.d.ts +0 -32
- package/dist/components/Input/Input.js +0 -58
- package/dist/components/Link/Link.css +0 -76
- package/dist/components/Link/Link.d.ts +0 -15
- package/dist/components/Link/Link.js +0 -19
- package/dist/components/Logo/Logo.css +0 -32
- package/dist/components/Logo/Logo.d.ts +0 -10
- package/dist/components/Logo/Logo.js +0 -16
- package/dist/components/Modal/Modal.css +0 -155
- package/dist/components/Modal/Modal.d.ts +0 -36
- package/dist/components/Modal/Modal.js +0 -55
- package/dist/components/Radio/Radio.css +0 -114
- package/dist/components/Radio/Radio.d.ts +0 -19
- package/dist/components/Radio/Radio.js +0 -39
- package/dist/components/Select/Select.css +0 -291
- package/dist/components/Select/Select.d.ts +0 -38
- package/dist/components/Select/Select.js +0 -246
- package/dist/components/Spinner/Spinner.css +0 -60
- package/dist/components/Spinner/Spinner.d.ts +0 -16
- package/dist/components/Spinner/Spinner.js +0 -16
- package/dist/components/Switch/Switch.css +0 -127
- package/dist/components/Switch/Switch.d.ts +0 -18
- package/dist/components/Switch/Switch.js +0 -57
- package/dist/components/Tabs/Tab.d.ts +0 -19
- package/dist/components/Tabs/Tab.js +0 -76
- package/dist/components/Tabs/TabContent.d.ts +0 -15
- package/dist/components/Tabs/TabContent.js +0 -11
- package/dist/components/Tabs/Tabs.css +0 -124
- package/dist/components/Tabs/Tabs.d.ts +0 -20
- package/dist/components/Tabs/Tabs.js +0 -14
- package/dist/components/Tabs/TabsContent.d.ts +0 -16
- package/dist/components/Tabs/TabsContent.js +0 -12
- package/dist/components/Tabs/tabs.context.d.ts +0 -2
- package/dist/components/Tabs/tabs.context.js +0 -2
- package/dist/components/Tag/Tag.css +0 -140
- package/dist/components/Tag/Tag.d.ts +0 -63
- package/dist/components/Tag/Tag.js +0 -48
- package/dist/components/Text/Text.css +0 -1213
- package/dist/components/Text/Text.d.ts +0 -70
- package/dist/components/Text/Text.js +0 -89
- package/dist/components/Textarea/Textarea.css +0 -133
- package/dist/components/Textarea/Textarea.d.ts +0 -28
- package/dist/components/Textarea/Textarea.js +0 -72
- package/dist/index.d.ts +0 -25
- package/dist/index.js +0 -27
- package/dist/utils/theme.d.ts +0 -30
- package/dist/utils/theme.js +0 -106
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# @1doc/1ds-react
|
|
2
2
|
|
|
3
|
-
Biblioteca de componentes React do
|
|
3
|
+
Biblioteca de componentes React do 1DS Design System.
|
|
4
|
+
|
|
5
|
+
📚 **Documentação viva (Storybook):** https://1dsux.vercel.app
|
|
4
6
|
|
|
5
7
|
## Instalação
|
|
6
8
|
|
|
@@ -8,17 +10,24 @@ Biblioteca de componentes React do 1ds Design System.
|
|
|
8
10
|
pnpm add @1doc/1ds-react @1doc/1ds-tokens
|
|
9
11
|
```
|
|
10
12
|
|
|
13
|
+
`@1doc/1ds-tokens` é **peerDependency** — o app instala e importa os CSS de
|
|
14
|
+
tokens diretamente (ver abaixo).
|
|
15
|
+
|
|
11
16
|
## Configuração
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
> [!IMPORTANT]
|
|
19
|
+
> Importe o CSS dos tokens no ponto de entrada da aplicação (`main.tsx`),
|
|
20
|
+
> **nesta ordem**. Sem esses imports, os componentes ficam sem estilo.
|
|
14
21
|
|
|
15
22
|
```tsx
|
|
16
|
-
import "@1doc/1ds-tokens/
|
|
17
|
-
import "@1doc/1ds-tokens/
|
|
18
|
-
import "@1doc/1ds-tokens/
|
|
19
|
-
import "iconoir/css/iconoir.css";
|
|
23
|
+
import "@1doc/1ds-tokens/css/primitives.css";
|
|
24
|
+
import "@1doc/1ds-tokens/css/semantic.css";
|
|
25
|
+
import "@1doc/1ds-tokens/css/theme-1doc.css"; // tema da sua marca (ver Temas)
|
|
20
26
|
```
|
|
21
27
|
|
|
28
|
+
Os caminhos `dist/css/...` continuam funcionando, mas prefira os subpaths
|
|
29
|
+
estáveis `css/...` acima.
|
|
30
|
+
|
|
22
31
|
## Uso
|
|
23
32
|
|
|
24
33
|
```tsx
|
|
@@ -32,3 +41,126 @@ function App() {
|
|
|
32
41
|
);
|
|
33
42
|
}
|
|
34
43
|
```
|
|
44
|
+
|
|
45
|
+
## Componentes
|
|
46
|
+
|
|
47
|
+
Accordion · Alert · Avatar/AvatarGroup · Badge · Breadcrumb · Button · Card ·
|
|
48
|
+
Checkbox · Chips · ConfirmDialog · DataTable · DatePicker · Divider · Dropdown ·
|
|
49
|
+
EmptyState · FileInput · Grid · Icon · Input · Link · Logo · Modal · MultiSelect ·
|
|
50
|
+
PageHeader · Pagination · PhoneInput · Portal · ProgressBar · Radio · Select ·
|
|
51
|
+
Skeleton · Spinner · Stepper · Switch · Tabs · Tag · Text · Textarea ·
|
|
52
|
+
TimePicker · Toast/ToastStack · Tooltip
|
|
53
|
+
|
|
54
|
+
Props e exemplos de cada componente: Storybook (link acima) ou os `.d.ts`
|
|
55
|
+
(JSDoc em português em todas as props).
|
|
56
|
+
|
|
57
|
+
> [!NOTE]
|
|
58
|
+
> As interfaces de props são **estritas** (sem `[key: string]: any`): typo de
|
|
59
|
+
> prop é erro de compilação. Atributos arbitrários (`data-*` etc.) **não são
|
|
60
|
+
> repassados ao DOM** pelos componentes — antes compilavam mas eram
|
|
61
|
+
> silenciosamente descartados; agora o TypeScript avisa.
|
|
62
|
+
|
|
63
|
+
## Ícones
|
|
64
|
+
|
|
65
|
+
Os ícones **internos** dos componentes (chevrons, calendário, setas de
|
|
66
|
+
ordenação, ×, etc.) funcionam **out-of-the-box** — cada componente importa
|
|
67
|
+
seus glifos diretamente (tree-shakeable, só o que é usado entra no bundle).
|
|
68
|
+
|
|
69
|
+
Para ícones do **seu app** (`<Icon icon="...">`, `startIcon`, `Tab.icon`...),
|
|
70
|
+
há dois caminhos:
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
// 1) Recomendado: objeto importado (tree-shakeable, typo = erro de compilação)
|
|
74
|
+
import { faHouse } from "@fortawesome/free-solid-svg-icons";
|
|
75
|
+
<Icon icon={faHouse} />;
|
|
76
|
+
|
|
77
|
+
// 2) Por nome (string): exige registrar os ícones na library UMA vez no bootstrap
|
|
78
|
+
import { library } from "@1doc/1ds-react";
|
|
79
|
+
import { faHouse, faGear } from "@fortawesome/free-solid-svg-icons";
|
|
80
|
+
library.add(faHouse, faGear);
|
|
81
|
+
<Icon icon="house" />;
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
> [!WARNING]
|
|
85
|
+
> Evite `library.add(fas)` (pacote inteiro): adiciona ~900 kB ao bundle.
|
|
86
|
+
> Registre apenas os ícones que o app usa, ou prefira objetos importados.
|
|
87
|
+
> Use packs FontAwesome na faixa **6.x** (compatível com o svg-core do DS).
|
|
88
|
+
|
|
89
|
+
## Temas
|
|
90
|
+
|
|
91
|
+
Tema via atributo `data-theme` no `<html>`. Utilitários em `@1doc/1ds-react`:
|
|
92
|
+
`setTheme` · `getTheme` · `initTheme` · `onThemeChange` · `registerTheme` ·
|
|
93
|
+
`getAvailableThemes`.
|
|
94
|
+
|
|
95
|
+
| Marca | Light (arquivo / setTheme) | Dark |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| 1Doc | `theme-1doc.css` / `"1doc"` | `theme-1doc-dark.css` / `"1doc-dark"` |
|
|
98
|
+
| Green | `theme-1ds-green.css` / `"1ds-green"` | `theme-1ds-green-dark.css` / `"1ds-green-dark"` |
|
|
99
|
+
| Orange | `theme-1ds-orange.css` / `"1ds-orange"` | `theme-1ds-orange-dark.css` / `"1ds-orange-dark"` |
|
|
100
|
+
| Purple | `theme-1ds-purple.css` / `"1ds-purple"` | `theme-1ds-purple-dark.css` / `"1ds-purple-dark"` |
|
|
101
|
+
| Red | `theme-1ds-red.css` / `"1ds-red"` | `theme-1ds-red-dark.css` / `"1ds-red-dark"` |
|
|
102
|
+
| Yellow | `theme-1ds-yellow.css` / `"1ds-yellow"` | `theme-1ds-yellow-dark.css` / `"1ds-yellow-dark"` |
|
|
103
|
+
|
|
104
|
+
Alto contraste (acessibilidade, fora da matriz de marcas): `theme-1doc-high-contrast.css` /
|
|
105
|
+
`"1doc-high-contrast"` — fundo preto, texto branco, interativo amarelo. O DS não o aplica
|
|
106
|
+
sozinho; ver a seção "Alto contraste" do README de `@1doc/1ds-tokens` para seguir
|
|
107
|
+
`prefers-contrast: more`.
|
|
108
|
+
|
|
109
|
+
> O CSS do tema escolhido precisa estar **importado** para o `setTheme` ter
|
|
110
|
+
> efeito. Todos os temas podem ser carregados juntos (cada um é escopado em
|
|
111
|
+
> `[data-theme="..."]`) — a troca em runtime é atômica.
|
|
112
|
+
|
|
113
|
+
### Tema de marca própria (white-label)
|
|
114
|
+
|
|
115
|
+
Crie um CSS com `[data-theme="minha-marca"]` sobrescrevendo os 25 tokens
|
|
116
|
+
`--umds-theme-*` (contrato idêntico aos 12 temas oficiais — o mínimo para a
|
|
117
|
+
cor de marca são os 4 `primary-*`), carregue-o após o `semantic.css` e
|
|
118
|
+
registre o tema para o utilitário aceitá-lo (validação + persistência):
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
import { registerTheme, initTheme, setTheme } from "@1doc/1ds-react";
|
|
122
|
+
|
|
123
|
+
registerTheme("minha-marca");
|
|
124
|
+
registerTheme("minha-marca-dark");
|
|
125
|
+
initTheme(); // restaura tema persistido (inclusive custom, já registrado)
|
|
126
|
+
setTheme("minha-marca");
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Formulários
|
|
130
|
+
|
|
131
|
+
Todos os campos participam do submit nativo (`<form>` + `FormData`) via prop
|
|
132
|
+
`name` — Select/DatePicker/TimePicker/MultiSelect emitem hidden inputs;
|
|
133
|
+
PhoneInput emite também `{name}_ddi`; FileInput mantém os `File` no input.
|
|
134
|
+
Contrato de validação uniforme: `invalid`/`errorMessage`/`helperText`
|
|
135
|
+
(com `aria-describedby`).
|
|
136
|
+
|
|
137
|
+
Em viewports ≤640px os campos de texto sobem automaticamente para fonte 16px
|
|
138
|
+
(evita o zoom do Safari iOS ao focar).
|
|
139
|
+
|
|
140
|
+
## Modo touch (densidade)
|
|
141
|
+
|
|
142
|
+
Para apps mobile/touch, ative o modo touch — os alvos de toque dos controles
|
|
143
|
+
interativos (botões, campos, checkboxes, paginação, calendário…) sobem para
|
|
144
|
+
≥44px, aumentando controle e espaçamento juntos:
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
import { setDensity } from "@1doc/1ds-react";
|
|
148
|
+
|
|
149
|
+
setDensity("touch"); // <html data-density="touch">
|
|
150
|
+
setDensity("default"); // volta ao padrão desktop
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Elementos inline compactos (Tag, Chips) mantêm o tamanho — em contextos touch,
|
|
154
|
+
dê espaçamento entre eles no layout.
|
|
155
|
+
|
|
156
|
+
## Toasts
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import { ToastStack, toast } from "@1doc/1ds-react";
|
|
160
|
+
|
|
161
|
+
// 1× na raiz do app:
|
|
162
|
+
<ToastStack position="top-right" />;
|
|
163
|
+
|
|
164
|
+
// em qualquer handler:
|
|
165
|
+
toast({ message: "Salvo!", variant: "positive" });
|
|
166
|
+
```
|
package/package.json
CHANGED
|
@@ -1,26 +1,59 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1doc/1ds-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.7.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
|
+
"sideEffects": [
|
|
6
|
+
"**/*.css"
|
|
7
|
+
],
|
|
5
8
|
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./utils/theme": {
|
|
15
|
+
"types": "./dist/utils/theme.d.ts",
|
|
16
|
+
"default": "./dist/utils/theme.js"
|
|
17
|
+
},
|
|
18
|
+
"./utils/toast": {
|
|
19
|
+
"types": "./dist/utils/toast.d.ts",
|
|
20
|
+
"default": "./dist/utils/toast.js"
|
|
21
|
+
},
|
|
22
|
+
"./dist/*": "./dist/*",
|
|
23
|
+
"./src/*": "./src/*",
|
|
24
|
+
"./package.json": "./package.json"
|
|
25
|
+
},
|
|
6
26
|
"dependencies": {
|
|
7
|
-
"
|
|
8
|
-
"@
|
|
27
|
+
"@floating-ui/dom": "^1.7.6",
|
|
28
|
+
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
|
29
|
+
"@fortawesome/free-regular-svg-icons": "^6.7.2",
|
|
30
|
+
"@fortawesome/free-solid-svg-icons": "^6.5.0"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@1doc/1ds-tokens": ">=0.1.0",
|
|
34
|
+
"react": ">=18",
|
|
35
|
+
"react-dom": ">=18"
|
|
9
36
|
},
|
|
10
37
|
"devDependencies": {
|
|
38
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
39
|
+
"@testing-library/react": "^16.3.3",
|
|
40
|
+
"@testing-library/user-event": "^14.6.1",
|
|
11
41
|
"@types/react": "^18.0.0",
|
|
42
|
+
"@types/react-dom": "^18.0.0",
|
|
43
|
+
"jsdom": "^26.1.0",
|
|
12
44
|
"react": "^18.0.0",
|
|
45
|
+
"react-dom": "^18.0.0",
|
|
13
46
|
"typescript": "^5.0.0",
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"react": ">=18"
|
|
47
|
+
"vitest": "^3.2.7",
|
|
48
|
+
"@1doc/1ds-core": "2.7.2",
|
|
49
|
+
"@1doc/1ds-tokens": "0.4.1"
|
|
18
50
|
},
|
|
19
51
|
"publishConfig": {
|
|
20
|
-
"access": "public"
|
|
21
|
-
"registry": "https://registry.npmjs.org/"
|
|
52
|
+
"access": "public"
|
|
22
53
|
},
|
|
23
54
|
"scripts": {
|
|
24
|
-
"build": "tsc && node scripts/postbuild.js"
|
|
55
|
+
"build": "tsc && node scripts/postbuild.js",
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"test:watch": "vitest"
|
|
25
58
|
}
|
|
26
59
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,28 +1,173 @@
|
|
|
1
1
|
// Theme utilities
|
|
2
2
|
export * from "./utils/theme";
|
|
3
3
|
|
|
4
|
+
// Fila imperativa de toasts (toast/dismissToast/subscribeToasts) — host: <ToastStack/>
|
|
5
|
+
export * from "./utils/toast";
|
|
6
|
+
// Densidade global (modo touch ≥44px via data-density)
|
|
7
|
+
export * from "./utils/density";
|
|
8
|
+
// FontAwesome
|
|
9
|
+
export { library } from "@fortawesome/fontawesome-svg-core";
|
|
10
|
+
|
|
4
11
|
// Components
|
|
5
|
-
export { default as
|
|
6
|
-
export { default as
|
|
7
|
-
export { default as
|
|
8
|
-
export { default as
|
|
9
|
-
export { default as
|
|
12
|
+
export { default as Accordion } from "./components/Accordion/Accordion";
|
|
13
|
+
export { default as AILabel } from "./components/AILabel/AILabel";
|
|
14
|
+
export { default as AIContent } from "./components/AIContent/AIContent";
|
|
15
|
+
export { default as ConfirmAIAction } from "./components/ConfirmAIAction/ConfirmAIAction";
|
|
16
|
+
export { default as Alert } from "./components/Alert/Alert";
|
|
17
|
+
export { default as AppHeader } from "./components/AppHeader/AppHeader";
|
|
18
|
+
export { default as AppHeaderAction } from "./components/AppHeaderAction/AppHeaderAction";
|
|
19
|
+
export { default as AppNavItem } from "./components/AppNavItem/AppNavItem";
|
|
20
|
+
export { default as AppShell } from "./components/AppShell/AppShell";
|
|
21
|
+
export { default as AppSidebar } from "./components/AppSidebar/AppSidebar";
|
|
22
|
+
export { default as AppSubheader } from "./components/AppSubheader/AppSubheader";
|
|
23
|
+
export { default as AttachmentCard } from "./components/AttachmentCard/AttachmentCard";
|
|
10
24
|
export { default as Avatar } from "./components/Avatar/Avatar";
|
|
11
25
|
export { default as AvatarGroup } from "./components/Avatar/AvatarGroup";
|
|
12
|
-
export { default as
|
|
13
|
-
export { default as
|
|
14
|
-
export { default as
|
|
26
|
+
export { default as Badge } from "./components/Badge/Badge";
|
|
27
|
+
export { default as BarChart } from "./components/Chart/BarChart";
|
|
28
|
+
export { default as BottomNavigation } from "./components/BottomNavigation/BottomNavigation";
|
|
29
|
+
export { default as BottomSheet } from "./components/BottomSheet/BottomSheet";
|
|
30
|
+
export { default as Box } from "./components/Box/Box";
|
|
31
|
+
export { default as Breadcrumb } from "./components/Breadcrumb/Breadcrumb";
|
|
32
|
+
export { default as Button } from "./components/Button/Button";
|
|
33
|
+
export { default as Card } from "./components/Card/Card";
|
|
15
34
|
export { default as Checkbox } from "./components/Checkbox/Checkbox";
|
|
16
|
-
export { default as
|
|
17
|
-
export { default as
|
|
35
|
+
export { default as Chips } from "./components/Chips/Chips";
|
|
36
|
+
export { default as Combobox } from "./components/Combobox/Combobox";
|
|
37
|
+
export { default as ConfirmDialog } from "./components/ConfirmDialog/ConfirmDialog";
|
|
38
|
+
export { default as Container } from "./components/Container/Container";
|
|
39
|
+
export { default as DataTable } from "./components/DataTable/DataTable";
|
|
40
|
+
export { default as DatePicker } from "./components/DatePicker/DatePicker";
|
|
41
|
+
export { default as Divider } from "./components/Divider/Divider";
|
|
42
|
+
export { default as DonutChart } from "./components/Chart/DonutChart";
|
|
43
|
+
export { default as Drawer } from "./components/Drawer/Drawer";
|
|
44
|
+
export { default as Dropdown } from "./components/Dropdown/Dropdown";
|
|
45
|
+
export { default as EmptyState } from "./components/EmptyState/EmptyState";
|
|
46
|
+
export { default as FileInput } from "./components/FileInput/FileInput";
|
|
18
47
|
export { default as Grid } from "./components/Grid/Grid";
|
|
19
|
-
export { default as
|
|
48
|
+
export { default as Icon } from "./components/Icon/Icon";
|
|
49
|
+
export { default as Input } from "./components/Input/Input";
|
|
50
|
+
export { default as Kanban } from "./components/Kanban/Kanban";
|
|
51
|
+
export { default as KanbanCard } from "./components/Kanban/KanbanCard";
|
|
52
|
+
export { default as KanbanColumn } from "./components/Kanban/KanbanColumn";
|
|
53
|
+
export { default as Link } from "./components/Link/Link";
|
|
54
|
+
export { default as LineChart } from "./components/Chart/LineChart";
|
|
55
|
+
export { default as Logo } from "./components/Logo/Logo";
|
|
56
|
+
export { default as Modal } from "./components/Modal/Modal";
|
|
57
|
+
export { default as MultiSelect } from "./components/MultiSelect/MultiSelect";
|
|
58
|
+
export { default as PageHeader } from "./components/PageHeader/PageHeader";
|
|
59
|
+
export { default as Pagination } from "./components/Pagination/Pagination";
|
|
60
|
+
export { default as PhoneInput } from "./components/PhoneInput/PhoneInput";
|
|
61
|
+
export { default as Podium } from "./components/Podium/Podium";
|
|
62
|
+
export { default as ProgressBar } from "./components/ProgressBar/ProgressBar";
|
|
63
|
+
export { default as Radio } from "./components/Radio/Radio";
|
|
64
|
+
export { default as RadioGroup } from "./components/Radio/RadioGroup";
|
|
65
|
+
export { default as SegmentedControl } from "./components/SegmentedControl/SegmentedControl";
|
|
20
66
|
export { default as Select } from "./components/Select/Select";
|
|
21
|
-
export { default as
|
|
22
|
-
export { default as
|
|
67
|
+
export { default as SidePanel } from "./components/SidePanel/SidePanel";
|
|
68
|
+
export { default as SidePanelItem } from "./components/SidePanelItem/SidePanelItem";
|
|
69
|
+
export { default as SidePanelSearch } from "./components/SidePanelSearch/SidePanelSearch";
|
|
70
|
+
export { default as SidePanelSection } from "./components/SidePanelSection/SidePanelSection";
|
|
71
|
+
export { default as Skeleton } from "./components/Skeleton/Skeleton";
|
|
72
|
+
export { default as Spinner } from "./components/Spinner/Spinner";
|
|
73
|
+
export { default as Stack } from "./components/Stack/Stack";
|
|
74
|
+
export { default as StatCard } from "./components/StatCard/StatCard";
|
|
75
|
+
export { default as Stepper } from "./components/Stepper/Stepper";
|
|
76
|
+
export { default as Switch } from "./components/Switch/Switch";
|
|
23
77
|
export { default as Tab } from "./components/Tabs/Tab";
|
|
24
|
-
export { default as TabsContent } from "./components/Tabs/TabsContent";
|
|
25
78
|
export { default as TabContent } from "./components/Tabs/TabContent";
|
|
79
|
+
export { default as Tabs } from "./components/Tabs/Tabs";
|
|
80
|
+
export { default as TabsContent } from "./components/Tabs/TabsContent";
|
|
26
81
|
export { default as Tag } from "./components/Tag/Tag";
|
|
27
|
-
export { default as
|
|
28
|
-
export { default as
|
|
82
|
+
export { default as Text } from "./components/Text/Text";
|
|
83
|
+
export { default as Textarea } from "./components/Textarea/Textarea";
|
|
84
|
+
export { default as TimePicker } from "./components/TimePicker/TimePicker";
|
|
85
|
+
export { default as Timeline } from "./components/Timeline/Timeline";
|
|
86
|
+
export { default as Toast } from "./components/Toast/Toast";
|
|
87
|
+
export { default as ToastStack } from "./components/Toast/ToastStack";
|
|
88
|
+
export { default as Tooltip } from "./components/Tooltip/Tooltip";
|
|
89
|
+
export { default as Popover } from "./components/Popover/Popover";
|
|
90
|
+
export { default as Portal } from "./components/Portal/Portal";
|
|
91
|
+
export { default as Tree } from "./components/Tree/Tree";
|
|
92
|
+
|
|
93
|
+
// Component Props Types
|
|
94
|
+
export type * from "./components/Accordion/Accordion";
|
|
95
|
+
export type * from "./components/AILabel/AILabel";
|
|
96
|
+
export type * from "./components/AIContent/AIContent";
|
|
97
|
+
export type * from "./components/ConfirmAIAction/ConfirmAIAction";
|
|
98
|
+
export type * from "./components/Alert/Alert";
|
|
99
|
+
export type * from "./components/AppHeader/AppHeader";
|
|
100
|
+
export type * from "./components/AppHeaderAction/AppHeaderAction";
|
|
101
|
+
export type * from "./components/AppNavItem/AppNavItem";
|
|
102
|
+
export type * from "./components/AppShell/AppShell";
|
|
103
|
+
export type * from "./components/AppSidebar/AppSidebar";
|
|
104
|
+
export type * from "./components/AppSubheader/AppSubheader";
|
|
105
|
+
export type * from "./components/AttachmentCard/AttachmentCard";
|
|
106
|
+
export type * from "./components/Avatar/Avatar";
|
|
107
|
+
export type * from "./components/Avatar/AvatarGroup";
|
|
108
|
+
export type * from "./components/Badge/Badge";
|
|
109
|
+
export type * from "./components/Chart/BarChart";
|
|
110
|
+
export type * from "./components/BottomNavigation/BottomNavigation";
|
|
111
|
+
export type * from "./components/BottomSheet/BottomSheet";
|
|
112
|
+
export type * from "./components/Box/Box";
|
|
113
|
+
export type * from "./components/Breadcrumb/Breadcrumb";
|
|
114
|
+
export type * from "./components/Button/Button";
|
|
115
|
+
export type * from "./components/Card/Card";
|
|
116
|
+
export type * from "./components/Checkbox/Checkbox";
|
|
117
|
+
export type * from "./components/Chips/Chips";
|
|
118
|
+
export type * from "./components/Combobox/Combobox";
|
|
119
|
+
export type * from "./components/ConfirmDialog/ConfirmDialog";
|
|
120
|
+
export type * from "./components/Container/Container";
|
|
121
|
+
export type * from "./components/DataTable/DataTable";
|
|
122
|
+
export type * from "./components/DatePicker/DatePicker";
|
|
123
|
+
export type * from "./components/Divider/Divider";
|
|
124
|
+
export type * from "./components/Chart/DonutChart";
|
|
125
|
+
export type * from "./components/Drawer/Drawer";
|
|
126
|
+
export type * from "./components/Dropdown/Dropdown";
|
|
127
|
+
export type * from "./components/EmptyState/EmptyState";
|
|
128
|
+
export type * from "./components/FileInput/FileInput";
|
|
129
|
+
export type * from "./components/Grid/Grid";
|
|
130
|
+
export type * from "./components/Icon/Icon";
|
|
131
|
+
export type * from "./components/Input/Input";
|
|
132
|
+
export type * from "./components/Kanban/Kanban";
|
|
133
|
+
export type * from "./components/Kanban/KanbanCard";
|
|
134
|
+
export type * from "./components/Kanban/KanbanColumn";
|
|
135
|
+
export type * from "./components/Link/Link";
|
|
136
|
+
export type * from "./components/Chart/LineChart";
|
|
137
|
+
export type * from "./components/Logo/Logo";
|
|
138
|
+
export type * from "./components/Modal/Modal";
|
|
139
|
+
export type * from "./components/MultiSelect/MultiSelect";
|
|
140
|
+
export type * from "./components/PageHeader/PageHeader";
|
|
141
|
+
export type * from "./components/Pagination/Pagination";
|
|
142
|
+
export type * from "./components/PhoneInput/PhoneInput";
|
|
143
|
+
export type * from "./components/Podium/Podium";
|
|
144
|
+
export type * from "./components/ProgressBar/ProgressBar";
|
|
145
|
+
export type * from "./components/Radio/Radio";
|
|
146
|
+
export type * from "./components/Radio/RadioGroup";
|
|
147
|
+
export type * from "./components/SegmentedControl/SegmentedControl";
|
|
148
|
+
export type * from "./components/Select/Select";
|
|
149
|
+
export type * from "./components/SidePanel/SidePanel";
|
|
150
|
+
export type * from "./components/SidePanelItem/SidePanelItem";
|
|
151
|
+
export type * from "./components/SidePanelSearch/SidePanelSearch";
|
|
152
|
+
export type * from "./components/SidePanelSection/SidePanelSection";
|
|
153
|
+
export type * from "./components/Skeleton/Skeleton";
|
|
154
|
+
export type * from "./components/Spinner/Spinner";
|
|
155
|
+
export type * from "./components/Stack/Stack";
|
|
156
|
+
export type * from "./components/StatCard/StatCard";
|
|
157
|
+
export type * from "./components/Stepper/Stepper";
|
|
158
|
+
export type * from "./components/Switch/Switch";
|
|
159
|
+
export type * from "./components/Tabs/Tab";
|
|
160
|
+
export type * from "./components/Tabs/TabContent";
|
|
161
|
+
export type * from "./components/Tabs/Tabs";
|
|
162
|
+
export type * from "./components/Tabs/TabsContent";
|
|
163
|
+
export type * from "./components/Tag/Tag";
|
|
164
|
+
export type * from "./components/Text/Text";
|
|
165
|
+
export type * from "./components/Textarea/Textarea";
|
|
166
|
+
export type * from "./components/TimePicker/TimePicker";
|
|
167
|
+
export type * from "./components/Timeline/Timeline";
|
|
168
|
+
export type * from "./components/Toast/Toast";
|
|
169
|
+
export type * from "./components/Toast/ToastStack";
|
|
170
|
+
export type * from "./components/Tooltip/Tooltip";
|
|
171
|
+
export type * from "./components/Popover/Popover";
|
|
172
|
+
export type * from "./components/Portal/Portal";
|
|
173
|
+
export type * from "./components/Tree/Tree"
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { render, screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import AttachmentCard from "../components/AttachmentCard/AttachmentCard";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* O que precisa ser provado:
|
|
8
|
+
* - com `onOpen` existe UM alvo de foco, com nome que diz o que abre;
|
|
9
|
+
* - sem `onOpen` o cartão é leitura — nenhum botão finge ação;
|
|
10
|
+
* - "obrigatório" chega ao leitor de tela como texto, não só como asterisco;
|
|
11
|
+
* - sem `status` ou sem `statusLabel`, o selo não aparece vazio.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
describe("AttachmentCard · abrir", () => {
|
|
15
|
+
it("o nome do arquivo é o único botão, e o clique chama onOpen", async () => {
|
|
16
|
+
const usuario = userEvent.setup();
|
|
17
|
+
const aoAbrir = vi.fn();
|
|
18
|
+
render(
|
|
19
|
+
<AttachmentCard
|
|
20
|
+
fileName="cnh frente e verso.pdf"
|
|
21
|
+
documentType="CNH"
|
|
22
|
+
onOpen={aoAbrir}
|
|
23
|
+
/>,
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const botoes = screen.getAllByRole("button");
|
|
27
|
+
expect(botoes).toHaveLength(1);
|
|
28
|
+
expect(botoes[0]).toHaveAccessibleName("Abrir CNH: cnh frente e verso.pdf");
|
|
29
|
+
|
|
30
|
+
await usuario.click(botoes[0]);
|
|
31
|
+
expect(aoAbrir).toHaveBeenCalledTimes(1);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("abre pelo teclado", async () => {
|
|
35
|
+
const usuario = userEvent.setup();
|
|
36
|
+
const aoAbrir = vi.fn();
|
|
37
|
+
render(<AttachmentCard fileName="parecer.pdf" onOpen={aoAbrir} />);
|
|
38
|
+
|
|
39
|
+
await usuario.tab();
|
|
40
|
+
expect(screen.getByRole("button", { name: "Abrir parecer.pdf" })).toHaveFocus();
|
|
41
|
+
await usuario.keyboard("{Enter}");
|
|
42
|
+
expect(aoAbrir).toHaveBeenCalledTimes(1);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("sem onOpen não há botão", () => {
|
|
46
|
+
render(<AttachmentCard fileName="parecer.pdf" documentType="Parecer" />);
|
|
47
|
+
expect(screen.queryByRole("button")).toBeNull();
|
|
48
|
+
expect(screen.getByText("parecer.pdf")).toBeInTheDocument();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("AttachmentCard · conteúdo", () => {
|
|
53
|
+
it("obrigatório vira texto para leitor de tela", () => {
|
|
54
|
+
render(<AttachmentCard fileName="rg.pdf" documentType="RG" required />);
|
|
55
|
+
expect(screen.getByText("(obrigatório)")).toBeInTheDocument();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("mostra o selo de situação só com status e rótulo", () => {
|
|
59
|
+
const { rerender } = render(
|
|
60
|
+
<AttachmentCard fileName="t.pdf" status="negative" statusLabel="Rejeitado" />,
|
|
61
|
+
);
|
|
62
|
+
expect(screen.getByText("Rejeitado").closest(".umds-attachment-card__status"))
|
|
63
|
+
.toHaveClass("umds-attachment-card__status--negative");
|
|
64
|
+
|
|
65
|
+
rerender(<AttachmentCard fileName="t.pdf" status="negative" />);
|
|
66
|
+
expect(document.querySelector(".umds-attachment-card__status")).toBeNull();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const CONFERENCIA = {
|
|
71
|
+
description: "Dados pessoais confirmados com sucesso.",
|
|
72
|
+
fields: [
|
|
73
|
+
{ label: "Nº do CPF", value: "384.221.760-05", copyable: true },
|
|
74
|
+
{ label: "Situação cadastral", value: "Regular" },
|
|
75
|
+
],
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
function renderComPainel(extra: Record<string, any> = {}) {
|
|
79
|
+
return render(
|
|
80
|
+
<AttachmentCard
|
|
81
|
+
fileName="comprovante_cpf.pdf"
|
|
82
|
+
documentType="CPF"
|
|
83
|
+
status="positive"
|
|
84
|
+
statusLabel="Pré-aprovado"
|
|
85
|
+
statusDetails={CONFERENCIA}
|
|
86
|
+
onOpen={() => {}}
|
|
87
|
+
{...extra}
|
|
88
|
+
/>,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
describe("AttachmentCard · painel de conferência", () => {
|
|
93
|
+
it("hover abre sem tirar o foco de onde está, e sair fecha", async () => {
|
|
94
|
+
const usuario = userEvent.setup();
|
|
95
|
+
renderComPainel();
|
|
96
|
+
const selo = screen.getByRole("button", { name: /^Pré-aprovado/ });
|
|
97
|
+
|
|
98
|
+
await usuario.hover(selo);
|
|
99
|
+
const painel = await screen.findByRole("dialog", { name: "comprovante_cpf.pdf" });
|
|
100
|
+
expect(painel).toHaveTextContent("Dados pessoais confirmados com sucesso.");
|
|
101
|
+
expect(painel).toHaveTextContent("384.221.760-05");
|
|
102
|
+
expect(document.activeElement).toBe(document.body);
|
|
103
|
+
|
|
104
|
+
await usuario.unhover(selo);
|
|
105
|
+
await waitFor(() => expect(screen.queryByRole("dialog")).toBeNull());
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("clique fixa o painel, leva o foco para dentro e Esc devolve ao selo", async () => {
|
|
109
|
+
const usuario = userEvent.setup();
|
|
110
|
+
const aoAbrir = vi.fn();
|
|
111
|
+
renderComPainel({ onOpen: aoAbrir });
|
|
112
|
+
const selo = screen.getByRole("button", { name: /^Pré-aprovado/ });
|
|
113
|
+
|
|
114
|
+
await usuario.click(selo);
|
|
115
|
+
expect(selo).toHaveAttribute("aria-expanded", "true");
|
|
116
|
+
// Clicar no selo NÃO abre o arquivo — o selo fica acima do alvo esticado.
|
|
117
|
+
expect(aoAbrir).not.toHaveBeenCalled();
|
|
118
|
+
await waitFor(() =>
|
|
119
|
+
expect(screen.getByRole("button", { name: "Copiar Nº do CPF" })).toHaveFocus(),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// Fixo: tirar o ponteiro não fecha.
|
|
123
|
+
await usuario.unhover(selo);
|
|
124
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
125
|
+
expect(screen.getByRole("dialog")).toBeInTheDocument();
|
|
126
|
+
|
|
127
|
+
await usuario.keyboard("{Escape}");
|
|
128
|
+
await waitFor(() => expect(screen.queryByRole("dialog")).toBeNull());
|
|
129
|
+
expect(selo).toHaveFocus();
|
|
130
|
+
expect(selo).toHaveAttribute("aria-expanded", "false");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("copiar só aparece em campo copiável e avisa o campo", async () => {
|
|
134
|
+
const usuario = userEvent.setup();
|
|
135
|
+
const aoCopiar = vi.fn();
|
|
136
|
+
renderComPainel({ onCopyField: aoCopiar });
|
|
137
|
+
|
|
138
|
+
await usuario.click(screen.getByRole("button", { name: /^Pré-aprovado/ }));
|
|
139
|
+
expect(screen.queryByRole("button", { name: "Copiar Situação cadastral" })).toBeNull();
|
|
140
|
+
|
|
141
|
+
await usuario.click(screen.getByRole("button", { name: "Copiar Nº do CPF" }));
|
|
142
|
+
expect(aoCopiar).toHaveBeenCalledWith(CONFERENCIA.fields[0]);
|
|
143
|
+
expect(screen.getByRole("button", { name: "Nº do CPF copiado" })).toBeInTheDocument();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("clique fora fecha", async () => {
|
|
147
|
+
const usuario = userEvent.setup();
|
|
148
|
+
renderComPainel();
|
|
149
|
+
await usuario.click(screen.getByRole("button", { name: /^Pré-aprovado/ }));
|
|
150
|
+
expect(await screen.findByRole("dialog")).toBeInTheDocument();
|
|
151
|
+
|
|
152
|
+
await usuario.click(document.body);
|
|
153
|
+
await waitFor(() => expect(screen.queryByRole("dialog")).toBeNull());
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("sem statusDetails o selo é só leitura", () => {
|
|
157
|
+
renderComPainel({ statusDetails: undefined });
|
|
158
|
+
expect(screen.queryByRole("button", { name: /^Pré-aprovado/ })).toBeNull();
|
|
159
|
+
expect(screen.getByText("Pré-aprovado")).toBeInTheDocument();
|
|
160
|
+
});
|
|
161
|
+
});
|