@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,219 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
3
+ import { renderToString } from "react-dom/server";
4
+ import Tooltip from "../components/Tooltip/Tooltip";
5
+ import Modal from "../components/Modal/Modal";
6
+ import Drawer from "../components/Drawer/Drawer";
7
+
8
+ /**
9
+ * O Tooltip embrulha o gatilho num <span> que não recebe foco. O
10
+ * `aria-describedby` morava nesse span — e o leitor de tela lê a descrição
11
+ * de quem tem o FOCO, o botão. O texto nunca era anunciado. Estes testes
12
+ * guardam que a descrição chega ao elemento focável, que o foco abre a
13
+ * bolha e que o Esc a fecha.
14
+ */
15
+
16
+ describe("Tooltip · descrição no elemento focável", () => {
17
+ it("o botão dentro do Tooltip é descrito pelo texto", () => {
18
+ render(
19
+ <Tooltip content="Salva o documento atual">
20
+ <button type="button">Salvar</button>
21
+ </Tooltip>,
22
+ );
23
+ expect(screen.getByRole("button", { name: "Salvar" })).toHaveAccessibleDescription(
24
+ "Salva o documento atual",
25
+ );
26
+ });
27
+
28
+ it("título e conteúdo entram os dois na descrição", () => {
29
+ render(
30
+ <Tooltip title="Atalho" content="Ctrl+S">
31
+ <button type="button">Salvar</button>
32
+ </Tooltip>,
33
+ );
34
+ expect(screen.getByRole("button")).toHaveAccessibleDescription(
35
+ "Atalho Ctrl+S",
36
+ );
37
+ });
38
+
39
+ it("soma ao aria-describedby que o filho já tinha", () => {
40
+ render(
41
+ <>
42
+ <span id="dica-externa">Obrigatório</span>
43
+ <Tooltip content="Formato DD/MM/AAAA">
44
+ <input aria-label="Data" aria-describedby="dica-externa" />
45
+ </Tooltip>
46
+ </>,
47
+ );
48
+ expect(screen.getByRole("textbox")).toHaveAccessibleDescription(
49
+ "Obrigatório Formato DD/MM/AAAA",
50
+ );
51
+ });
52
+
53
+ it("disabled não descreve", () => {
54
+ render(
55
+ <Tooltip content="Bloqueado" disabled>
56
+ <button type="button">Publicar</button>
57
+ </Tooltip>,
58
+ );
59
+ expect(screen.getByRole("button")).not.toHaveAttribute("aria-describedby");
60
+ });
61
+
62
+ it("ao ficar disabled, o filho volta ao aria-describedby original", () => {
63
+ const { rerender } = render(
64
+ <Tooltip content="Dica">
65
+ <input aria-label="Nome" aria-describedby="erro" />
66
+ </Tooltip>,
67
+ );
68
+ const campo = screen.getByRole("textbox");
69
+ expect(campo.getAttribute("aria-describedby")).toMatch(/^erro umds-tooltip-/);
70
+ rerender(
71
+ <Tooltip content="Dica" disabled>
72
+ <input aria-label="Nome" aria-describedby="erro" />
73
+ </Tooltip>,
74
+ );
75
+ expect(campo).toHaveAttribute("aria-describedby", "erro");
76
+ });
77
+ });
78
+
79
+ describe("Tooltip · foco e Esc", () => {
80
+ it("abre no foco do filho e fecha no Esc", async () => {
81
+ render(
82
+ <Tooltip content="Salva o documento atual">
83
+ <button type="button">Salvar</button>
84
+ </Tooltip>,
85
+ );
86
+ screen.getByRole("button").focus();
87
+ await waitFor(() =>
88
+ expect(screen.getByRole("tooltip")).toHaveClass(
89
+ "umds-tooltip__bubble--open",
90
+ ),
91
+ );
92
+
93
+ fireEvent.keyDown(document.activeElement as Element, { key: "Escape" });
94
+ await waitFor(() =>
95
+ expect(screen.queryByRole("tooltip")).not.toBeInTheDocument(),
96
+ );
97
+ });
98
+
99
+ it("fecha quando o foco sai do gatilho", async () => {
100
+ render(
101
+ <>
102
+ <Tooltip content="Dica">
103
+ <button type="button">Com dica</button>
104
+ </Tooltip>
105
+ <button type="button">Outro</button>
106
+ </>,
107
+ );
108
+ screen.getByRole("button", { name: "Com dica" }).focus();
109
+ await waitFor(() => expect(screen.getByRole("tooltip")).toBeInTheDocument());
110
+ screen.getByRole("button", { name: "Outro" }).focus();
111
+ await waitFor(() =>
112
+ expect(screen.queryByRole("tooltip")).not.toBeInTheDocument(),
113
+ );
114
+ });
115
+ });
116
+
117
+ describe("Tooltip · Esc dentro de overlay", () => {
118
+ /**
119
+ * Modal, Drawer e BottomSheet fecham no Esc escutando o document. Com um
120
+ * tooltip aberto dentro deles, o primeiro Esc tem de fechar SÓ o tooltip:
121
+ * quem aperta Esc para dispensar a dica não espera perder o formulário
122
+ * inteiro. O segundo Esc, já sem tooltip, volta a ser do overlay.
123
+ */
124
+ const abrirDica = async (nome: string) => {
125
+ screen.getByRole("button", { name: nome }).focus();
126
+ await waitFor(() =>
127
+ expect(screen.getByRole("tooltip")).toHaveClass(
128
+ "umds-tooltip__bubble--open",
129
+ ),
130
+ );
131
+ };
132
+
133
+ it("com o tooltip aberto, o Esc fecha a dica e não fecha o Modal", async () => {
134
+ const aoFechar = vi.fn();
135
+ render(
136
+ <Modal open title="Editar processo" onClose={aoFechar}>
137
+ <Tooltip content="Salva sem enviar">
138
+ <button type="button">Salvar rascunho</button>
139
+ </Tooltip>
140
+ </Modal>,
141
+ );
142
+ await abrirDica("Salvar rascunho");
143
+
144
+ fireEvent.keyDown(document.activeElement as Element, { key: "Escape" });
145
+ await waitFor(() =>
146
+ expect(screen.queryByRole("tooltip")).not.toBeInTheDocument(),
147
+ );
148
+ expect(aoFechar).not.toHaveBeenCalled();
149
+ });
150
+
151
+ it("com o tooltip fechado, o Esc segue para o Modal", async () => {
152
+ const aoFechar = vi.fn();
153
+ render(
154
+ <Modal open title="Editar processo" onClose={aoFechar}>
155
+ <Tooltip content="Salva sem enviar">
156
+ <button type="button">Salvar rascunho</button>
157
+ </Tooltip>
158
+ </Modal>,
159
+ );
160
+ await abrirDica("Salvar rascunho");
161
+
162
+ // Primeiro Esc: só a dica.
163
+ fireEvent.keyDown(document.activeElement as Element, { key: "Escape" });
164
+ await waitFor(() =>
165
+ expect(screen.queryByRole("tooltip")).not.toBeInTheDocument(),
166
+ );
167
+ expect(aoFechar).not.toHaveBeenCalled();
168
+
169
+ // Segundo Esc, sem tooltip: agora é do Modal.
170
+ fireEvent.keyDown(document.activeElement as Element, { key: "Escape" });
171
+ expect(aoFechar).toHaveBeenCalledTimes(1);
172
+ });
173
+
174
+ it("com o tooltip aberto, o Esc não fecha o Drawer", async () => {
175
+ const aoFechar = vi.fn();
176
+ render(
177
+ <Drawer open title="Detalhes" onClose={aoFechar}>
178
+ <Tooltip content="Copia o número do processo">
179
+ <button type="button">Copiar</button>
180
+ </Tooltip>
181
+ </Drawer>,
182
+ );
183
+ await abrirDica("Copiar");
184
+
185
+ fireEvent.keyDown(document.activeElement as Element, { key: "Escape" });
186
+ await waitFor(() =>
187
+ expect(screen.queryByRole("tooltip")).not.toBeInTheDocument(),
188
+ );
189
+ expect(aoFechar).not.toHaveBeenCalled();
190
+
191
+ fireEvent.keyDown(document.activeElement as Element, { key: "Escape" });
192
+ expect(aoFechar).toHaveBeenCalledTimes(1);
193
+ });
194
+
195
+ it("tooltip que nunca abriu não engole o Esc", () => {
196
+ const aoFechar = vi.fn();
197
+ render(
198
+ <Modal open title="Editar processo" onClose={aoFechar}>
199
+ <Tooltip content="Salva sem enviar">
200
+ <button type="button">Salvar rascunho</button>
201
+ </Tooltip>
202
+ </Modal>,
203
+ );
204
+ fireEvent.keyDown(document.body, { key: "Escape" });
205
+ expect(aoFechar).toHaveBeenCalledTimes(1);
206
+ });
207
+ });
208
+
209
+ describe("Tooltip · servidor", () => {
210
+ it("o HTML do servidor é determinístico", () => {
211
+ const html = () =>
212
+ renderToString(
213
+ <Tooltip content="Dica">
214
+ <button type="button">Salvar</button>
215
+ </Tooltip>,
216
+ );
217
+ expect(html()).toBe(html());
218
+ });
219
+ });
@@ -0,0 +1,51 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { render, screen, waitFor } from "@testing-library/react";
3
+ import Tooltip from "../components/Tooltip/Tooltip";
4
+
5
+ async function abrir(ui: React.ReactElement) {
6
+ render(ui);
7
+ screen.getByRole("button").focus();
8
+ await waitFor(() => expect(screen.getByRole("tooltip")).toHaveClass("umds-tooltip__bubble--open"));
9
+ return screen.getByRole("tooltip");
10
+ }
11
+
12
+ describe("Tooltip · variant dark e maxWidth", () => {
13
+ it("dark escreve a classe no balão e no wrapper flutuante", async () => {
14
+ const balao = await abrir(
15
+ <Tooltip content="Atualizar" variant="dark">
16
+ <button type="button">Atualizar</button>
17
+ </Tooltip>,
18
+ );
19
+ expect(balao).toHaveClass("umds-tooltip__bubble--dark");
20
+ expect(balao.parentElement).toHaveClass("umds-tooltip__floating--dark");
21
+ });
22
+
23
+ it("sem variant o balão é o padrão", async () => {
24
+ const balao = await abrir(
25
+ <Tooltip content="Atualizar">
26
+ <button type="button">Atualizar</button>
27
+ </Tooltip>,
28
+ );
29
+ expect(balao).not.toHaveClass("umds-tooltip__bubble--dark");
30
+ expect(balao.parentElement).not.toHaveClass("umds-tooltip__floating--dark");
31
+ expect(balao.parentElement?.style.maxWidth).toBe("");
32
+ });
33
+
34
+ it("maxWidth vai ao wrapper flutuante, em qualquer variante", async () => {
35
+ const balao = await abrir(
36
+ <Tooltip content="Texto longo" variant="dark" maxWidth="140px">
37
+ <button type="button">x</button>
38
+ </Tooltip>,
39
+ );
40
+ expect(balao.parentElement?.style.maxWidth).toBe("140px");
41
+ });
42
+
43
+ it("a descrição acessível não muda com a variante", () => {
44
+ render(
45
+ <Tooltip content="Atualizar" variant="dark">
46
+ <button type="button">Recarregar</button>
47
+ </Tooltip>,
48
+ );
49
+ expect(screen.getByRole("button")).toHaveAccessibleDescription("Atualizar");
50
+ });
51
+ });
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Datas do `Calendar`, em ISO local (`YYYY-MM-DD`) e sem biblioteca.
3
+ *
4
+ * Tudo aqui trabalha com o DIA como texto: `"2026-05-15"`. O `Date` só aparece
5
+ * por dentro, sempre à meia-noite LOCAL (`new Date(ano, mes, dia)`), nunca
6
+ * `new Date("2026-05-15")`, que o JavaScript lê como UTC e, no fuso do Brasil,
7
+ * devolve o dia anterior.
8
+ */
9
+
10
+ const doisDigitos = (n: number): string => n < 10 ? "0" + n : String(n);
11
+
12
+ /** `Date` local → `YYYY-MM-DD`. */
13
+ export const toIso = (data: Date): string => data.getFullYear() + "-" + doisDigitos(data.getMonth() + 1) + "-" + doisDigitos(data.getDate());
14
+
15
+ /** `YYYY-MM-DD` → `Date` à meia-noite local. */
16
+ export const parseIso = (iso: string): Date => {
17
+ const partes = iso.slice(0, 10).split("-");
18
+ return new Date(Number(partes[0]), Number(partes[1]) - 1, Number(partes[2]));
19
+ };
20
+
21
+ /** Aceita `Date` ou texto (`2026-05-15` e `2026-05-15T10:00`) e devolve `YYYY-MM-DD`; "" se não der. */
22
+ export const normalizeIso = (valor: any): string => {
23
+ if (!valor) return "";
24
+ if (typeof valor === "string") return /^\d{4}-\d{2}-\d{2}/.test(valor) ? valor.slice(0, 10) : "";
25
+ if (valor instanceof Date && !isNaN(valor.getTime())) return toIso(valor);
26
+ return "";
27
+ };
28
+ export const addDays = (iso: string, dias: number): string => {
29
+ const data = parseIso(iso);
30
+ data.setDate(data.getDate() + dias);
31
+ return toIso(data);
32
+ };
33
+
34
+ /** Soma meses mantendo o dia, ou o último dia do mês de chegada (31/01 + 1 mês = 28/02). */
35
+ export const addMonths = (iso: string, meses: number): string => {
36
+ const data = parseIso(iso);
37
+ const dia = data.getDate();
38
+ data.setDate(1);
39
+ data.setMonth(data.getMonth() + meses);
40
+ const ultimo = new Date(data.getFullYear(), data.getMonth() + 1, 0).getDate();
41
+ data.setDate(Math.min(dia, ultimo));
42
+ return toIso(data);
43
+ };
44
+
45
+ /** Dia da semana: 0 = domingo. */
46
+ export const weekdayOf = (iso: string): number => parseIso(iso).getDay();
47
+
48
+ /** Primeiro dia da semana que contém `iso`, dado o dia em que a semana começa (0 = domingo, 1 = segunda). */
49
+ export const startOfWeek = (iso: string, comecaEm: number): string => {
50
+ const recuo = (weekdayOf(iso) - comecaEm + 7) % 7;
51
+ return addDays(iso, -recuo);
52
+ };
53
+ export const startOfMonth = (iso: string): string => iso.slice(0, 8) + "01";
54
+ export const endOfMonth = (iso: string): string => {
55
+ const data = parseIso(iso);
56
+ return toIso(new Date(data.getFullYear(), data.getMonth() + 1, 0));
57
+ };
58
+ export const isSameMonth = (a: string, b: string): boolean => a.slice(0, 7) === b.slice(0, 7);
59
+
60
+ /** Os sete dias da semana que contém `iso`. */
61
+ export const weekDays = (iso: string, comecaEm: number): string[] => {
62
+ const inicio = startOfWeek(iso, comecaEm);
63
+ const dias: string[] = [];
64
+ for (let i = 0; i < 7; i++) dias.push(addDays(inicio, i));
65
+ return dias;
66
+ };
67
+
68
+ /** O mês de `iso` em semanas inteiras (de 4 a 6 linhas), com os dias dos meses vizinhos que completam as pontas. */
69
+ export const monthMatrix = (iso: string, comecaEm: number): string[][] => {
70
+ const ultimo = endOfMonth(iso);
71
+ let cursor = startOfWeek(startOfMonth(iso), comecaEm);
72
+ const semanas: string[][] = [];
73
+ while (cursor <= ultimo) {
74
+ semanas.push(weekDays(cursor, comecaEm));
75
+ cursor = addDays(cursor, 7);
76
+ }
77
+ return semanas;
78
+ }
@@ -490,6 +490,10 @@ export interface ChartEntradaCartesiana extends ChartFormato {
490
490
  ariaLabel?: string;
491
491
  showLegend?: any;
492
492
  showGrid?: any;
493
+ /** Nome do tipo de gráfico (legenda da tabela sem título e começo do resumo automático). */
494
+ typeLabel?: string;
495
+ /** Nome da lista de alvos focáveis; o título, se houver, vem depois de " — ". */
496
+ valuesListLabel?: string;
493
497
  }
494
498
  export interface ChartGradeLinha {
495
499
  chave: string;
@@ -564,7 +568,8 @@ export function modeloCartesiano(tipo: "barras" | "linhas", entrada: ChartEntrad
564
568
  serie: j + 1,
565
569
  nome: s.name
566
570
  }));
567
- const nomeDoTipo = tipo === "barras" ? "Gráfico de barras" : "Gráfico de linhas";
571
+ const nomeDoTipo = entrada.typeLabel || (tipo === "barras" ? "Gráfico de barras" : "Gráfico de linhas");
572
+ const nomeDaLista = entrada.valuesListLabel || "Valores por categoria";
568
573
  const base: ChartModeloCartesiano = {
569
574
  vazio: true,
570
575
  viewBox: `0 0 ${largo} ${alto}`,
@@ -585,7 +590,7 @@ export function modeloCartesiano(tipo: "barras" | "linhas", entrada: ChartEntrad
585
590
  pontos: [],
586
591
  legenda,
587
592
  resumo: entrada.ariaLabel || `${nomeDoTipo} sem dados.`,
588
- rotuloDaLista: entrada.title ? `Valores por categoria — ${entrada.title}` : "Valores por categoria",
593
+ rotuloDaLista: entrada.title ? `${nomeDaLista} — ${entrada.title}` : nomeDaLista,
589
594
  legendaDaTabela: entrada.title || nomeDoTipo,
590
595
  cabecalhos: legenda,
591
596
  tabela: []
@@ -0,0 +1,94 @@
1
+ /**
2
+ * HTML seguro para o `RichTextEditor`.
3
+ *
4
+ * O editor devolve HTML (`onChange`) e recebe HTML (`value`). HTML que vem de
5
+ * fora (um valor guardado, um texto colado) pode trazer `<img onerror>`,
6
+ * `<a href="javascript:...">`, `<script>`, `style` e classes de outro site. Este
7
+ * módulo reduz o HTML ao que o editor sabe escrever: negrito, itálico,
8
+ * sublinhado, tachado, listas, parágrafos, quebras, títulos, citação e link.
9
+ *
10
+ * Roda no navegador (precisa de DOM). Dentro de um `<template>` o HTML é inerte:
11
+ * nada executa e nenhuma imagem carrega enquanto se higieniza. Sem DOM
12
+ * (servidor), devolve o texto escapado.
13
+ */
14
+
15
+ const PERMITIDAS = ["A", "B", "STRONG", "I", "EM", "U", "S", "STRIKE", "DEL", "UL", "OL", "LI", "P", "BR", "DIV", "SPAN", "H1", "H2", "H3", "H4", "BLOCKQUOTE"];
16
+ const DESCARTADAS = ["SCRIPT", "STYLE", "IFRAME", "OBJECT", "EMBED", "LINK", "META", "TEMPLATE", "NOSCRIPT", "SVG", "MATH", "FORM", "INPUT", "BUTTON", "TEXTAREA", "SELECT"];
17
+ const escapar = (texto: string): string => texto.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
18
+
19
+ /**
20
+ * Endereço de link que o editor aceita: `http:`, `https:`, `mailto:`, `tel:`,
21
+ * caminho (`/x`, `./x`, `../x`) ou âncora (`#x`). Devolve o endereço limpo, ou ""
22
+ * se for perigoso (`javascript:`, `data:`, `vbscript:`...).
23
+ */
24
+ export const enderecoSeguro = (bruto: string): string => {
25
+ const texto = (bruto || "").trim();
26
+ if (!texto) return "";
27
+ // Caracteres de controle e espaços no meio do esquema são o truque clássico (`java\nscript:`).
28
+ const semControle = texto.replace(/[\u0000-\u001f\u007f\s]+/g, "");
29
+ if (/^(https?:\/\/|mailto:|tel:|\/|\.\.?\/|#)/i.test(semControle)) return texto;
30
+ if (/^[a-z][a-z0-9+.-]*:/i.test(semControle)) return "";
31
+ return "";
32
+ };
33
+
34
+ /** Devolve o endereço pronto para `href`: o seguro como veio, ou `https://` + o texto quando não tem esquema. "" se perigoso ou vazio. */
35
+ export const normalizarEndereco = (bruto: string): string => {
36
+ const texto = (bruto || "").trim();
37
+ if (!texto) return "";
38
+ const seguro = enderecoSeguro(texto);
39
+ if (seguro) return seguro;
40
+ const semControle = texto.replace(/[\u0000-\u001f\u007f\s]+/g, "");
41
+ if (/^[a-z][a-z0-9+.-]*:/i.test(semControle)) return "";
42
+ return "https://" + texto;
43
+ };
44
+ const limparNo = (no: any, saida: any, documento: any): void => {
45
+ const filhos = Array.prototype.slice.call(no.childNodes);
46
+ for (let i = 0; i < filhos.length; i++) {
47
+ const filho = filhos[i];
48
+ if (filho.nodeType === 3) {
49
+ saida.appendChild(documento.createTextNode(filho.nodeValue));
50
+ continue;
51
+ }
52
+ if (filho.nodeType !== 1) continue;
53
+ const tag = String(filho.tagName).toUpperCase();
54
+ if (DESCARTADAS.indexOf(tag) !== -1) continue;
55
+ if (PERMITIDAS.indexOf(tag) === -1) {
56
+ // Tag desconhecida: some a tag, fica o conteúdo.
57
+ limparNo(filho, saida, documento);
58
+ continue;
59
+ }
60
+ const novo = documento.createElement(tag.toLowerCase());
61
+ if (tag === "A") {
62
+ const href = enderecoSeguro(filho.getAttribute("href") || "");
63
+ if (href) {
64
+ novo.setAttribute("href", href);
65
+ novo.setAttribute("rel", "noopener noreferrer");
66
+ }
67
+ }
68
+ limparNo(filho, novo, documento);
69
+ saida.appendChild(novo);
70
+ }
71
+ };
72
+
73
+ /** Reduz `html` ao que o editor escreve. Sem DOM, devolve o texto escapado. */
74
+ export const sanitizarHtml = (html: string): string => {
75
+ const bruto = html || "";
76
+ if (typeof document === "undefined") return escapar(bruto);
77
+ const molde: any = document.createElement("template");
78
+ molde.innerHTML = bruto;
79
+ const saida: any = document.createElement("div");
80
+ limparNo(molde.content, saida, document);
81
+ return saida.innerHTML;
82
+ };
83
+
84
+ /** O editor está "vazio": sem texto, sem lista e sem quebra solta (o `<br>` que o navegador deixa). */
85
+ export const htmlVazio = (html: string): boolean => {
86
+ const texto = (html || "").replace(/<br\s*\/?>/gi, "").replace(/<\/?(div|p|span)[^>]*>/gi, "").trim();
87
+ return texto === "";
88
+ };
89
+
90
+ /** O `<a>` que o editor escreve quando o link não tem texto escolhido: o endereço (já seguro) é o href e o texto. */
91
+ export const htmlDoLink = (endereco: string): string => {
92
+ const seguro = escapar(endereco);
93
+ return '<a href="' + seguro + '">' + seguro + "</a>";
94
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Ids automáticos que não quebram a hidratação SSR.
3
+ *
4
+ * O padrão antigo da biblioteca sorteava o id na inicialização do store
5
+ * (`"umds-x-" + Math.random()`). No servidor sai um número, no cliente sai
6
+ * outro, e o React/Vue reclamam de hidratação divergente — e, pior, o
7
+ * `aria-describedby` que o servidor mandou aponta para um id que o cliente
8
+ * trocou.
9
+ *
10
+ * A regra agora: o componente começa SEM id automático (servidor e primeira
11
+ * renderização do cliente saem idênticos) e chama `gerarId` no `onMount`,
12
+ * que só roda no navegador. O contador é de módulo, mas como só avança no
13
+ * cliente não acumula entre requisições do servidor. Quem precisa do id já
14
+ * no HTML do servidor passa a prop `id`.
15
+ *
16
+ * Singleton por bundle, compartilhado entre os componentes gerados.
17
+ */
18
+ let contador = 0;
19
+
20
+ /** Próximo id da página para o prefixo dado, ex.: `umds-select-3`. */
21
+ export const gerarId = (prefixo: string): string => {
22
+ contador += 1;
23
+ return prefixo + "-" + contador;
24
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Paleta de cor categórica do 1DS: os primitivos `--umds-primitive-<família>-<tom>`.
3
+ *
4
+ * É a lista que o `ColorPicker` mostra por padrão. Ela existe como constante
5
+ * exportada para o app mapear o valor guardado (o nome do token) de volta para
6
+ * um rótulo, ou montar a própria lista a partir dela (`COLOR_PALETTE.filter(...)`).
7
+ *
8
+ * Primitivo não muda com o tema: é o certo para cor de DADO (marcador, tipo de
9
+ * documento, setor), que precisa ser a mesma no claro e no escuro. Para cor com
10
+ * PAPEL (primário, erro, sucesso) use os semânticos `--umds-semantic-color-*`,
11
+ * que não entram aqui.
12
+ */
13
+ export type PaletteColor = {
14
+ /** Nome da variável CSS, ex.: `--umds-primitive-blue-pure`. Use como `var(${value})`. */
15
+ value: string;
16
+ /** Nome em português, ex.: "Azul escuro". */
17
+ label: string;
18
+ /** Família: blue, red, green, orange, yellow, purple ou gray. */
19
+ family: string;
20
+ /** Tom claro o bastante para o ícone de marcação sair escuro (contraste mínimo de 3:1). */
21
+ onLight: boolean;
22
+ };
23
+ const cor = (family: string, tone: string, label: string, onLight: boolean): PaletteColor => ({
24
+ value: "--umds-primitive-" + family + "-" + tone,
25
+ label,
26
+ family,
27
+ onLight
28
+ });
29
+
30
+ /** Os 31 primitivos de cor (6 famílias x 4 tons + 7 cinzas), do mais escuro ao mais claro. */
31
+ export const COLOR_PALETTE: PaletteColor[] = [cor("blue", "dark", "Azul escuro", false), cor("blue", "pure", "Azul", false), cor("blue", "medium", "Azul médio", false), cor("blue", "light", "Azul claro", true), cor("red", "dark", "Vermelho escuro", false), cor("red", "pure", "Vermelho", false), cor("red", "medium", "Vermelho médio", false), cor("red", "light", "Vermelho claro", true), cor("green", "dark", "Verde escuro", false), cor("green", "pure", "Verde", false), cor("green", "medium", "Verde médio", false), cor("green", "light", "Verde claro", true), cor("orange", "dark", "Laranja escuro", false), cor("orange", "pure", "Laranja", false), cor("orange", "medium", "Laranja médio", true), cor("orange", "light", "Laranja claro", true), cor("yellow", "dark", "Amarelo escuro", false), cor("yellow", "pure", "Amarelo", true), cor("yellow", "medium", "Amarelo médio", true), cor("yellow", "light", "Amarelo claro", true), cor("purple", "dark", "Roxo escuro", false), cor("purple", "pure", "Roxo", false), cor("purple", "medium", "Roxo médio", false), cor("purple", "light", "Roxo claro", true), cor("gray", "darkest", "Cinza muito escuro", false), cor("gray", "darker", "Cinza escuro", false), cor("gray", "dark", "Cinza", false), cor("gray", "medium", "Cinza médio", true), cor("gray", "light", "Cinza claro", true), cor("gray", "lighter", "Cinza mais claro", true), cor("gray", "lightest", "Cinza muito claro", true)];
32
+
33
+ /** `#RRGGBB` completo (o `#` é opcional). Hexadecimal curto (`#FFF`) não vale. */
34
+ export const isHexColor = (texto: string): boolean => /^#?[0-9a-fA-F]{6}$/.test((texto || "").trim())
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Texto para comparar: minúsculo, sem acento e sem espaço nas pontas. É o que a
3
+ * busca do `IconPicker` usa para achar "usuario" em "Usuário".
4
+ */
5
+ export const semAcento = (texto: string): string => String(texto || "").normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().trim()