@1doc/1ds-react 2.7.2 → 2.9.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 (302) hide show
  1. package/CHANGELOG.md +1280 -0
  2. package/dist/components/AIContent/AIContent.d.ts +10 -0
  3. package/dist/components/AIContent/AIContent.js +1 -1
  4. package/dist/components/AILabel/AILabel.d.ts +4 -0
  5. package/dist/components/AILabel/AILabel.js +2 -2
  6. package/dist/components/Accordion/Accordion.d.ts +6 -2
  7. package/dist/components/Alert/Alert.css +2 -0
  8. package/dist/components/Alert/Alert.d.ts +3 -1
  9. package/dist/components/Alert/Alert.js +7 -1
  10. package/dist/components/AppHeader/AppHeader.css +82 -8
  11. package/dist/components/AppHeader/AppHeader.d.ts +12 -1
  12. package/dist/components/AppHeader/AppHeader.js +186 -1
  13. package/dist/components/AppNavItem/AppNavItem.css +11 -3
  14. package/dist/components/AppShell/AppShell.css +9 -0
  15. package/dist/components/AppShell/AppShell.d.ts +8 -0
  16. package/dist/components/AppShell/AppShell.js +4 -1
  17. package/dist/components/AppSidebar/AppSidebar.css +1 -1
  18. package/dist/components/AppSidebar/AppSidebar.d.ts +4 -0
  19. package/dist/components/AppSidebar/AppSidebar.js +3 -1
  20. package/dist/components/AttachmentCard/AttachmentCard.css +17 -1
  21. package/dist/components/AttachmentCard/AttachmentCard.d.ts +29 -2
  22. package/dist/components/AttachmentCard/AttachmentCard.js +88 -20
  23. package/dist/components/Avatar/Avatar.css +7 -2
  24. package/dist/components/Avatar/AvatarGroup.d.ts +27 -0
  25. package/dist/components/Avatar/AvatarGroup.js +28 -4
  26. package/dist/components/Badge/Badge.css +61 -3
  27. package/dist/components/Badge/Badge.d.ts +14 -4
  28. package/dist/components/Badge/Badge.js +1 -0
  29. package/dist/components/BottomSheet/BottomSheet.css +1 -0
  30. package/dist/components/BottomSheet/BottomSheet.d.ts +8 -0
  31. package/dist/components/BottomSheet/BottomSheet.js +14 -2
  32. package/dist/components/Box/Box.css +83 -0
  33. package/dist/components/Box/Box.d.ts +82 -2
  34. package/dist/components/Box/Box.js +42 -2
  35. package/dist/components/Button/Button.css +29 -0
  36. package/dist/components/Button/Button.d.ts +4 -4
  37. package/dist/components/Button/Button.js +4 -1
  38. package/dist/components/Calendar/Calendar.css +245 -0
  39. package/dist/components/Calendar/Calendar.d.ts +125 -0
  40. package/dist/components/Calendar/Calendar.js +320 -0
  41. package/dist/components/Chart/BarChart.d.ts +6 -0
  42. package/dist/components/Chart/BarChart.js +3 -1
  43. package/dist/components/Chart/LineChart.d.ts +6 -0
  44. package/dist/components/Chart/LineChart.js +3 -1
  45. package/dist/components/Checkbox/Checkbox.css +4 -1
  46. package/dist/components/Chips/Chips.css +39 -0
  47. package/dist/components/Chips/Chips.d.ts +7 -2
  48. package/dist/components/Chips/Chips.js +6 -2
  49. package/dist/components/Coachmark/Coachmark.css +118 -0
  50. package/dist/components/Coachmark/Coachmark.d.ts +75 -0
  51. package/dist/components/Coachmark/Coachmark.js +321 -0
  52. package/dist/components/CodeBlock/CodeBlock.css +96 -0
  53. package/dist/components/CodeBlock/CodeBlock.d.ts +47 -0
  54. package/dist/components/CodeBlock/CodeBlock.js +103 -0
  55. package/dist/components/ColorPicker/ColorPicker.css +133 -0
  56. package/dist/components/ColorPicker/ColorPicker.d.ts +73 -0
  57. package/dist/components/ColorPicker/ColorPicker.js +219 -0
  58. package/dist/components/Combobox/Combobox.css +11 -3
  59. package/dist/components/ConfirmAIAction/ConfirmAIAction.d.ts +11 -1
  60. package/dist/components/ConfirmAIAction/ConfirmAIAction.js +6 -1
  61. package/dist/components/ConfirmDialog/ConfirmDialog.css +3 -0
  62. package/dist/components/ConfirmDialog/ConfirmDialog.d.ts +14 -0
  63. package/dist/components/ConfirmDialog/ConfirmDialog.js +2 -2
  64. package/dist/components/DataTable/DataTable.css +80 -0
  65. package/dist/components/DataTable/DataTable.d.ts +23 -4
  66. package/dist/components/DataTable/DataTable.js +61 -15
  67. package/dist/components/DatePicker/DatePicker.css +8 -0
  68. package/dist/components/DatePicker/DatePicker.d.ts +4 -0
  69. package/dist/components/DatePicker/DatePicker.js +14 -1
  70. package/dist/components/DescriptionList/DescriptionList.css +113 -0
  71. package/dist/components/DescriptionList/DescriptionList.d.ts +59 -0
  72. package/dist/components/DescriptionList/DescriptionList.js +51 -0
  73. package/dist/components/Drawer/Drawer.css +1 -0
  74. package/dist/components/Dropdown/Dropdown.js +47 -29
  75. package/dist/components/FileInput/FileInput.css +24 -4
  76. package/dist/components/FileInput/FileInput.d.ts +22 -2
  77. package/dist/components/FileInput/FileInput.js +23 -2
  78. package/dist/components/Grid/Grid.js +5 -5
  79. package/dist/components/IconPicker/IconPicker.css +107 -0
  80. package/dist/components/IconPicker/IconPicker.d.ts +74 -0
  81. package/dist/components/IconPicker/IconPicker.js +194 -0
  82. package/dist/components/Input/Input.css +41 -3
  83. package/dist/components/Input/Input.d.ts +14 -1
  84. package/dist/components/Input/Input.js +72 -6
  85. package/dist/components/Kanban/Kanban.css +5 -0
  86. package/dist/components/Kanban/Kanban.d.ts +41 -0
  87. package/dist/components/Kanban/Kanban.js +59 -27
  88. package/dist/components/Kanban/KanbanCard.d.ts +2 -0
  89. package/dist/components/Kanban/KanbanCard.js +1 -1
  90. package/dist/components/Kanban/KanbanColumn.d.ts +2 -0
  91. package/dist/components/Kanban/KanbanColumn.js +3 -0
  92. package/dist/components/Kbd/Kbd.css +66 -0
  93. package/dist/components/Kbd/Kbd.d.ts +32 -0
  94. package/dist/components/Kbd/Kbd.js +40 -0
  95. package/dist/components/Link/Link.css +1 -1
  96. package/dist/components/List/List.css +136 -0
  97. package/dist/components/List/List.d.ts +82 -0
  98. package/dist/components/List/List.js +53 -0
  99. package/dist/components/List/ListItem.d.ts +85 -0
  100. package/dist/components/List/ListItem.js +81 -0
  101. package/dist/components/Modal/Modal.css +93 -8
  102. package/dist/components/Modal/Modal.d.ts +45 -7
  103. package/dist/components/Modal/Modal.js +106 -6
  104. package/dist/components/MultiSelect/MultiSelect.css +9 -1
  105. package/dist/components/MultiSelect/MultiSelect.js +45 -8
  106. package/dist/components/OrgChart/OrgChart.css +149 -0
  107. package/dist/components/OrgChart/OrgChart.d.ts +83 -0
  108. package/dist/components/OrgChart/OrgChart.js +297 -0
  109. package/dist/components/OtpInput/OtpInput.css +93 -0
  110. package/dist/components/OtpInput/OtpInput.d.ts +60 -0
  111. package/dist/components/OtpInput/OtpInput.js +215 -0
  112. package/dist/components/Pagination/Pagination.d.ts +6 -0
  113. package/dist/components/Pagination/Pagination.js +9 -1
  114. package/dist/components/PhoneInput/PhoneInput.css +9 -1
  115. package/dist/components/PhoneInput/PhoneInput.d.ts +10 -0
  116. package/dist/components/PhoneInput/PhoneInput.js +20 -3
  117. package/dist/components/Popover/Popover.d.ts +7 -0
  118. package/dist/components/Popover/Popover.js +11 -1
  119. package/dist/components/RichTextEditor/RichTextEditor.css +203 -0
  120. package/dist/components/RichTextEditor/RichTextEditor.d.ts +83 -0
  121. package/dist/components/RichTextEditor/RichTextEditor.js +494 -0
  122. package/dist/components/SegmentedControl/SegmentedControl.d.ts +2 -0
  123. package/dist/components/SegmentedControl/SegmentedControl.js +21 -8
  124. package/dist/components/Select/Select.css +71 -1
  125. package/dist/components/Select/Select.d.ts +12 -0
  126. package/dist/components/Select/Select.js +93 -10
  127. package/dist/components/SidePanel/SidePanel.css +4 -4
  128. package/dist/components/SidePanelItem/SidePanelItem.css +7 -5
  129. package/dist/components/SidePanelSearch/SidePanelSearch.css +2 -2
  130. package/dist/components/SidePanelSection/SidePanelSection.css +7 -5
  131. package/dist/components/Stack/Stack.css +49 -0
  132. package/dist/components/Stack/Stack.d.ts +65 -7
  133. package/dist/components/Stack/Stack.js +30 -2
  134. package/dist/components/StatCard/StatCard.css +73 -0
  135. package/dist/components/StatCard/StatCard.d.ts +17 -0
  136. package/dist/components/StatCard/StatCard.js +4 -1
  137. package/dist/components/Stepper/Stepper.css +4 -1
  138. package/dist/components/Stepper/Stepper.d.ts +8 -0
  139. package/dist/components/Stepper/Stepper.js +4 -4
  140. package/dist/components/Switch/Switch.css +1 -0
  141. package/dist/components/Tabs/Tab.d.ts +15 -0
  142. package/dist/components/Tabs/Tab.js +30 -1
  143. package/dist/components/Tabs/Tabs.css +132 -0
  144. package/dist/components/Tabs/Tabs.d.ts +7 -0
  145. package/dist/components/Tabs/Tabs.js +13 -2
  146. package/dist/components/Tag/Tag.css +85 -2
  147. package/dist/components/Tag/Tag.d.ts +13 -6
  148. package/dist/components/Tag/Tag.js +1 -0
  149. package/dist/components/Text/Text.d.ts +9 -1
  150. package/dist/components/Text/Text.js +2 -2
  151. package/dist/components/Textarea/Textarea.css +11 -3
  152. package/dist/components/TimePicker/TimePicker.css +8 -0
  153. package/dist/components/TimePicker/TimePicker.d.ts +6 -0
  154. package/dist/components/TimePicker/TimePicker.js +1 -1
  155. package/dist/components/Tooltip/Tooltip.css +40 -0
  156. package/dist/components/Tooltip/Tooltip.d.ts +11 -0
  157. package/dist/components/Tooltip/Tooltip.js +114 -11
  158. package/dist/index.d.ts +25 -0
  159. package/dist/index.js +14 -0
  160. package/dist/teste/AILabel.test.d.ts +1 -0
  161. package/dist/teste/AILabel.test.js +33 -0
  162. package/dist/teste/AccordionConteudoLivre.test.d.ts +1 -0
  163. package/dist/teste/AccordionConteudoLivre.test.js +29 -0
  164. package/dist/teste/Alert.test.d.ts +1 -0
  165. package/dist/teste/Alert.test.js +27 -0
  166. package/dist/teste/AtributosRepassados.test.d.ts +1 -0
  167. package/dist/teste/AtributosRepassados.test.js +52 -0
  168. package/dist/teste/AttachmentCardIA.test.d.ts +1 -0
  169. package/dist/teste/AttachmentCardIA.test.js +89 -0
  170. package/dist/teste/AvatarGroupOverlap.test.d.ts +1 -0
  171. package/dist/teste/AvatarGroupOverlap.test.js +56 -0
  172. package/dist/teste/BadgeTag.test.d.ts +1 -0
  173. package/dist/teste/BadgeTag.test.js +42 -0
  174. package/dist/teste/BottomSheet.test.d.ts +1 -0
  175. package/dist/teste/BottomSheet.test.js +23 -0
  176. package/dist/teste/BoxItemFlex.test.d.ts +1 -0
  177. package/dist/teste/BoxItemFlex.test.js +54 -0
  178. package/dist/teste/BoxLayout.test.d.ts +1 -0
  179. package/dist/teste/BoxLayout.test.js +35 -0
  180. package/dist/teste/Button.test.d.ts +1 -0
  181. package/dist/teste/Button.test.js +27 -0
  182. package/dist/teste/Calendar.test.d.ts +1 -0
  183. package/dist/teste/Calendar.test.js +221 -0
  184. package/dist/teste/ChipsXs.test.d.ts +1 -0
  185. package/dist/teste/ChipsXs.test.js +31 -0
  186. package/dist/teste/Coachmark.test.d.ts +1 -0
  187. package/dist/teste/Coachmark.test.js +149 -0
  188. package/dist/teste/ColorPicker.test.d.ts +1 -0
  189. package/dist/teste/ColorPicker.test.js +132 -0
  190. package/dist/teste/ConfirmAIAction.test.d.ts +1 -0
  191. package/dist/teste/ConfirmAIAction.test.js +33 -0
  192. package/dist/teste/DataTableCelulaLivre.test.d.ts +1 -0
  193. package/dist/teste/DataTableCelulaLivre.test.js +76 -0
  194. package/dist/teste/DataTableDocumento.test.d.ts +1 -0
  195. package/dist/teste/DataTableDocumento.test.js +92 -0
  196. package/dist/teste/DatePicker.test.js +23 -0
  197. package/dist/teste/Dropdown.test.js +111 -0
  198. package/dist/teste/FileInputTrigger.test.d.ts +1 -0
  199. package/dist/teste/FileInputTrigger.test.js +53 -0
  200. package/dist/teste/IconPicker.test.d.ts +1 -0
  201. package/dist/teste/IconPicker.test.js +130 -0
  202. package/dist/teste/InputMascaraEAfins.test.d.ts +1 -0
  203. package/dist/teste/InputMascaraEAfins.test.js +99 -0
  204. package/dist/teste/KbdCodeBlock.test.d.ts +1 -0
  205. package/dist/teste/KbdCodeBlock.test.js +95 -0
  206. package/dist/teste/List.test.d.ts +1 -0
  207. package/dist/teste/List.test.js +153 -0
  208. package/dist/teste/Modal.test.d.ts +1 -0
  209. package/dist/teste/Modal.test.js +26 -0
  210. package/dist/teste/ModalVariantes.test.d.ts +1 -0
  211. package/dist/teste/ModalVariantes.test.js +151 -0
  212. package/dist/teste/MultiSelectErro.test.d.ts +1 -0
  213. package/dist/teste/MultiSelectErro.test.js +62 -0
  214. package/dist/teste/NomeAcessivelSsr.test.d.ts +1 -0
  215. package/dist/teste/NomeAcessivelSsr.test.js +88 -0
  216. package/dist/teste/OrgChart.test.d.ts +1 -0
  217. package/dist/teste/OrgChart.test.js +162 -0
  218. package/dist/teste/OtpInput.test.d.ts +1 -0
  219. package/dist/teste/OtpInput.test.js +143 -0
  220. package/dist/teste/PopoverMaxHeight.test.d.ts +1 -0
  221. package/dist/teste/PopoverMaxHeight.test.js +38 -0
  222. package/dist/teste/RichTextEditor.test.d.ts +1 -0
  223. package/dist/teste/RichTextEditor.test.js +287 -0
  224. package/dist/teste/RotulosTraduziveis.test.d.ts +1 -0
  225. package/dist/teste/RotulosTraduziveis.test.js +405 -0
  226. package/dist/teste/SegmentedControl.test.js +33 -0
  227. package/dist/teste/Select.test.d.ts +1 -0
  228. package/dist/teste/Select.test.js +62 -0
  229. package/dist/teste/SelectPlacementFiltro.test.d.ts +1 -0
  230. package/dist/teste/SelectPlacementFiltro.test.js +88 -0
  231. package/dist/teste/Stack.test.d.ts +1 -0
  232. package/dist/teste/Stack.test.js +57 -0
  233. package/dist/teste/StatCardRotuloTopo.test.d.ts +1 -0
  234. package/dist/teste/StatCardRotuloTopo.test.js +34 -0
  235. package/dist/teste/TabsContadorFechavel.test.d.ts +1 -0
  236. package/dist/teste/TabsContadorFechavel.test.js +79 -0
  237. package/dist/teste/Tooltip.test.d.ts +1 -0
  238. package/dist/teste/Tooltip.test.js +109 -0
  239. package/dist/teste/TooltipDark.test.d.ts +1 -0
  240. package/dist/teste/TooltipDark.test.js +31 -0
  241. package/dist/utils/calendario.d.ts +28 -0
  242. package/dist/utils/calendario.js +73 -0
  243. package/dist/utils/chart.d.ts +4 -0
  244. package/dist/utils/chart.js +3 -2
  245. package/dist/utils/html-seguro.d.ts +27 -0
  246. package/dist/utils/html-seguro.js +97 -0
  247. package/dist/utils/id.d.ts +2 -0
  248. package/dist/utils/id.js +23 -0
  249. package/dist/utils/paleta-de-cores.d.ts +26 -0
  250. package/dist/utils/paleta-de-cores.js +10 -0
  251. package/dist/utils/texto.d.ts +5 -0
  252. package/dist/utils/texto.js +5 -0
  253. package/package.json +3 -3
  254. package/src/index.ts +26 -0
  255. package/src/teste/AILabel.test.tsx +41 -0
  256. package/src/teste/AccordionConteudoLivre.test.tsx +39 -0
  257. package/src/teste/Alert.test.tsx +32 -0
  258. package/src/teste/AtributosRepassados.test.tsx +73 -0
  259. package/src/teste/AttachmentCardIA.test.tsx +126 -0
  260. package/src/teste/AvatarGroupOverlap.test.tsx +74 -0
  261. package/src/teste/BadgeTag.test.tsx +64 -0
  262. package/src/teste/BottomSheet.test.tsx +25 -0
  263. package/src/teste/BoxItemFlex.test.tsx +81 -0
  264. package/src/teste/BoxLayout.test.tsx +50 -0
  265. package/src/teste/Button.test.tsx +39 -0
  266. package/src/teste/Calendar.test.tsx +282 -0
  267. package/src/teste/ChipsXs.test.tsx +41 -0
  268. package/src/teste/Coachmark.test.tsx +198 -0
  269. package/src/teste/ColorPicker.test.tsx +153 -0
  270. package/src/teste/ConfirmAIAction.test.tsx +41 -0
  271. package/src/teste/DataTableCelulaLivre.test.tsx +103 -0
  272. package/src/teste/DataTableDocumento.test.tsx +102 -0
  273. package/src/teste/DatePicker.test.tsx +38 -0
  274. package/src/teste/Dropdown.test.tsx +146 -0
  275. package/src/teste/FileInputTrigger.test.tsx +89 -0
  276. package/src/teste/IconPicker.test.tsx +145 -0
  277. package/src/teste/InputMascaraEAfins.test.tsx +124 -0
  278. package/src/teste/KbdCodeBlock.test.tsx +106 -0
  279. package/src/teste/List.test.tsx +269 -0
  280. package/src/teste/Modal.test.tsx +28 -0
  281. package/src/teste/ModalVariantes.test.tsx +173 -0
  282. package/src/teste/MultiSelectErro.test.tsx +97 -0
  283. package/src/teste/NomeAcessivelSsr.test.tsx +110 -0
  284. package/src/teste/OrgChart.test.tsx +183 -0
  285. package/src/teste/OtpInput.test.tsx +163 -0
  286. package/src/teste/PopoverMaxHeight.test.tsx +47 -0
  287. package/src/teste/RichTextEditor.test.tsx +329 -0
  288. package/src/teste/RotulosTraduziveis.test.tsx +545 -0
  289. package/src/teste/SegmentedControl.test.tsx +57 -0
  290. package/src/teste/Select.test.tsx +97 -0
  291. package/src/teste/SelectPlacementFiltro.test.tsx +110 -0
  292. package/src/teste/Stack.test.tsx +80 -0
  293. package/src/teste/StatCardRotuloTopo.test.tsx +44 -0
  294. package/src/teste/TabsContadorFechavel.test.tsx +144 -0
  295. package/src/teste/Tooltip.test.tsx +219 -0
  296. package/src/teste/TooltipDark.test.tsx +51 -0
  297. package/src/utils/calendario.ts +78 -0
  298. package/src/utils/chart.ts +7 -2
  299. package/src/utils/html-seguro.ts +94 -0
  300. package/src/utils/id.ts +24 -0
  301. package/src/utils/paleta-de-cores.ts +34 -0
  302. package/src/utils/texto.ts +5 -0
@@ -0,0 +1,545 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { render, screen, waitFor, within } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+ import AIContent from "../components/AIContent/AIContent";
5
+ import AILabel from "../components/AILabel/AILabel";
6
+ import AppSidebar from "../components/AppSidebar/AppSidebar";
7
+ import AttachmentCard from "../components/AttachmentCard/AttachmentCard";
8
+ import AvatarGroup from "../components/Avatar/AvatarGroup";
9
+ import BarChart from "../components/Chart/BarChart";
10
+ import LineChart from "../components/Chart/LineChart";
11
+ import BottomSheet from "../components/BottomSheet/BottomSheet";
12
+ import Chips from "../components/Chips/Chips";
13
+ import ConfirmAIAction from "../components/ConfirmAIAction/ConfirmAIAction";
14
+ import ConfirmDialog from "../components/ConfirmDialog/ConfirmDialog";
15
+ import DataTable from "../components/DataTable/DataTable";
16
+ import DatePicker from "../components/DatePicker/DatePicker";
17
+ import FileInput from "../components/FileInput/FileInput";
18
+ import Input from "../components/Input/Input";
19
+ import Kanban from "../components/Kanban/Kanban";
20
+ import KanbanCard from "../components/Kanban/KanbanCard";
21
+ import KanbanColumn from "../components/Kanban/KanbanColumn";
22
+ import Pagination from "../components/Pagination/Pagination";
23
+ import PhoneInput from "../components/PhoneInput/PhoneInput";
24
+ import Stepper from "../components/Stepper/Stepper";
25
+ import TimePicker from "../components/TimePicker/TimePicker";
26
+
27
+ /**
28
+ * Rótulos traduzíveis. Cada componente tinha texto em português fixo no código
29
+ * (visível ou lido pelo leitor de tela), sem prop para trocar. Duas garantias
30
+ * por componente: SEM a prop, sai exatamente o português de antes; COM a prop,
31
+ * sai o texto passado. A primeira é a que protege quem já usa o DS.
32
+ */
33
+
34
+ describe("Chips · removeAriaLabel", () => {
35
+ it("padrão em português", () => {
36
+ render(<Chips label="Financeiro" removable />);
37
+ expect(screen.getByRole("button", { name: "Remover Financeiro" })).toBeInTheDocument();
38
+ });
39
+ it("prefixo customizado", () => {
40
+ render(<Chips label="Finance" removable removeAriaLabel="Remove" />);
41
+ expect(screen.getByRole("button", { name: "Remove Finance" })).toBeInTheDocument();
42
+ });
43
+ });
44
+
45
+ describe("Stepper · estados lidos", () => {
46
+ const passos = [
47
+ { title: "A", status: "completed" as const },
48
+ { title: "B", status: "active" as const },
49
+ { title: "C", status: "error" as const },
50
+ { title: "D", status: "pending" as const },
51
+ ];
52
+ it("padrão em português", () => {
53
+ render(<Stepper steps={passos} />);
54
+ for (const nome of ["A, concluído", "B, atual", "C, erro", "D, pendente"]) {
55
+ expect(screen.getByRole("listitem", { name: nome })).toBeInTheDocument();
56
+ }
57
+ });
58
+ it("rótulos customizados", () => {
59
+ render(
60
+ <Stepper
61
+ steps={passos}
62
+ completedLabel="completed"
63
+ activeLabel="current"
64
+ errorLabel="failed"
65
+ pendingLabel="pending"
66
+ />,
67
+ );
68
+ for (const nome of ["A, completed", "B, current", "C, failed", "D, pending"]) {
69
+ expect(screen.getByRole("listitem", { name: nome })).toBeInTheDocument();
70
+ }
71
+ });
72
+ });
73
+
74
+ describe("FileInput · textos", () => {
75
+ const arquivos = [
76
+ { name: "a.pdf", size: 2048, status: "completed" as const },
77
+ { name: "b.pdf", size: 2048, status: "uploading" as const, progress: 40 },
78
+ ];
79
+ it("padrão em português (base e card)", () => {
80
+ const { container, unmount } = render(
81
+ <FileInput files={arquivos} maxSize={1048576} />,
82
+ );
83
+ expect(screen.getByRole("button", { name: "Remover a.pdf" })).toBeInTheDocument();
84
+ expect(screen.getByRole("progressbar", { name: "Enviando b.pdf" })).toBeInTheDocument();
85
+ expect(screen.getByLabelText("Selecionar arquivo")).toBeInTheDocument();
86
+ const gatilho = container.querySelector(".umds-file-input__trigger-text")!;
87
+ expect(gatilho.textContent).toBe("Clique para selecionar ou arraste aqui");
88
+ expect(screen.getByText("Máximo 1.0 MB")).toBeInTheDocument();
89
+ unmount();
90
+
91
+ const card = render(<FileInput variant="card" />);
92
+ const texto = card.container.querySelector(".umds-file-input__card-text")!;
93
+ expect(texto.textContent).toBe("Arraste e solte ou clique para escolher");
94
+ });
95
+ it("rótulos customizados", () => {
96
+ const { container, unmount } = render(
97
+ <FileInput
98
+ files={arquivos}
99
+ maxSize={1048576}
100
+ selectFileLabel="Select file"
101
+ triggerLabel="Click to select"
102
+ triggerHint="or drag here"
103
+ maxSizeLabel="Max"
104
+ removeAriaLabel="Remove"
105
+ uploadingAriaLabel="Uploading"
106
+ />,
107
+ );
108
+ expect(screen.getByRole("button", { name: "Remove a.pdf" })).toBeInTheDocument();
109
+ expect(screen.getByRole("progressbar", { name: "Uploading b.pdf" })).toBeInTheDocument();
110
+ expect(screen.getByLabelText("Select file")).toBeInTheDocument();
111
+ const gatilho = container.querySelector(".umds-file-input__trigger-text")!;
112
+ expect(gatilho.textContent).toBe("Click to select or drag here");
113
+ expect(screen.getByText("Max 1.0 MB")).toBeInTheDocument();
114
+ unmount();
115
+
116
+ const card = render(
117
+ <FileInput variant="card" dropLabel="Drag and drop" orLabel="or" browseLabel="click to choose" />,
118
+ );
119
+ const texto = card.container.querySelector(".umds-file-input__card-text")!;
120
+ expect(texto.textContent).toBe("Drag and drop or click to choose");
121
+ });
122
+ });
123
+
124
+ describe("AILabel · painel", () => {
125
+ const props = { sources: ["Fonte"], notIncluded: "Fora" };
126
+ it("padrão em português", () => {
127
+ render(<AILabel {...props} open />);
128
+ expect(screen.getByRole("button", { name: "Conteúdo gerado por IA" })).toBeInTheDocument();
129
+ expect(screen.getByText("Baseado em")).toBeInTheDocument();
130
+ expect(screen.getByText("Não considerado")).toBeInTheDocument();
131
+ });
132
+ it("rótulos customizados", () => {
133
+ render(<AILabel {...props} open sourcesLabel="Based on" notIncludedLabel="Not considered" />);
134
+ expect(screen.getByText("Based on")).toBeInTheDocument();
135
+ expect(screen.getByText("Not considered")).toBeInTheDocument();
136
+ });
137
+ });
138
+
139
+ describe("AIContent · repasse ao selo", () => {
140
+ it("padrão em português", async () => {
141
+ const usuario = userEvent.setup();
142
+ render(<AIContent sources={["Fonte"]} notIncluded="Fora">Texto</AIContent>);
143
+ const selo = screen.getByRole("button", { name: "Conteúdo gerado por IA" });
144
+ expect(selo).toHaveTextContent("IA");
145
+ await usuario.click(selo);
146
+ expect(screen.getByText("Como isto foi gerado")).toBeInTheDocument();
147
+ expect(screen.getByText("Baseado em")).toBeInTheDocument();
148
+ expect(screen.getByText("Não considerado")).toBeInTheDocument();
149
+ });
150
+ it("rótulos customizados chegam ao AILabel", async () => {
151
+ const usuario = userEvent.setup();
152
+ render(
153
+ <AIContent
154
+ sources={["Source"]}
155
+ notIncluded="Out"
156
+ label="AI"
157
+ ariaLabel="AI-generated content"
158
+ explanationTitle="How this was generated"
159
+ sourcesLabel="Based on"
160
+ notIncludedLabel="Not considered"
161
+ >
162
+ Text
163
+ </AIContent>,
164
+ );
165
+ const selo = screen.getByRole("button", { name: "AI-generated content" });
166
+ expect(selo).toHaveTextContent("AI");
167
+ await usuario.click(selo);
168
+ expect(screen.getByText("How this was generated")).toBeInTheDocument();
169
+ expect(screen.getByText("Based on")).toBeInTheDocument();
170
+ expect(screen.getByText("Not considered")).toBeInTheDocument();
171
+ });
172
+ });
173
+
174
+ describe("Pagination · textos", () => {
175
+ it("padrão em português", () => {
176
+ const { unmount } = render(<Pagination page={1} totalPages={5} />);
177
+ expect(screen.getByText("de")).toBeInTheDocument();
178
+ unmount();
179
+ const resumida = render(
180
+ <Pagination variant="resumed" page={2} totalPages={5} pageSize={10} totalItems={45} />,
181
+ );
182
+ expect(screen.getByText("11-20 de 45")).toBeInTheDocument();
183
+ resumida.unmount();
184
+ render(<Pagination variant="full-width" page={1} totalPages={3} />);
185
+ expect(screen.getByRole("button", { name: "Página 2" })).toBeInTheDocument();
186
+ });
187
+ it("rótulos customizados", () => {
188
+ const rotulos = {
189
+ ofLabel: "of",
190
+ itemRangeLabel: (a: number, b: number, t: number) => `${a}–${b} of ${t}`,
191
+ pageLabel: (n: number) => `Page ${n}`,
192
+ };
193
+ const { unmount } = render(<Pagination {...rotulos} page={1} totalPages={5} />);
194
+ expect(screen.getByText("of")).toBeInTheDocument();
195
+ unmount();
196
+ const resumida = render(
197
+ <Pagination {...rotulos} variant="resumed" page={2} totalPages={5} pageSize={10} totalItems={45} />,
198
+ );
199
+ expect(screen.getByText("11–20 of 45")).toBeInTheDocument();
200
+ resumida.unmount();
201
+ render(<Pagination {...rotulos} variant="bordered" page={1} totalPages={3} />);
202
+ expect(screen.getByRole("button", { name: "Page 2" })).toBeInTheDocument();
203
+ });
204
+ });
205
+
206
+ describe("DataTable · seleção", () => {
207
+ const colunas = [{ key: "nome", label: "Nome" }];
208
+ const dados = [{ nome: "Ana" }, { nome: "Bia" }];
209
+ it("padrão em português", () => {
210
+ render(<DataTable columns={colunas} data={dados} selectable />);
211
+ expect(screen.getByRole("checkbox", { name: "Selecionar todas as linhas" })).toBeInTheDocument();
212
+ expect(screen.getByRole("checkbox", { name: "Selecionar linha 2" })).toBeInTheDocument();
213
+ });
214
+ it("rótulos customizados (a função recebe o número da linha a partir de 1)", () => {
215
+ render(
216
+ <DataTable
217
+ columns={colunas}
218
+ data={dados}
219
+ selectable
220
+ selectAllLabel="Select all rows"
221
+ selectRowLabel={(n) => `Select row ${n}`}
222
+ />,
223
+ );
224
+ expect(screen.getByRole("checkbox", { name: "Select all rows" })).toBeInTheDocument();
225
+ expect(screen.getByRole("checkbox", { name: "Select row 1" })).toBeInTheDocument();
226
+ });
227
+ });
228
+
229
+ describe("Input · senha", () => {
230
+ it("padrão em português", async () => {
231
+ const usuario = userEvent.setup();
232
+ render(<Input label="Senha" type="password" />);
233
+ await usuario.click(screen.getByRole("button", { name: "Mostrar senha" }));
234
+ expect(screen.getByRole("button", { name: "Ocultar senha" })).toBeInTheDocument();
235
+ });
236
+ it("rótulos customizados", async () => {
237
+ const usuario = userEvent.setup();
238
+ render(
239
+ <Input label="Password" type="password" showPasswordLabel="Show password" hidePasswordLabel="Hide password" />,
240
+ );
241
+ await usuario.click(screen.getByRole("button", { name: "Show password" }));
242
+ expect(screen.getByRole("button", { name: "Hide password" })).toBeInTheDocument();
243
+ });
244
+ });
245
+
246
+ describe("AvatarGroup · excedente", () => {
247
+ const pessoas = ["Ana", "Bia", "Caio", "Davi", "Edu", "Fábio"].map((name) => ({ name }));
248
+ it("padrão em português", () => {
249
+ render(<AvatarGroup avatars={pessoas} max={4} />);
250
+ const excedente = screen.getByLabelText("Mais 2 avatares");
251
+ expect(excedente).toHaveAttribute("title", "2 outros");
252
+ });
253
+ it("rótulos customizados", () => {
254
+ render(
255
+ <AvatarGroup
256
+ avatars={pessoas}
257
+ max={4}
258
+ overflowAriaLabel={(n) => `${n} more avatars`}
259
+ overflowTitle={(n) => `${n} others`}
260
+ />,
261
+ );
262
+ const excedente = screen.getByLabelText("2 more avatars");
263
+ expect(excedente).toHaveAttribute("title", "2 others");
264
+ });
265
+ });
266
+
267
+ describe("AppSidebar · abrir/fechar", () => {
268
+ const itens = [{ id: "a", label: "Início" }];
269
+ it("padrão em português", () => {
270
+ const { rerender } = render(<AppSidebar items={itens} />);
271
+ expect(screen.getByRole("button", { name: "Fechar a navegação" })).toBeInTheDocument();
272
+ rerender(<AppSidebar items={itens} collapsed />);
273
+ expect(screen.getByRole("button", { name: "Abrir a navegação" })).toBeInTheDocument();
274
+ });
275
+ it("rótulos customizados", () => {
276
+ const rotulos = { expandLabel: "Open navigation", collapseLabel: "Close navigation" };
277
+ const { rerender } = render(<AppSidebar items={itens} {...rotulos} />);
278
+ expect(screen.getByRole("button", { name: "Close navigation" })).toBeInTheDocument();
279
+ rerender(<AppSidebar items={itens} collapsed {...rotulos} />);
280
+ expect(screen.getByRole("button", { name: "Open navigation" })).toBeInTheDocument();
281
+ });
282
+ });
283
+
284
+ describe("AttachmentCard · complementos", () => {
285
+ const base = {
286
+ fileName: "cnh.pdf",
287
+ documentType: "CNH",
288
+ required: true,
289
+ status: "positive" as const,
290
+ statusLabel: "Pré-aprovado",
291
+ statusDetails: { description: "Ok" },
292
+ };
293
+ it("padrão em português", () => {
294
+ render(<AttachmentCard {...base} />);
295
+ expect(screen.getByRole("button", { name: /^Pré-aprovado.*: conferência de CNH$/ })).toBeInTheDocument();
296
+ expect(screen.getByText("(obrigatório)")).toBeInTheDocument();
297
+ });
298
+ it("rótulos customizados", () => {
299
+ render(
300
+ <AttachmentCard
301
+ {...base}
302
+ statusLabel="Pre-approved"
303
+ statusContextLabel={(s) => `review of ${s}`}
304
+ requiredLabel="(required)"
305
+ />,
306
+ );
307
+ expect(screen.getByRole("button", { name: /^Pre-approved.*: review of CNH$/ })).toBeInTheDocument();
308
+ expect(screen.getByText("(required)")).toBeInTheDocument();
309
+ });
310
+ });
311
+
312
+ describe("BarChart e LineChart · tabela oculta", () => {
313
+ const dados = [
314
+ { label: "A", value: 1 },
315
+ { label: "B", value: 2 },
316
+ ];
317
+ it("padrão em português", () => {
318
+ render(<BarChart data={dados} />);
319
+ expect(screen.getByRole("columnheader", { name: "Categoria" })).toBeInTheDocument();
320
+ expect(screen.getByRole("list", { name: "Valores por categoria" })).toBeInTheDocument();
321
+ expect(screen.getByRole("table", { name: "Gráfico de barras" })).toBeInTheDocument();
322
+ });
323
+ it("rótulos customizados", () => {
324
+ render(
325
+ <LineChart
326
+ data={dados}
327
+ title="Cases"
328
+ categoryLabel="Category"
329
+ valuesListLabel="Values by category"
330
+ typeLabel="Line chart"
331
+ />,
332
+ );
333
+ expect(screen.getByRole("columnheader", { name: "Category" })).toBeInTheDocument();
334
+ expect(screen.getByRole("list", { name: "Values by category — Cases" })).toBeInTheDocument();
335
+ });
336
+ });
337
+
338
+ describe("BottomSheet · alça", () => {
339
+ it("padrão em português", () => {
340
+ render(<BottomSheet open title="Filtros" snapPoints={[0.3, 0.6]} />);
341
+ expect(screen.getByRole("slider", { name: "Altura do painel" })).toBeInTheDocument();
342
+ });
343
+ it("rótulo customizado", () => {
344
+ render(<BottomSheet open title="Filters" snapPoints={[0.3, 0.6]} grabberLabel="Sheet height" />);
345
+ expect(screen.getByRole("slider", { name: "Sheet height" })).toBeInTheDocument();
346
+ });
347
+ });
348
+
349
+ describe("ConfirmDialog · carregando", () => {
350
+ it("padrão em português", () => {
351
+ render(<ConfirmDialog open inline title="Excluir?" confirmLoading />);
352
+ expect(screen.getByRole("button", { name: /Aguarde\.\.\./ })).toBeInTheDocument();
353
+ });
354
+ it("rótulo customizado", () => {
355
+ render(<ConfirmDialog open inline title="Delete?" confirmLoading loadingLabel="Please wait..." />);
356
+ expect(screen.getByRole("button", { name: /Please wait\.\.\./ })).toBeInTheDocument();
357
+ });
358
+ });
359
+
360
+ describe("ConfirmAIAction · risco alto", () => {
361
+ it("padrão em português", () => {
362
+ render(<ConfirmAIAction open risk="alto" title="Arquivar" affected={["Processo 1"]} typedValue="" />);
363
+ expect(screen.getByText("O que será afetado")).toBeInTheDocument();
364
+ expect(screen.getByText("Esta ação não pode ser desfeita.")).toBeInTheDocument();
365
+ expect(screen.getByText("O botão de confirmar fica desabilitado até o texto conferir.")).toBeInTheDocument();
366
+ const rotulo = document.querySelector(".umds-confirmai__label")!;
367
+ expect(rotulo.textContent).toBe("Para confirmar, digite CONFIRMAR");
368
+ });
369
+ it("rótulos customizados", () => {
370
+ const rotulos = {
371
+ impactLabel: "What will be affected",
372
+ irreversibleLabel: "This action cannot be undone.",
373
+ typePhraseLabel: "To confirm, type",
374
+ phraseMatchedLabel: "Text matches.",
375
+ phrasePendingLabel: "Waiting for the text.",
376
+ };
377
+ const { rerender } = render(
378
+ <ConfirmAIAction open risk="alto" title="Archive" affected={["Case 1"]} confirmPhrase="CONFIRM" typedValue="" {...rotulos} />,
379
+ );
380
+ expect(screen.getByText("What will be affected")).toBeInTheDocument();
381
+ expect(screen.getByRole("list", { name: "What will be affected" })).toBeInTheDocument();
382
+ expect(screen.getByText("This action cannot be undone.")).toBeInTheDocument();
383
+ expect(screen.getByText("Waiting for the text.")).toBeInTheDocument();
384
+ expect(document.querySelector(".umds-confirmai__label")!.textContent).toBe("To confirm, type CONFIRM");
385
+ rerender(
386
+ <ConfirmAIAction open risk="alto" title="Archive" affected={["Case 1"]} confirmPhrase="CONFIRM" typedValue="confirm" {...rotulos} />,
387
+ );
388
+ expect(screen.getByText("Text matches.")).toBeInTheDocument();
389
+ });
390
+ });
391
+
392
+ describe("DatePicker · painel e dias", () => {
393
+ it("padrão em português", async () => {
394
+ const usuario = userEvent.setup();
395
+ render(<DatePicker label="Data" value="2026-07-10" />);
396
+ await usuario.click(screen.getByRole("combobox"));
397
+ const painel = await screen.findByRole("dialog", { name: "Selecionar data" });
398
+ expect(within(painel).getAllByRole("button", { name: /^20 de [a-zç]+ de 2026$/ }).length).toBeGreaterThan(0);
399
+ });
400
+ it("rótulos customizados", async () => {
401
+ const usuario = userEvent.setup();
402
+ render(
403
+ <DatePicker
404
+ label="Date"
405
+ value="2026-07-10"
406
+ dialogLabel="Pick a date"
407
+ dayLabel={(iso) => `day ${iso}`}
408
+ />,
409
+ );
410
+ await usuario.click(screen.getByRole("combobox"));
411
+ const painel = await screen.findByRole("dialog", { name: "Pick a date" });
412
+ expect(within(painel).getAllByRole("button", { name: /^day 2026-\d{2}-20$/ }).length).toBeGreaterThan(0);
413
+ });
414
+ });
415
+
416
+ describe("Kanban · textos", () => {
417
+ const colunas = [
418
+ {
419
+ id: "a",
420
+ title: "A fazer",
421
+ wipLimit: 2,
422
+ cards: [{ id: "1", title: "Cartão 1", assignee: "Ana Lima", tags: [{ label: "RH" }] }],
423
+ },
424
+ { id: "b", title: "Feito", cards: [] },
425
+ ];
426
+ it("padrão em português", async () => {
427
+ const usuario = userEvent.setup();
428
+ const { container } = render(<Kanban columns={colunas} />);
429
+ const cartao = screen.getByRole("button", { name: "Cartão 1" });
430
+ expect(cartao).toHaveAttribute("aria-roledescription", "cartão arrastável");
431
+ expect(screen.getByText("Responsável: Ana Lima. Etiquetas: RH")).toBeInTheDocument();
432
+ expect(screen.getByText("1 de 2 cartões")).toBeInTheDocument();
433
+ cartao.focus();
434
+ await usuario.keyboard(" ");
435
+ const regiao = container.querySelector('[aria-live="assertive"]')!;
436
+ await waitFor(() =>
437
+ expect(regiao).toHaveTextContent("Cartão Cartão 1 pego. Coluna A fazer, posição 1 de 1."),
438
+ );
439
+ await usuario.keyboard("{ArrowUp}");
440
+ await waitFor(() => expect(regiao).toHaveTextContent("Início da coluna."));
441
+ await usuario.keyboard("{Escape}");
442
+ await waitFor(() => expect(regiao).toHaveTextContent("Movimento cancelado. Cartão Cartão 1 voltou."));
443
+ });
444
+ it("rótulos customizados", async () => {
445
+ const usuario = userEvent.setup();
446
+ const { container } = render(
447
+ <Kanban
448
+ columns={colunas}
449
+ assigneeLabel="Assignee"
450
+ tagsLabel="Tags"
451
+ cardRoleDescription="draggable card"
452
+ columnCountLabel={(n, limite) => `${n}/${limite ?? "-"} cards`}
453
+ announcements={{
454
+ position: (coluna, pos, total) => `Column ${coluna}, ${pos} of ${total}.`,
455
+ grabbed: (c, pos) => `Grabbed ${c}. ${pos}`,
456
+ columnStart: "Top of column.",
457
+ cancelled: (c) => `Cancelled ${c}.`,
458
+ }}
459
+ />,
460
+ );
461
+ const cartao = screen.getByRole("button", { name: "Cartão 1" });
462
+ expect(cartao).toHaveAttribute("aria-roledescription", "draggable card");
463
+ expect(screen.getByText("Assignee: Ana Lima. Tags: RH")).toBeInTheDocument();
464
+ expect(screen.getByText("1/2 cards")).toBeInTheDocument();
465
+ expect(screen.getByText("0/- cards")).toBeInTheDocument();
466
+ cartao.focus();
467
+ await usuario.keyboard(" ");
468
+ const regiao = container.querySelector('[aria-live="assertive"]')!;
469
+ await waitFor(() => expect(regiao).toHaveTextContent("Grabbed Cartão 1. Column A fazer, 1 of 1."));
470
+ await usuario.keyboard("{ArrowUp}");
471
+ await waitFor(() => expect(regiao).toHaveTextContent("Top of column. Column A fazer, 1 of 1."));
472
+ await usuario.keyboard("{Escape}");
473
+ await waitFor(() => expect(regiao).toHaveTextContent("Cancelled Cartão 1."));
474
+ });
475
+ });
476
+
477
+ describe("KanbanCard e KanbanColumn · avulsos", () => {
478
+ it("padrão em português", () => {
479
+ render(
480
+ <KanbanColumn title="Coluna" count={1}>
481
+ <KanbanCard title="T" assignee="Ana Lima" />
482
+ </KanbanColumn>,
483
+ );
484
+ expect(screen.getByLabelText("Responsável: Ana Lima")).toBeInTheDocument();
485
+ expect(screen.getByText("1 cartão")).toBeInTheDocument();
486
+ });
487
+ it("rótulos customizados", () => {
488
+ render(
489
+ <KanbanColumn title="Column" count={3} countLabel={(n) => `${n} cards`}>
490
+ <KanbanCard title="T" assignee="Ana Lima" assigneeLabel="Assignee" />
491
+ </KanbanColumn>,
492
+ );
493
+ expect(screen.getByLabelText("Assignee: Ana Lima")).toBeInTheDocument();
494
+ expect(screen.getByText("3 cards")).toBeInTheDocument();
495
+ });
496
+ });
497
+
498
+ describe("PhoneInput · copiar e países", () => {
499
+ it("padrão em português", () => {
500
+ render(<PhoneInput value="11 99999-0000" country="ES" />);
501
+ expect(screen.getByRole("button", { name: "Copiar número" })).toHaveAttribute("title", "Copiar");
502
+ expect(screen.getAllByAltText("Espanha").length).toBeGreaterThan(0);
503
+ });
504
+ it("rótulos customizados", () => {
505
+ render(
506
+ <PhoneInput
507
+ value="11 99999-0000"
508
+ country="ES"
509
+ copyLabel="Copy number"
510
+ copyTitle="Copy"
511
+ copiedLabel="Copied!"
512
+ countryNames={{ ES: "Spain" }}
513
+ />,
514
+ );
515
+ expect(screen.getByRole("button", { name: "Copy number" })).toHaveAttribute("title", "Copy");
516
+ expect(screen.getAllByAltText("Spain").length).toBeGreaterThan(0);
517
+ });
518
+ });
519
+
520
+ describe("TimePicker · unidades", () => {
521
+ it("padrão em português", async () => {
522
+ const usuario = userEvent.setup();
523
+ render(<TimePicker label="Hora" format="HH:mm:ss" />);
524
+ await usuario.click(screen.getByRole("combobox"));
525
+ await waitFor(() => expect(screen.getByText("seg")).toBeInTheDocument());
526
+ expect(screen.getByText("min")).toBeInTheDocument();
527
+ expect(screen.getByText("h")).toBeInTheDocument();
528
+ });
529
+ it("rótulos customizados", async () => {
530
+ const usuario = userEvent.setup();
531
+ render(
532
+ <TimePicker
533
+ label="Time"
534
+ format="HH:mm:ss"
535
+ hourUnitLabel="hr"
536
+ minuteUnitLabel="mins"
537
+ secondUnitLabel="sec"
538
+ />,
539
+ );
540
+ await usuario.click(screen.getByRole("combobox"));
541
+ await waitFor(() => expect(screen.getByText("sec")).toBeInTheDocument());
542
+ expect(screen.getByText("mins")).toBeInTheDocument();
543
+ expect(screen.getByText("hr")).toBeInTheDocument();
544
+ });
545
+ });
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
2
  import { render, screen, waitFor } from "@testing-library/react";
3
+ import { renderToString } from "react-dom/server";
3
4
  import userEvent from "@testing-library/user-event";
4
5
  import { faList } from "@fortawesome/free-solid-svg-icons";
5
6
  import SegmentedControl from "../components/SegmentedControl/SegmentedControl";
@@ -181,3 +182,59 @@ describe("SegmentedControl · acessibilidade", () => {
181
182
  expect(new Set(nomes).size).toBe(2);
182
183
  });
183
184
  });
185
+
186
+ /**
187
+ * O id automático era sorteado na inicialização: um no servidor, outro no
188
+ * cliente, e a hidratação divergia no `for`, no `name` e no
189
+ * `aria-labelledby`. Duas renderizações de servidor têm de sair iguais.
190
+ */
191
+ describe("SegmentedControl · ids estáveis", () => {
192
+ it("o HTML do servidor é determinístico", () => {
193
+ const html = () =>
194
+ renderToString(
195
+ <SegmentedControl label="Itens" options={POR_PAGINA} defaultValue="25" />,
196
+ );
197
+ expect(html()).toBe(html());
198
+ });
199
+
200
+ it("com `id`, o servidor já manda os ids e o name derivados dele", () => {
201
+ const html = renderToString(
202
+ <SegmentedControl
203
+ id="por-pagina"
204
+ label="Itens"
205
+ options={POR_PAGINA}
206
+ errorMessage="Escolha um"
207
+ />,
208
+ );
209
+ expect(html).toContain('id="por-pagina-label"');
210
+ expect(html).toContain('for="por-pagina-0"');
211
+ expect(html).toContain('name="por-pagina"');
212
+ expect(html).toContain('aria-describedby="por-pagina-error"');
213
+ });
214
+
215
+ it("sem `id` nem `name`, o cliente gera um name único por grupo", () => {
216
+ render(
217
+ <>
218
+ <SegmentedControl label="A" options={POR_PAGINA} />
219
+ <SegmentedControl label="B" options={POR_PAGINA} />
220
+ </>,
221
+ );
222
+ const [grupoA, grupoB] = screen.getAllByRole("radiogroup");
223
+ const nomeA = grupoA.querySelector("input")?.getAttribute("name");
224
+ const nomeB = grupoB.querySelector("input")?.getAttribute("name");
225
+ expect(nomeA).toBeTruthy();
226
+ expect(nomeB).toBeTruthy();
227
+ expect(nomeA).not.toBe(nomeB);
228
+ expect(screen.getByRole("radiogroup", { name: "A" })).toBe(grupoA);
229
+ });
230
+
231
+ it("`name` explícito continua mandando no agrupamento", () => {
232
+ render(
233
+ <SegmentedControl id="x" name="perPage" label="Itens" options={POR_PAGINA} />,
234
+ );
235
+ expect(screen.getByRole("radio", { name: "25" })).toHaveAttribute(
236
+ "name",
237
+ "perPage",
238
+ );
239
+ });
240
+ });