@1doc/1ds-react 2.8.0 → 2.10.0

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.
Files changed (287) hide show
  1. package/CHANGELOG.md +1540 -0
  2. package/dist/components/AILabel/AILabel.js +1 -1
  3. package/dist/components/Accordion/Accordion.css +13 -0
  4. package/dist/components/Accordion/Accordion.d.ts +9 -2
  5. package/dist/components/Accordion/Accordion.js +1 -0
  6. package/dist/components/AppSubheader/AppSubheader.css +10 -1
  7. package/dist/components/AttachmentCard/AttachmentCard.css +26 -8
  8. package/dist/components/AttachmentCard/AttachmentCard.d.ts +22 -2
  9. package/dist/components/AttachmentCard/AttachmentCard.js +47 -3
  10. package/dist/components/Avatar/Avatar.css +7 -2
  11. package/dist/components/Avatar/AvatarGroup.d.ts +17 -0
  12. package/dist/components/Avatar/AvatarGroup.js +22 -8
  13. package/dist/components/Badge/Badge.css +67 -2
  14. package/dist/components/Badge/Badge.d.ts +23 -4
  15. package/dist/components/Badge/Badge.js +4 -0
  16. package/dist/components/Box/Box.css +83 -0
  17. package/dist/components/Box/Box.d.ts +82 -2
  18. package/dist/components/Box/Box.js +42 -2
  19. package/dist/components/Button/Button.css +29 -0
  20. package/dist/components/Button/Button.d.ts +4 -4
  21. package/dist/components/Button/Button.js +4 -1
  22. package/dist/components/Calendar/Calendar.css +245 -0
  23. package/dist/components/Calendar/Calendar.d.ts +126 -0
  24. package/dist/components/Calendar/Calendar.js +320 -0
  25. package/dist/components/Chips/Chips.css +38 -0
  26. package/dist/components/Chips/Chips.d.ts +5 -2
  27. package/dist/components/Chips/Chips.js +2 -1
  28. package/dist/components/Coachmark/Coachmark.css +128 -0
  29. package/dist/components/Coachmark/Coachmark.d.ts +87 -0
  30. package/dist/components/Coachmark/Coachmark.js +362 -0
  31. package/dist/components/CodeBlock/CodeBlock.css +96 -0
  32. package/dist/components/CodeBlock/CodeBlock.d.ts +47 -0
  33. package/dist/components/CodeBlock/CodeBlock.js +103 -0
  34. package/dist/components/ColorPicker/ColorPicker.css +140 -0
  35. package/dist/components/ColorPicker/ColorPicker.d.ts +73 -0
  36. package/dist/components/ColorPicker/ColorPicker.js +219 -0
  37. package/dist/components/Combobox/Combobox.css +8 -0
  38. package/dist/components/DataTable/DataTable.css +254 -0
  39. package/dist/components/DataTable/DataTable.d.ts +81 -5
  40. package/dist/components/DataTable/DataTable.js +335 -19
  41. package/dist/components/DatePicker/DatePicker.css +8 -0
  42. package/dist/components/DatePicker/DatePicker.js +10 -1
  43. package/dist/components/DescriptionList/DescriptionList.css +127 -0
  44. package/dist/components/DescriptionList/DescriptionList.d.ts +62 -0
  45. package/dist/components/DescriptionList/DescriptionList.js +52 -0
  46. package/dist/components/EmptyState/EmptyState.d.ts +4 -2
  47. package/dist/components/EmptyState/EmptyState.js +2 -2
  48. package/dist/components/FileInput/FileInput.css +16 -0
  49. package/dist/components/FileInput/FileInput.d.ts +4 -2
  50. package/dist/components/FileInput/FileInput.js +8 -2
  51. package/dist/components/Grid/Grid.js +5 -5
  52. package/dist/components/IconPicker/IconPicker.css +107 -0
  53. package/dist/components/IconPicker/IconPicker.d.ts +74 -0
  54. package/dist/components/IconPicker/IconPicker.js +194 -0
  55. package/dist/components/Input/Input.css +59 -0
  56. package/dist/components/Input/Input.d.ts +17 -1
  57. package/dist/components/Input/Input.js +67 -5
  58. package/dist/components/Kanban/Kanban.css +4 -0
  59. package/dist/components/Kbd/Kbd.css +66 -0
  60. package/dist/components/Kbd/Kbd.d.ts +32 -0
  61. package/dist/components/Kbd/Kbd.js +40 -0
  62. package/dist/components/List/List.css +136 -0
  63. package/dist/components/List/List.d.ts +82 -0
  64. package/dist/components/List/List.js +53 -0
  65. package/dist/components/List/ListItem.d.ts +97 -0
  66. package/dist/components/List/ListItem.js +82 -0
  67. package/dist/components/Modal/Modal.css +80 -0
  68. package/dist/components/Modal/Modal.d.ts +33 -6
  69. package/dist/components/Modal/Modal.js +101 -6
  70. package/dist/components/MultiSelect/MultiSelect.css +8 -0
  71. package/dist/components/MultiSelect/MultiSelect.js +10 -1
  72. package/dist/components/OrgChart/OrgChart.css +149 -0
  73. package/dist/components/OrgChart/OrgChart.d.ts +83 -0
  74. package/dist/components/OrgChart/OrgChart.js +297 -0
  75. package/dist/components/OtpInput/OtpInput.css +93 -0
  76. package/dist/components/OtpInput/OtpInput.d.ts +60 -0
  77. package/dist/components/OtpInput/OtpInput.js +215 -0
  78. package/dist/components/PhoneInput/PhoneInput.css +8 -0
  79. package/dist/components/Popover/Popover.d.ts +7 -0
  80. package/dist/components/Popover/Popover.js +11 -1
  81. package/dist/components/RichTextEditor/RichTextEditor.css +250 -0
  82. package/dist/components/RichTextEditor/RichTextEditor.d.ts +97 -0
  83. package/dist/components/RichTextEditor/RichTextEditor.js +951 -0
  84. package/dist/components/SegmentedControl/SegmentedControl.css +24 -0
  85. package/dist/components/SegmentedControl/SegmentedControl.d.ts +2 -0
  86. package/dist/components/SegmentedControl/SegmentedControl.js +10 -2
  87. package/dist/components/Select/Select.css +164 -0
  88. package/dist/components/Select/Select.d.ts +34 -0
  89. package/dist/components/Select/Select.js +258 -28
  90. package/dist/components/Stack/Stack.css +49 -0
  91. package/dist/components/Stack/Stack.d.ts +65 -7
  92. package/dist/components/Stack/Stack.js +30 -2
  93. package/dist/components/StatCard/StatCard.css +138 -0
  94. package/dist/components/StatCard/StatCard.d.ts +26 -0
  95. package/dist/components/StatCard/StatCard.js +13 -1
  96. package/dist/components/Tabs/Tab.d.ts +15 -0
  97. package/dist/components/Tabs/Tab.js +30 -1
  98. package/dist/components/Tabs/Tabs.css +132 -0
  99. package/dist/components/Tabs/Tabs.d.ts +7 -0
  100. package/dist/components/Tabs/Tabs.js +13 -2
  101. package/dist/components/Tag/Tag.css +85 -2
  102. package/dist/components/Tag/Tag.d.ts +13 -6
  103. package/dist/components/Tag/Tag.js +1 -0
  104. package/dist/components/Text/Text.js +2 -2
  105. package/dist/components/Textarea/Textarea.css +8 -0
  106. package/dist/components/TimePicker/TimePicker.css +8 -0
  107. package/dist/components/Toast/Toast.css +32 -0
  108. package/dist/components/Toast/Toast.d.ts +18 -0
  109. package/dist/components/Toast/Toast.js +14 -1
  110. package/dist/components/Toast/ToastStack.js +8 -1
  111. package/dist/components/Tooltip/Tooltip.css +25 -0
  112. package/dist/components/Tooltip/Tooltip.d.ts +11 -0
  113. package/dist/components/Tooltip/Tooltip.js +18 -3
  114. package/dist/index.d.ts +25 -0
  115. package/dist/index.js +14 -0
  116. package/dist/teste/AILabel.test.d.ts +1 -0
  117. package/dist/teste/AILabel.test.js +33 -0
  118. package/dist/teste/AccordionConteudoLivre.test.d.ts +1 -0
  119. package/dist/teste/AccordionConteudoLivre.test.js +29 -0
  120. package/dist/teste/AccordionTituloNeutro.test.d.ts +1 -0
  121. package/dist/teste/AccordionTituloNeutro.test.js +23 -0
  122. package/dist/teste/AppSubheaderRegiao.test.d.ts +1 -0
  123. package/dist/teste/AppSubheaderRegiao.test.js +13 -0
  124. package/dist/teste/AtributosRepassados.test.d.ts +1 -0
  125. package/dist/teste/AtributosRepassados.test.js +52 -0
  126. package/dist/teste/AttachmentCardIA.test.d.ts +1 -0
  127. package/dist/teste/AttachmentCardIA.test.js +89 -0
  128. package/dist/teste/AttachmentCardLista.test.d.ts +1 -0
  129. package/dist/teste/AttachmentCardLista.test.js +49 -0
  130. package/dist/teste/AttachmentCardSm.test.d.ts +1 -0
  131. package/dist/teste/AttachmentCardSm.test.js +26 -0
  132. package/dist/teste/AvatarGroupOverlap.test.d.ts +1 -0
  133. package/dist/teste/AvatarGroupOverlap.test.js +56 -0
  134. package/dist/teste/BadgeNeutroColado.test.d.ts +1 -0
  135. package/dist/teste/BadgeNeutroColado.test.js +11 -0
  136. package/dist/teste/BadgeTag.test.d.ts +1 -0
  137. package/dist/teste/BadgeTag.test.js +55 -0
  138. package/dist/teste/BoxItemFlex.test.d.ts +1 -0
  139. package/dist/teste/BoxItemFlex.test.js +54 -0
  140. package/dist/teste/BoxLayout.test.d.ts +1 -0
  141. package/dist/teste/BoxLayout.test.js +35 -0
  142. package/dist/teste/Button.test.d.ts +1 -0
  143. package/dist/teste/Button.test.js +27 -0
  144. package/dist/teste/Calendar.test.d.ts +1 -0
  145. package/dist/teste/Calendar.test.js +221 -0
  146. package/dist/teste/CalendarCabecalho.test.d.ts +1 -0
  147. package/dist/teste/CalendarCabecalho.test.js +11 -0
  148. package/dist/teste/ChipsXs.test.d.ts +1 -0
  149. package/dist/teste/ChipsXs.test.js +31 -0
  150. package/dist/teste/Coachmark.test.d.ts +1 -0
  151. package/dist/teste/Coachmark.test.js +149 -0
  152. package/dist/teste/CoachmarkSemVeu.test.d.ts +1 -0
  153. package/dist/teste/CoachmarkSemVeu.test.js +85 -0
  154. package/dist/teste/ColorPicker.test.d.ts +1 -0
  155. package/dist/teste/ColorPicker.test.js +132 -0
  156. package/dist/teste/ColorPickerXs.test.d.ts +1 -0
  157. package/dist/teste/ColorPickerXs.test.js +21 -0
  158. package/dist/teste/DataTableArvoreEVariantes.test.d.ts +1 -0
  159. package/dist/teste/DataTableArvoreEVariantes.test.js +190 -0
  160. package/dist/teste/DataTableCelulaLivre.test.d.ts +1 -0
  161. package/dist/teste/DataTableCelulaLivre.test.js +76 -0
  162. package/dist/teste/DataTableDocumento.test.d.ts +1 -0
  163. package/dist/teste/DataTableDocumento.test.js +92 -0
  164. package/dist/teste/DatePicker.test.js +23 -0
  165. package/dist/teste/DescriptionListCompacta.test.d.ts +1 -0
  166. package/dist/teste/DescriptionListCompacta.test.js +22 -0
  167. package/dist/teste/EmptyStateTituloRico.test.d.ts +1 -0
  168. package/dist/teste/EmptyStateTituloRico.test.js +23 -0
  169. package/dist/teste/FileInputTrigger.test.d.ts +1 -0
  170. package/dist/teste/FileInputTrigger.test.js +53 -0
  171. package/dist/teste/IconPicker.test.d.ts +1 -0
  172. package/dist/teste/IconPicker.test.js +130 -0
  173. package/dist/teste/InputAgrupado.test.d.ts +1 -0
  174. package/dist/teste/InputAgrupado.test.js +24 -0
  175. package/dist/teste/InputMascaraEAfins.test.d.ts +1 -0
  176. package/dist/teste/InputMascaraEAfins.test.js +99 -0
  177. package/dist/teste/KbdCodeBlock.test.d.ts +1 -0
  178. package/dist/teste/KbdCodeBlock.test.js +95 -0
  179. package/dist/teste/List.test.d.ts +1 -0
  180. package/dist/teste/List.test.js +153 -0
  181. package/dist/teste/ListItemTooltipStandalone.test.d.ts +1 -0
  182. package/dist/teste/ListItemTooltipStandalone.test.js +39 -0
  183. package/dist/teste/ModalVariantes.test.d.ts +1 -0
  184. package/dist/teste/ModalVariantes.test.js +151 -0
  185. package/dist/teste/NomeAcessivelSsr.test.d.ts +1 -0
  186. package/dist/teste/NomeAcessivelSsr.test.js +88 -0
  187. package/dist/teste/OrgChart.test.d.ts +1 -0
  188. package/dist/teste/OrgChart.test.js +162 -0
  189. package/dist/teste/OtpInput.test.d.ts +1 -0
  190. package/dist/teste/OtpInput.test.js +143 -0
  191. package/dist/teste/PopoverMaxHeight.test.d.ts +1 -0
  192. package/dist/teste/PopoverMaxHeight.test.js +38 -0
  193. package/dist/teste/RichTextEditor.test.d.ts +1 -0
  194. package/dist/teste/RichTextEditor.test.js +287 -0
  195. package/dist/teste/RichTextEditorAlinhaImagemTabela.test.d.ts +1 -0
  196. package/dist/teste/RichTextEditorAlinhaImagemTabela.test.js +212 -0
  197. package/dist/teste/RichTextEditorEstiloDeTexto.test.d.ts +1 -0
  198. package/dist/teste/RichTextEditorEstiloDeTexto.test.js +120 -0
  199. package/dist/teste/SegmentedControlContador.test.d.ts +1 -0
  200. package/dist/teste/SegmentedControlContador.test.js +39 -0
  201. package/dist/teste/SelectAbertoControladoEFlutuante.test.d.ts +1 -0
  202. package/dist/teste/SelectAbertoControladoEFlutuante.test.js +145 -0
  203. package/dist/teste/SelectFiltroCorETamanho.test.d.ts +1 -0
  204. package/dist/teste/SelectFiltroCorETamanho.test.js +77 -0
  205. package/dist/teste/SelectPlacementFiltro.test.d.ts +1 -0
  206. package/dist/teste/SelectPlacementFiltro.test.js +88 -0
  207. package/dist/teste/Stack.test.d.ts +1 -0
  208. package/dist/teste/Stack.test.js +57 -0
  209. package/dist/teste/StatCardRotuloTopo.test.d.ts +1 -0
  210. package/dist/teste/StatCardRotuloTopo.test.js +34 -0
  211. package/dist/teste/StatCardSecundariaETom.test.d.ts +1 -0
  212. package/dist/teste/StatCardSecundariaETom.test.js +45 -0
  213. package/dist/teste/TabsContadorFechavel.test.d.ts +1 -0
  214. package/dist/teste/TabsContadorFechavel.test.js +79 -0
  215. package/dist/teste/ToastAcaoEDetalhes.test.d.ts +1 -0
  216. package/dist/teste/ToastAcaoEDetalhes.test.js +94 -0
  217. package/dist/teste/TooltipDark.test.d.ts +1 -0
  218. package/dist/teste/TooltipDark.test.js +31 -0
  219. package/dist/utils/calendario.d.ts +28 -0
  220. package/dist/utils/calendario.js +73 -0
  221. package/dist/utils/html-seguro.d.ts +45 -0
  222. package/dist/utils/html-seguro.js +208 -0
  223. package/dist/utils/paleta-de-cores.d.ts +26 -0
  224. package/dist/utils/paleta-de-cores.js +10 -0
  225. package/dist/utils/texto.d.ts +5 -0
  226. package/dist/utils/texto.js +5 -0
  227. package/dist/utils/toast.d.ts +10 -1
  228. package/dist/utils/toast.js +2 -1
  229. package/package.json +3 -3
  230. package/src/index.ts +26 -0
  231. package/src/teste/AILabel.test.tsx +41 -0
  232. package/src/teste/AccordionConteudoLivre.test.tsx +39 -0
  233. package/src/teste/AccordionTituloNeutro.test.tsx +25 -0
  234. package/src/teste/AppSubheaderRegiao.test.tsx +17 -0
  235. package/src/teste/AtributosRepassados.test.tsx +73 -0
  236. package/src/teste/AttachmentCardIA.test.tsx +126 -0
  237. package/src/teste/AttachmentCardLista.test.tsx +53 -0
  238. package/src/teste/AttachmentCardSm.test.tsx +29 -0
  239. package/src/teste/AvatarGroupOverlap.test.tsx +74 -0
  240. package/src/teste/BadgeNeutroColado.test.tsx +15 -0
  241. package/src/teste/BadgeTag.test.tsx +85 -0
  242. package/src/teste/BoxItemFlex.test.tsx +81 -0
  243. package/src/teste/BoxLayout.test.tsx +50 -0
  244. package/src/teste/Button.test.tsx +39 -0
  245. package/src/teste/Calendar.test.tsx +282 -0
  246. package/src/teste/CalendarCabecalho.test.tsx +11 -0
  247. package/src/teste/ChipsXs.test.tsx +41 -0
  248. package/src/teste/Coachmark.test.tsx +198 -0
  249. package/src/teste/CoachmarkSemVeu.test.tsx +101 -0
  250. package/src/teste/ColorPicker.test.tsx +153 -0
  251. package/src/teste/ColorPickerXs.test.tsx +22 -0
  252. package/src/teste/DataTableArvoreEVariantes.test.tsx +240 -0
  253. package/src/teste/DataTableCelulaLivre.test.tsx +103 -0
  254. package/src/teste/DataTableDocumento.test.tsx +102 -0
  255. package/src/teste/DatePicker.test.tsx +38 -0
  256. package/src/teste/DescriptionListCompacta.test.tsx +24 -0
  257. package/src/teste/EmptyStateTituloRico.test.tsx +34 -0
  258. package/src/teste/FileInputTrigger.test.tsx +89 -0
  259. package/src/teste/IconPicker.test.tsx +145 -0
  260. package/src/teste/InputAgrupado.test.tsx +26 -0
  261. package/src/teste/InputMascaraEAfins.test.tsx +124 -0
  262. package/src/teste/KbdCodeBlock.test.tsx +106 -0
  263. package/src/teste/List.test.tsx +269 -0
  264. package/src/teste/ListItemTooltipStandalone.test.tsx +64 -0
  265. package/src/teste/ModalVariantes.test.tsx +173 -0
  266. package/src/teste/NomeAcessivelSsr.test.tsx +110 -0
  267. package/src/teste/OrgChart.test.tsx +183 -0
  268. package/src/teste/OtpInput.test.tsx +163 -0
  269. package/src/teste/PopoverMaxHeight.test.tsx +47 -0
  270. package/src/teste/RichTextEditor.test.tsx +329 -0
  271. package/src/teste/RichTextEditorAlinhaImagemTabela.test.tsx +238 -0
  272. package/src/teste/RichTextEditorEstiloDeTexto.test.tsx +137 -0
  273. package/src/teste/SegmentedControlContador.test.tsx +43 -0
  274. package/src/teste/SelectAbertoControladoEFlutuante.test.tsx +180 -0
  275. package/src/teste/SelectFiltroCorETamanho.test.tsx +90 -0
  276. package/src/teste/SelectPlacementFiltro.test.tsx +110 -0
  277. package/src/teste/Stack.test.tsx +80 -0
  278. package/src/teste/StatCardRotuloTopo.test.tsx +44 -0
  279. package/src/teste/StatCardSecundariaETom.test.tsx +51 -0
  280. package/src/teste/TabsContadorFechavel.test.tsx +144 -0
  281. package/src/teste/ToastAcaoEDetalhes.test.tsx +109 -0
  282. package/src/teste/TooltipDark.test.tsx +51 -0
  283. package/src/utils/calendario.ts +78 -0
  284. package/src/utils/html-seguro.ts +197 -0
  285. package/src/utils/paleta-de-cores.ts +34 -0
  286. package/src/utils/texto.ts +5 -0
  287. package/src/utils/toast.ts +12 -2
@@ -0,0 +1,240 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { render, screen, fireEvent } from "@testing-library/react";
3
+ import { faBoxOpen } from "@fortawesome/free-solid-svg-icons";
4
+ import DataTable from "../components/DataTable/DataTable";
5
+
6
+ const COLUNAS = [
7
+ { key: "nome", label: "Nome" },
8
+ { key: "sigla", label: "Sigla" },
9
+ ];
10
+ const DADOS = [
11
+ { id: "a", nome: "Ana", sigla: "SADM" },
12
+ { id: "b", nome: "Bruno", sigla: "SOBR" },
13
+ { id: "c", nome: "Carla", sigla: "SEFIN" },
14
+ ];
15
+
16
+ describe("DataTable: cabeçalho, zebra e destaque", () => {
17
+ it("headerVariant dark e headerUppercase ligam as classes; sem as props, nada muda", () => {
18
+ const { container, rerender } = render(<DataTable columns={COLUNAS} data={DADOS} />);
19
+ const raiz = () => container.querySelector(".umds-datatable") as HTMLElement;
20
+ expect(raiz().className).not.toMatch(/header-dark|header-uppercase|striped|header-hidden/);
21
+ rerender(<DataTable columns={COLUNAS} data={DADOS} headerVariant="dark" headerUppercase striped hideHeader />);
22
+ expect(raiz()).toHaveClass("umds-datatable--header-dark");
23
+ expect(raiz()).toHaveClass("umds-datatable--header-uppercase");
24
+ expect(raiz()).toHaveClass("umds-datatable--striped");
25
+ expect(raiz()).toHaveClass("umds-datatable--header-hidden");
26
+ });
27
+
28
+ it("hideHeader não remove o cabeçalho: as colunas continuam nomeadas", () => {
29
+ render(<DataTable columns={COLUNAS} data={DADOS} hideHeader />);
30
+ expect(screen.getAllByRole("columnheader")).toHaveLength(2);
31
+ });
32
+
33
+ it("striped marca as linhas ímpares", () => {
34
+ const { container } = render(<DataTable columns={COLUNAS} data={DADOS} striped />);
35
+ const linhas = [...container.querySelectorAll("tbody tr")];
36
+ expect(linhas.map((l) => l.classList.contains("umds-datatable__row--striped"))).toEqual([false, true, false]);
37
+ });
38
+
39
+ it("selectedRowKeys destaca a linha sem checkbox e marca aria-selected", () => {
40
+ const { container } = render(<DataTable columns={COLUNAS} data={DADOS} rowKey="id" selectedRowKeys={["b"]} />);
41
+ const linhas = [...container.querySelectorAll("tbody tr")];
42
+ expect(linhas[1]).toHaveClass("umds-datatable__row--selected");
43
+ expect(linhas[1]).toHaveClass("umds-datatable__row--highlighted");
44
+ expect(linhas[1]).toHaveAttribute("aria-selected", "true");
45
+ expect(linhas[0]).not.toHaveClass("umds-datatable__row--selected");
46
+ expect(linhas[0]).not.toHaveAttribute("aria-selected");
47
+ expect(screen.queryByRole("checkbox")).toBeNull();
48
+ });
49
+
50
+ it("isRowSelected destaca por função", () => {
51
+ const { container } = render(
52
+ <DataTable columns={COLUNAS} data={DADOS} isRowSelected={(row) => row.sigla === "SEFIN"} />
53
+ );
54
+ const linhas = [...container.querySelectorAll("tbody tr")];
55
+ expect(linhas[2]).toHaveClass("umds-datatable__row--selected");
56
+ expect(linhas[0]).not.toHaveClass("umds-datatable__row--selected");
57
+ });
58
+
59
+ it("emptyIcon e emptyDescription entram no estado vazio", () => {
60
+ const { container } = render(
61
+ <DataTable
62
+ columns={COLUNAS}
63
+ data={[]}
64
+ emptyMessage="Nada por aqui"
65
+ emptyDescription="Ajuste o filtro de setor."
66
+ emptyIcon={faBoxOpen}
67
+ />
68
+ );
69
+ expect(screen.getByText("Nada por aqui")).toBeInTheDocument();
70
+ expect(screen.getByText("Ajuste o filtro de setor.")).toBeInTheDocument();
71
+ expect(container.querySelector(".umds-datatable__empty-icon svg")).toHaveAttribute("data-icon", "box-open");
72
+ });
73
+
74
+ it("o ícone padrão do estado vazio continua o inbox", () => {
75
+ const { container } = render(<DataTable columns={COLUNAS} data={[]} />);
76
+ expect(container.querySelector(".umds-datatable__empty-icon svg")).toHaveAttribute("data-icon", "inbox");
77
+ expect(container.querySelector(".umds-datatable__empty-desc")).toBeNull();
78
+ });
79
+
80
+ it("compact na tabela de documento soma as duas classes", () => {
81
+ const { container } = render(<DataTable variant="document" density="compact" columns={COLUNAS} data={DADOS} />);
82
+ const raiz = container.querySelector(".umds-datatable") as HTMLElement;
83
+ expect(raiz).toHaveClass("umds-datatable--document");
84
+ expect(raiz).toHaveClass("umds-datatable--compact");
85
+ });
86
+ });
87
+
88
+ const ARVORE = [
89
+ {
90
+ id: "gab",
91
+ nome: "Gabinete",
92
+ sigla: "GAB",
93
+ children: [
94
+ { id: "sadm", nome: "Administração", sigla: "SADM", children: [{ id: "rh", nome: "Recursos Humanos", sigla: "RH" }] },
95
+ { id: "sobr", nome: "Obras", sigla: "SOBR" },
96
+ ],
97
+ },
98
+ { id: "sefin", nome: "Finanças", sigla: "SEFIN" },
99
+ ];
100
+
101
+ const linhas = (c: HTMLElement) => [...c.querySelectorAll("tbody tr")] as HTMLElement[];
102
+ const nomes = (c: HTMLElement) => linhas(c).map((l) => l.querySelectorAll("td")[0].textContent);
103
+
104
+ describe("DataTable treeData", () => {
105
+ it("vira treegrid; fechada, mostra só as raízes, com aria-level, posinset, setsize e expanded", () => {
106
+ const { container } = render(<DataTable columns={COLUNAS} data={ARVORE} treeData rowKey="id" />);
107
+ expect(screen.getByRole("treegrid")).toBeInTheDocument();
108
+ expect(nomes(container)).toEqual(["Gabinete", "Finanças"]);
109
+ const [gab, fin] = linhas(container);
110
+ expect(gab).toHaveAttribute("aria-level", "1");
111
+ expect(gab).toHaveAttribute("aria-posinset", "1");
112
+ expect(gab).toHaveAttribute("aria-setsize", "2");
113
+ expect(gab).toHaveAttribute("aria-expanded", "false");
114
+ expect(fin).not.toHaveAttribute("aria-expanded");
115
+ });
116
+
117
+ it("o botão expande e recolhe, com nome acessível e onExpand", () => {
118
+ const aoExpandir = vi.fn();
119
+ const { container } = render(<DataTable columns={COLUNAS} data={ARVORE} treeData rowKey="id" onExpand={aoExpandir} />);
120
+ fireEvent.click(screen.getByRole("button", { name: "Expandir" }));
121
+ expect(nomes(container)).toEqual(["Gabinete", "Administração", "Obras", "Finanças"]);
122
+ expect(aoExpandir).toHaveBeenCalledWith(["gab"], ARVORE[0], true);
123
+ const filha = linhas(container)[1];
124
+ expect(filha).toHaveAttribute("aria-level", "2");
125
+ expect(filha).toHaveAttribute("aria-expanded", "false");
126
+ fireEvent.click(screen.getAllByRole("button", { name: "Recolher" })[0]);
127
+ expect(nomes(container)).toEqual(["Gabinete", "Finanças"]);
128
+ expect(aoExpandir).toHaveBeenLastCalledWith([], ARVORE[0], false);
129
+ });
130
+
131
+ it("expandLabel e collapseLabel traduzem os botões", () => {
132
+ render(
133
+ <DataTable
134
+ columns={COLUNAS}
135
+ data={ARVORE}
136
+ treeData
137
+ rowKey="id"
138
+ defaultExpandedKeys={["gab"]}
139
+ expandLabel="Expand"
140
+ collapseLabel="Collapse"
141
+ />
142
+ );
143
+ expect(screen.getByRole("button", { name: "Collapse" })).toBeInTheDocument();
144
+ expect(screen.getByRole("button", { name: "Expand" })).toBeInTheDocument();
145
+ });
146
+
147
+ it("defaultExpandAll abre tudo, e o primeiro recolher parte da árvore aberta", () => {
148
+ const { container } = render(<DataTable columns={COLUNAS} data={ARVORE} treeData rowKey="id" defaultExpandAll />);
149
+ expect(nomes(container)).toEqual(["Gabinete", "Administração", "Recursos Humanos", "Obras", "Finanças"]);
150
+ expect(linhas(container)[2]).toHaveAttribute("aria-level", "3");
151
+ fireEvent.click(screen.getAllByRole("button", { name: "Recolher" })[1]);
152
+ expect(nomes(container)).toEqual(["Gabinete", "Administração", "Obras", "Finanças"]);
153
+ });
154
+
155
+ it("expandedKeys controla: o clique avisa, mas só muda quando o pai muda a prop", () => {
156
+ const aoExpandir = vi.fn();
157
+ const { container, rerender } = render(
158
+ <DataTable columns={COLUNAS} data={ARVORE} treeData rowKey="id" expandedKeys={[]} onExpand={aoExpandir} />
159
+ );
160
+ fireEvent.click(screen.getByRole("button", { name: "Expandir" }));
161
+ expect(aoExpandir).toHaveBeenCalledWith(["gab"], ARVORE[0], true);
162
+ expect(nomes(container)).toEqual(["Gabinete", "Finanças"]);
163
+ rerender(<DataTable columns={COLUNAS} data={ARVORE} treeData rowKey="id" expandedKeys={["gab"]} onExpand={aoExpandir} />);
164
+ expect(nomes(container)).toEqual(["Gabinete", "Administração", "Obras", "Finanças"]);
165
+ });
166
+
167
+ it("sem rowKey, a chave é o caminho de índices", () => {
168
+ const aoExpandir = vi.fn();
169
+ render(<DataTable columns={COLUNAS} data={ARVORE} treeData onExpand={aoExpandir} />);
170
+ fireEvent.click(screen.getByRole("button", { name: "Expandir" }));
171
+ expect(aoExpandir.mock.calls[0][0]).toEqual(["0"]);
172
+ });
173
+
174
+ it("desenha os fios: tee para quem tem irmã depois, elbow para a última, line para o que continua", () => {
175
+ const { container } = render(<DataTable columns={COLUNAS} data={ARVORE} treeData rowKey="id" defaultExpandAll />);
176
+ const guias = (l: HTMLElement) => [...l.querySelectorAll(".umds-datatable__guide")].map((g) => g.className.replace("umds-datatable__guide umds-datatable__guide--", ""));
177
+ const [gab, sadm, rh, sobr] = linhas(container);
178
+ expect(guias(gab)).toEqual([]);
179
+ expect(guias(sadm)).toEqual(["tee"]);
180
+ expect(guias(rh)).toEqual(["line", "elbow"]);
181
+ expect(guias(sobr)).toEqual(["elbow"]);
182
+ });
183
+
184
+ it("o recuo cresce por nível", () => {
185
+ const { container } = render(<DataTable columns={COLUNAS} data={ARVORE} treeData rowKey="id" defaultExpandAll />);
186
+ const recuo = (l: HTMLElement) => (l.querySelector(".umds-datatable__tree-content") as HTMLElement).style.paddingLeft;
187
+ expect(recuo(linhas(container)[0])).toContain("calc(0 *");
188
+ expect(recuo(linhas(container)[2])).toContain("calc(2 *");
189
+ });
190
+
191
+ it("teclado: ↓ ↑ andam, → expande e desce, ← recolhe e sobe, Home e End", () => {
192
+ const { container } = render(<DataTable columns={COLUNAS} data={ARVORE} treeData rowKey="id" />);
193
+ const gab = linhas(container)[0];
194
+ gab.focus();
195
+ expect(gab).toHaveAttribute("tabindex", "0");
196
+ expect(linhas(container)[1]).toHaveAttribute("tabindex", "-1");
197
+
198
+ fireEvent.keyDown(gab, { key: "ArrowRight" });
199
+ expect(nomes(container)).toEqual(["Gabinete", "Administração", "Obras", "Finanças"]);
200
+ expect(document.activeElement).toBe(gab);
201
+
202
+ fireEvent.keyDown(gab, { key: "ArrowRight" });
203
+ expect(document.activeElement).toBe(linhas(container)[1]);
204
+
205
+ fireEvent.keyDown(document.activeElement as Element, { key: "ArrowDown" });
206
+ expect(document.activeElement).toBe(linhas(container)[2]);
207
+ expect(linhas(container)[2]).toHaveAttribute("tabindex", "0");
208
+
209
+ fireEvent.keyDown(document.activeElement as Element, { key: "ArrowLeft" });
210
+ expect(document.activeElement).toBe(linhas(container)[0]);
211
+
212
+ fireEvent.keyDown(document.activeElement as Element, { key: "End" });
213
+ expect(document.activeElement).toBe(linhas(container)[3]);
214
+ fireEvent.keyDown(document.activeElement as Element, { key: "Home" });
215
+ expect(document.activeElement).toBe(linhas(container)[0]);
216
+
217
+ fireEvent.keyDown(document.activeElement as Element, { key: "ArrowLeft" });
218
+ expect(nomes(container)).toEqual(["Gabinete", "Finanças"]);
219
+ });
220
+
221
+ it("clicar no botão não aciona o onRowClick da linha", () => {
222
+ const aoClicar = vi.fn();
223
+ render(<DataTable columns={COLUNAS} data={ARVORE} treeData rowKey="id" onRowClick={aoClicar} />);
224
+ fireEvent.click(screen.getByRole("button", { name: "Expandir" }));
225
+ expect(aoClicar).not.toHaveBeenCalled();
226
+ });
227
+
228
+ it("sem treeData, a tabela é a de sempre: sem role, sem aria-level", () => {
229
+ const { container } = render(<DataTable columns={COLUNAS} data={ARVORE} />);
230
+ expect(screen.queryByRole("treegrid")).toBeNull();
231
+ expect(container.querySelector("[aria-level]")).toBeNull();
232
+ expect(container.querySelector(".umds-datatable__tree-toggle")).toBeNull();
233
+ });
234
+
235
+ it("childrenKey troca o campo das filhas", () => {
236
+ const dados = [{ id: 1, nome: "Pai", sigla: "P", itens: [{ id: 2, nome: "Filho", sigla: "F" }] }];
237
+ const { container } = render(<DataTable columns={COLUNAS} data={dados} treeData childrenKey="itens" rowKey="id" defaultExpandAll />);
238
+ expect(nomes(container)).toEqual(["Pai", "Filho"]);
239
+ });
240
+ });
@@ -0,0 +1,103 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { render, screen, within } from "@testing-library/react";
3
+ import DataTable, { type DataTableColumn } from "../components/DataTable/DataTable";
4
+
5
+ /**
6
+ * Célula livre (`render` devolvendo nó) e linha de grupo (`rowGroup`).
7
+ * No Vue o equivalente é o slot `#cell-<key>`: ver `datatable-slots.test.mjs`.
8
+ */
9
+
10
+ const USUARIOS = [
11
+ { id: 1, nome: "Lucas Andrade", setor: "SADM", perfil: "Administrador" },
12
+ { id: 2, nome: "Marina Souza", setor: "SADM", perfil: "Nível 1" },
13
+ { id: 3, nome: "Rafael Pereira", setor: "SOBR", perfil: "Nível 2" },
14
+ ];
15
+
16
+ describe("DataTable · célula livre", () => {
17
+ it("render devolvendo elemento desenha o nó, não texto", () => {
18
+ const colunas: DataTableColumn[] = [
19
+ { key: "nome", label: "Usuário", render: (row) => <strong data-testid="nome">{row.nome}</strong> },
20
+ { key: "perfil", label: "Perfil" },
21
+ ];
22
+ render(<DataTable columns={colunas} data={USUARIOS} />);
23
+
24
+ const nos = screen.getAllByTestId("nome");
25
+ expect(nos).toHaveLength(3);
26
+ expect(nos[0].tagName).toBe("STRONG");
27
+ expect(nos[0]).toHaveTextContent("Lucas Andrade");
28
+ });
29
+
30
+ it("render recebe a linha e o índice", () => {
31
+ const colunas: DataTableColumn[] = [
32
+ { key: "nome", label: "Usuário", render: (row, i) => `${i + 1}. ${row.nome}` },
33
+ ];
34
+ render(<DataTable columns={colunas} data={USUARIOS} />);
35
+ expect(screen.getByText("2. Marina Souza")).toBeInTheDocument();
36
+ });
37
+
38
+ it("DataTableColumn é exportado e tipa a coluna com render de nó", () => {
39
+ const coluna: DataTableColumn = {
40
+ key: "acoes",
41
+ label: "Ações",
42
+ render: () => <button type="button">Editar</button>,
43
+ };
44
+ render(<DataTable columns={[coluna]} data={USUARIOS} />);
45
+ expect(screen.getAllByRole("button", { name: "Editar" })).toHaveLength(3);
46
+ });
47
+ });
48
+
49
+ describe("DataTable · linha de grupo (rowGroup)", () => {
50
+ const colunas: DataTableColumn[] = [
51
+ { key: "nome", label: "Usuário" },
52
+ { key: "perfil", label: "Perfil" },
53
+ ];
54
+
55
+ it("sem rowGroup não desenha linha de grupo", () => {
56
+ const { container } = render(<DataTable columns={colunas} data={USUARIOS} />);
57
+ expect(container.querySelector("th[scope='rowgroup']")).toBeNull();
58
+ });
59
+
60
+ it("rowGroup por campo abre um grupo por valor consecutivo", () => {
61
+ const { container } = render(<DataTable columns={colunas} data={USUARIOS} rowGroup="setor" />);
62
+ const grupos = container.querySelectorAll("th[scope='rowgroup']");
63
+ expect(Array.from(grupos).map((g) => g.textContent)).toEqual(["SADM", "SOBR"]);
64
+ // a linha de grupo atravessa as colunas todas
65
+ expect(grupos[0]).toHaveAttribute("colspan", "2");
66
+ });
67
+
68
+ it("rowGroup por função e colspan com selectable", () => {
69
+ const { container } = render(
70
+ <DataTable
71
+ columns={colunas}
72
+ data={USUARIOS}
73
+ selectable
74
+ rowGroup={(row) => (row.id === 3 ? undefined : "Equipe A")}
75
+ />,
76
+ );
77
+ const grupos = container.querySelectorAll("th[scope='rowgroup']");
78
+ expect(grupos).toHaveLength(1);
79
+ expect(grupos[0]).toHaveTextContent("Equipe A");
80
+ expect(grupos[0]).toHaveAttribute("colspan", "3");
81
+ });
82
+
83
+ it("renderRowGroup devolve nó", () => {
84
+ render(
85
+ <DataTable
86
+ columns={colunas}
87
+ data={USUARIOS}
88
+ rowGroup="setor"
89
+ renderRowGroup={(rotulo) => <em data-testid="grupo">{rotulo}!</em>}
90
+ />,
91
+ );
92
+ expect(screen.getAllByTestId("grupo").map((n) => n.textContent)).toEqual(["SADM!", "SOBR!"]);
93
+ });
94
+
95
+ it("a linha de grupo não entra na seleção por índice", async () => {
96
+ const { container } = render(
97
+ <DataTable columns={colunas} data={USUARIOS} rowGroup="setor" selectable selectedRows={[2]} />,
98
+ );
99
+ const selecionadas = container.querySelectorAll(".umds-datatable__row--selected");
100
+ expect(selecionadas).toHaveLength(1);
101
+ expect(within(selecionadas[0] as HTMLElement).getByText("Rafael Pereira")).toBeInTheDocument();
102
+ });
103
+ });
@@ -0,0 +1,102 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+ import DataTable from "../components/DataTable/DataTable";
5
+ import DescriptionList from "../components/DescriptionList/DescriptionList";
6
+
7
+ const COLUNAS = [
8
+ { key: "descricao", label: "Descrição", sortable: true },
9
+ { key: "quantidade", label: "Quantidade", align: "right" as const },
10
+ ];
11
+ const DADOS = [
12
+ { descricao: "Diesel S10", quantidade: "5.760" },
13
+ { descricao: "Gasolina comum", quantidade: "1.200" },
14
+ ];
15
+
16
+ describe("DataTable variant=document", () => {
17
+ it("é a tabela de documento: classe própria, cabeçalho e linhas", () => {
18
+ const { container } = render(<DataTable variant="document" columns={COLUNAS} data={DADOS} />);
19
+ expect(container.querySelector(".umds-datatable--document")).not.toBeNull();
20
+ expect(screen.getAllByRole("columnheader")).toHaveLength(2);
21
+ expect(screen.getByText("Diesel S10")).toBeInTheDocument();
22
+ });
23
+
24
+ it("não ordena: sem botão de ordenar, sem aria-sort", () => {
25
+ render(<DataTable variant="document" columns={COLUNAS} data={DADOS} sortKey="descricao" sortDirection="asc" />);
26
+ expect(screen.queryByRole("button")).toBeNull();
27
+ for (const th of screen.getAllByRole("columnheader")) expect(th).not.toHaveAttribute("aria-sort");
28
+ });
29
+
30
+ it("não seleciona: selectable é ignorado", () => {
31
+ render(<DataTable variant="document" selectable columns={COLUNAS} data={DADOS} />);
32
+ expect(screen.queryByRole("checkbox")).toBeNull();
33
+ expect(screen.getAllByRole("columnheader")).toHaveLength(2);
34
+ });
35
+
36
+ it("linha não é clicável nem entra na ordem de Tab", async () => {
37
+ const usuario = userEvent.setup();
38
+ const aoClicar = vi.fn();
39
+ const { container } = render(<DataTable variant="document" columns={COLUNAS} data={DADOS} onRowClick={aoClicar} />);
40
+ const linha = container.querySelector("tbody tr") as HTMLElement;
41
+ expect(linha).not.toHaveAttribute("tabindex");
42
+ expect(linha).not.toHaveClass("umds-datatable__row--clickable");
43
+ await usuario.click(linha);
44
+ expect(aoClicar).not.toHaveBeenCalled();
45
+ });
46
+
47
+ it("a tabela de interface continua igual (ordena, seleciona, clica)", async () => {
48
+ const usuario = userEvent.setup();
49
+ const aoOrdenar = vi.fn();
50
+ render(<DataTable columns={COLUNAS} data={DADOS} selectable onSort={aoOrdenar} />);
51
+ expect(screen.getAllByRole("checkbox").length).toBeGreaterThan(0);
52
+ await usuario.click(screen.getByRole("button", { name: /Descrição/ }));
53
+ expect(aoOrdenar).toHaveBeenCalledWith("descricao", "asc");
54
+ });
55
+ });
56
+
57
+ describe("DescriptionList", () => {
58
+ const ITENS = [
59
+ { label: "Dotação", value: "02.031.0001" },
60
+ { label: "Empenho", value: "2026NE000184" },
61
+ { label: "Fonte", value: "" },
62
+ ];
63
+
64
+ it("é um dl com um dt e um dd por par", () => {
65
+ const { container } = render(<DescriptionList items={ITENS} ariaLabel="Dados de empenho" />);
66
+ const dl = container.querySelector("dl") as HTMLElement;
67
+ expect(dl).toHaveAttribute("aria-label", "Dados de empenho");
68
+ expect(dl.querySelectorAll("dt")).toHaveLength(3);
69
+ expect(dl.querySelectorAll("dd")).toHaveLength(3);
70
+ expect(dl.querySelector("dt")).toHaveTextContent("Dotação");
71
+ expect(dl.querySelector("dd")).toHaveTextContent("02.031.0001");
72
+ });
73
+
74
+ it("valor vazio mostra o traço, ou o texto de emptyValue", () => {
75
+ const { container, rerender } = render(<DescriptionList items={ITENS} />);
76
+ expect(container.querySelectorAll("dd")[2]).toHaveTextContent("—");
77
+ rerender(<DescriptionList items={ITENS} emptyValue="Não informado" />);
78
+ expect(container.querySelectorAll("dd")[2]).toHaveTextContent("Não informado");
79
+ });
80
+
81
+ it("inline é o padrão; fit encolhe", () => {
82
+ const { container, rerender } = render(<DescriptionList items={ITENS} />);
83
+ const dl = container.querySelector("dl") as HTMLElement;
84
+ expect(dl).toHaveClass("umds-description-list--inline");
85
+ expect(dl).not.toHaveClass("umds-description-list--fit");
86
+ rerender(<DescriptionList items={ITENS} fit />);
87
+ expect(container.querySelector("dl")).toHaveClass("umds-description-list--fit");
88
+ });
89
+
90
+ it("stacked ganha as colunas e a caixa do valor", () => {
91
+ const { container } = render(<DescriptionList items={ITENS} layout="stacked" columns={3} bordered />);
92
+ const dl = container.querySelector("dl") as HTMLElement;
93
+ expect(dl).toHaveClass("umds-description-list--stacked");
94
+ expect(dl).toHaveClass("umds-description-list--cols-3");
95
+ expect(dl).toHaveClass("umds-description-list--bordered");
96
+ });
97
+
98
+ it("sem itens, não desenha par nenhum", () => {
99
+ const { container } = render(<DescriptionList />);
100
+ expect(container.querySelectorAll("dt")).toHaveLength(0);
101
+ });
102
+ });
@@ -84,3 +84,41 @@ describe("DatePicker · teclado", () => {
84
84
  expect(screen.getByRole("combobox")).toHaveTextContent(/15\/\d{2}\/\d{4}/);
85
85
  });
86
86
  });
87
+
88
+ /**
89
+ * A story "Com valor" mostrava "Selecione o período" com `value="2025-12-31"`.
90
+ * Não era a story: no modo single o componente só lia `range`, e `value` e
91
+ * `defaultValue` eram declarados e nunca chegavam ao campo.
92
+ */
93
+ describe("DatePicker · valor no modo single", () => {
94
+ it("`value` aparece no campo, no formato pedido", () => {
95
+ render(
96
+ <DatePicker label="Vencimento" value="2025-12-31" format="DD/MM/YYYY" />,
97
+ );
98
+ expect(screen.getByRole("combobox")).toHaveTextContent("31/12/2025");
99
+ });
100
+
101
+ it("`defaultValue` semeia o campo no modo não-controlado", () => {
102
+ render(<DatePicker label="Vencimento" defaultValue="2025-06-15" />);
103
+ expect(screen.getByRole("combobox")).toHaveTextContent("15/06/2025");
104
+ });
105
+
106
+ it("`value` vazio no modo controlado mostra o placeholder", () => {
107
+ render(<DatePicker label="Vencimento" value="" placeholder="DD/MM/AAAA" />);
108
+ expect(screen.getByRole("combobox")).toHaveTextContent("DD/MM/AAAA");
109
+ });
110
+
111
+ it("o modo range continua lendo `range`, não `value`", () => {
112
+ render(
113
+ <DatePicker
114
+ mode="range"
115
+ label="Período"
116
+ value="2025-01-01"
117
+ range={{ from: "2025-03-01", to: "2025-03-10" }}
118
+ />,
119
+ );
120
+ expect(screen.getByRole("combobox")).toHaveTextContent(
121
+ "01/03/2025 – 10/03/2025",
122
+ );
123
+ });
124
+ });
@@ -0,0 +1,24 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { render } from "@testing-library/react";
3
+ import DescriptionList from "../components/DescriptionList/DescriptionList";
4
+
5
+ const ITENS = [{ label: "Dotação", value: "02.031.0001" }];
6
+
7
+ describe("DescriptionList · density", () => {
8
+ it("compact escreve a classe; o padrão não", () => {
9
+ const { container, rerender } = render(<DescriptionList items={ITENS} density="compact" />);
10
+ const dl = () => container.querySelector("dl") as HTMLElement;
11
+ expect(dl()).toHaveClass("umds-description-list--compact");
12
+ rerender(<DescriptionList items={ITENS} density="default" />);
13
+ expect(dl()).not.toHaveClass("umds-description-list--compact");
14
+ rerender(<DescriptionList items={ITENS} />);
15
+ expect(dl()).not.toHaveClass("umds-description-list--compact");
16
+ });
17
+
18
+ it("compact vale nos dois desenhos", () => {
19
+ const { container, rerender } = render(<DescriptionList items={ITENS} density="compact" layout="stacked" bordered />);
20
+ expect(container.querySelector("dl")).toHaveClass("umds-description-list--stacked", "umds-description-list--compact");
21
+ rerender(<DescriptionList items={ITENS} density="compact" fit />);
22
+ expect(container.querySelector("dl")).toHaveClass("umds-description-list--inline", "umds-description-list--compact");
23
+ });
24
+ });
@@ -0,0 +1,34 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { faInbox } from "@fortawesome/free-solid-svg-icons";
4
+ import EmptyState from "../components/EmptyState/EmptyState";
5
+
6
+ describe("EmptyState · título rico", () => {
7
+ it("título em texto continua igual", () => {
8
+ const { container } = render(<EmptyState icon={faInbox} title="Nenhuma mensagem" description="Tudo em dia." />);
9
+ expect(container.querySelector(".umds-empty-state__title")).toHaveTextContent("Nenhuma mensagem");
10
+ expect(screen.getByText("Tudo em dia.")).toBeInTheDocument();
11
+ });
12
+
13
+ it("título aceita conteúdo rico: negrito, link", () => {
14
+ const { container } = render(
15
+ <EmptyState
16
+ title={
17
+ <>
18
+ Nenhum processo para <strong data-testid="termo">SOBR</strong>{" "}
19
+ <a href="#limpar">Limpar filtro</a>
20
+ </>
21
+ }
22
+ />,
23
+ );
24
+ const titulo = container.querySelector(".umds-empty-state__title") as HTMLElement;
25
+ expect(titulo).toContainElement(screen.getByTestId("termo"));
26
+ expect(titulo.querySelector("a")).toHaveAttribute("href", "#limpar");
27
+ });
28
+
29
+ it("o título é um div (conteúdo de bloco não vira HTML inválido dentro de p)", () => {
30
+ const { container } = render(<EmptyState title={<div data-testid="bloco">Bloco</div>} />);
31
+ expect(container.querySelector(".umds-empty-state__title")!.tagName).toBe("DIV");
32
+ expect(screen.getByTestId("bloco").parentElement).toHaveClass("umds-empty-state__title");
33
+ });
34
+ });
@@ -0,0 +1,89 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { fireEvent, render, screen } from "@testing-library/react";
3
+ import FileInput from "../components/FileInput/FileInput";
4
+
5
+ const arquivo = (nome: string) => new File(["conteudo"], nome, { type: "application/pdf" });
6
+
7
+ describe("FileInput · variant trigger", () => {
8
+ it("renderiza o conteúdo como gatilho, sem a caixa de arrastar", () => {
9
+ const { container } = render(
10
+ <FileInput variant="trigger">
11
+ <button type="button">Anexar arquivo</button>
12
+ </FileInput>,
13
+ );
14
+ expect(screen.getByRole("button", { name: "Anexar arquivo" })).toBeInTheDocument();
15
+ expect(container.querySelector(".umds-file-input__drop-zone")).toBeNull();
16
+ expect(container.querySelector(".umds-file-input--trigger")).not.toBeNull();
17
+ });
18
+
19
+ it("clicar no conteúdo abre o seletor (clique no input nativo)", () => {
20
+ const { container } = render(
21
+ <FileInput variant="trigger">
22
+ <button type="button">Anexar arquivo</button>
23
+ </FileInput>,
24
+ );
25
+ const nativo = container.querySelector('input[type="file"]') as HTMLInputElement;
26
+ const clique = vi.spyOn(nativo, "click");
27
+ fireEvent.click(screen.getByRole("button", { name: "Anexar arquivo" }));
28
+ expect(clique).toHaveBeenCalledTimes(1);
29
+ });
30
+
31
+ it("disabled não abre o seletor", () => {
32
+ const { container } = render(
33
+ <FileInput variant="trigger" disabled>
34
+ <span>Alterar foto</span>
35
+ </FileInput>,
36
+ );
37
+ const nativo = container.querySelector('input[type="file"]') as HTMLInputElement;
38
+ const clique = vi.spyOn(nativo, "click");
39
+ fireEvent.click(screen.getByText("Alterar foto"));
40
+ expect(clique).not.toHaveBeenCalled();
41
+ });
42
+
43
+ it("escolher arquivos chama onChange, e accept e multiple chegam ao input", () => {
44
+ const aoMudar = vi.fn();
45
+ const { container } = render(
46
+ <FileInput variant="trigger" accept=".pdf" multiple onChange={aoMudar}>
47
+ <button type="button">Anexar</button>
48
+ </FileInput>,
49
+ );
50
+ const nativo = container.querySelector('input[type="file"]') as HTMLInputElement;
51
+ expect(nativo).toHaveAttribute("accept", ".pdf");
52
+ expect(nativo).toHaveAttribute("multiple");
53
+ fireEvent.change(nativo, { target: { files: [arquivo("a.pdf"), arquivo("b.pdf")] } });
54
+ expect(aoMudar).toHaveBeenCalledTimes(1);
55
+ expect(aoMudar.mock.calls[0][0].map((f: File) => f.name)).toEqual(["a.pdf", "b.pdf"]);
56
+ });
57
+
58
+ it("o input nativo não entra no Tab e tem nome acessível", () => {
59
+ const { container } = render(
60
+ <FileInput variant="trigger" label="Anexar arquivo">
61
+ <button type="button">Anexar</button>
62
+ </FileInput>,
63
+ );
64
+ const nativo = container.querySelector('input[type="file"]') as HTMLInputElement;
65
+ expect(nativo).toHaveAttribute("tabindex", "-1");
66
+ expect(nativo).toHaveAttribute("aria-label", "Anexar arquivo");
67
+ // o rótulo visível não aparece: o gatilho é o conteúdo
68
+ expect(container.querySelector(".umds-file-input__label")).toBeNull();
69
+ });
70
+
71
+ it("a lista de arquivos continua aparecendo no modo trigger", () => {
72
+ render(
73
+ <FileInput variant="trigger" files={[{ name: "oficio.pdf", size: 2048 }]}>
74
+ <button type="button">Anexar</button>
75
+ </FileInput>,
76
+ );
77
+ expect(screen.getByText("oficio.pdf")).toBeInTheDocument();
78
+ });
79
+
80
+ it("as variantes base e card seguem com a caixa", () => {
81
+ const { container } = render(
82
+ <>
83
+ <FileInput variant="base" />
84
+ <FileInput variant="card" />
85
+ </>,
86
+ );
87
+ expect(container.querySelectorAll(".umds-file-input__drop-zone")).toHaveLength(2);
88
+ });
89
+ });