@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,57 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { describe, expect, it, vi } from "vitest";
3
+ import { fireEvent, render, screen } from "@testing-library/react";
4
+ import Stack from "../components/Stack/Stack";
5
+ const TAGS = ["div", "span", "ul", "ol", "li", "section", "nav", "header", "footer", "form"];
6
+ describe("Stack", () => {
7
+ it("sem props continua um div vertical com gap xxxs", () => {
8
+ const { container } = render(_jsx(Stack, { children: "x" }));
9
+ const el = container.firstElementChild;
10
+ expect(el.tagName).toBe("DIV");
11
+ expect(el.className).toBe("umds-stack umds-stack--vertical umds-stack--gap-xxxs");
12
+ });
13
+ it("aceita gap none", () => {
14
+ const { container } = render(_jsx(Stack, { gap: "none", children: "x" }));
15
+ expect(container.firstElementChild).toHaveClass("umds-stack--gap-none");
16
+ });
17
+ it.each(TAGS)("as=%s renderiza o elemento certo", (tag) => {
18
+ const { container } = render(_jsx(Stack, { as: tag, children: "x" }));
19
+ expect(container.firstElementChild?.tagName.toLowerCase()).toBe(tag);
20
+ });
21
+ it("ul e ol mantêm a semântica de lista", () => {
22
+ const { container } = render(_jsx(Stack, { as: "ul", "aria-label": "Itens", children: _jsx(Stack, { as: "li", children: "um" }) }));
23
+ expect(screen.getByRole("list", { name: "Itens" })).toBeInTheDocument();
24
+ expect(container.querySelectorAll("li")).toHaveLength(1);
25
+ });
26
+ it("repassa id, aria-label, title e style ao elemento raiz", () => {
27
+ const { container } = render(_jsx(Stack, { as: "nav", id: "menu", "aria-label": "Principal", title: "Dica", style: { marginTop: 4 }, children: "x" }));
28
+ const el = container.firstElementChild;
29
+ expect(el.id).toBe("menu");
30
+ expect(el.getAttribute("aria-label")).toBe("Principal");
31
+ expect(el.getAttribute("title")).toBe("Dica");
32
+ expect(el.style.marginTop).toBe("4px");
33
+ expect(screen.getByRole("navigation", { name: "Principal" })).toBe(el);
34
+ });
35
+ it("as=form chama onSubmit", () => {
36
+ const aoEnviar = vi.fn((e) => e.preventDefault());
37
+ const { container } = render(_jsx(Stack, { as: "form", onSubmit: aoEnviar, children: _jsx("button", { type: "submit", children: "Enviar" }) }));
38
+ fireEvent.submit(container.querySelector("form"));
39
+ expect(aoEnviar).toHaveBeenCalledTimes(1);
40
+ });
41
+ it("escreve as classes de item flex", () => {
42
+ const { container } = render(_jsx(Stack, { grow: true, shrink: "0", basis: "50", minWidth: "auto", overflow: "hidden", overflowY: "auto", fullHeight: true, children: "x" }));
43
+ const el = container.firstElementChild;
44
+ for (const c of [
45
+ "umds-stack--grow",
46
+ "umds-stack--shrink-0",
47
+ "umds-stack--basis-50",
48
+ "umds-stack--min-w-auto",
49
+ "umds-stack--overflow-hidden",
50
+ "umds-stack--overflow-y-auto",
51
+ "umds-stack--full-height",
52
+ ]) {
53
+ expect(el).toHaveClass(c);
54
+ }
55
+ expect(el).not.toHaveClass("umds-stack--full-width");
56
+ });
57
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { describe, expect, it } from "vitest";
3
+ import { render, screen } from "@testing-library/react";
4
+ import StatCard from "../components/StatCard/StatCard";
5
+ const raiz = (c) => c.firstElementChild;
6
+ describe("StatCard · labelPosition", () => {
7
+ it("top escreve a classe; sem a prop o cartão é o de antes", () => {
8
+ const { container } = render(_jsxs(_Fragment, { children: [_jsx(StatCard, { value: 44, label: "N\u00E3o lidos", labelPosition: "top" }), _jsx(StatCard, { value: 44, label: "N\u00E3o lidos" })] }));
9
+ expect(container.children[0]).toHaveClass("umds-stat-card--rotulo-topo");
10
+ expect(container.children[1]).not.toHaveClass("umds-stat-card--rotulo-topo");
11
+ });
12
+ it("o número continua antes do rótulo no HTML (ordem do leitor de tela)", () => {
13
+ const { container } = render(_jsx(StatCard, { value: 44, label: "N\u00E3o lidos", labelPosition: "top" }));
14
+ const valor = container.querySelector(".umds-stat-card__valor");
15
+ const rotulo = container.querySelector(".umds-stat-card__rotulo");
16
+ expect(valor.compareDocumentPosition(rotulo) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
17
+ });
18
+ });
19
+ describe("StatCard · infoTooltip", () => {
20
+ it("mostra o ⓘ como botão nomeado, dentro do rótulo, descrito pelo texto de ajuda", () => {
21
+ const { container } = render(_jsx(StatCard, { value: 44, label: "N\u00E3o lidos", infoTooltip: "Ainda n\u00E3o abertos por voc\u00EA." }));
22
+ const info = screen.getByRole("button", { name: "Mais informações" });
23
+ expect(container.querySelector(".umds-stat-card__rotulo")?.contains(info)).toBe(true);
24
+ expect(info).toHaveAccessibleDescription("Ainda não abertos por você.");
25
+ });
26
+ it("infoLabel traduz o nome do ⓘ", () => {
27
+ render(_jsx(StatCard, { value: 1, label: "Unread", infoTooltip: "Not opened yet.", infoLabel: "More info" }));
28
+ expect(screen.getByRole("button", { name: "More info" })).toBeInTheDocument();
29
+ });
30
+ it("sem infoTooltip não há botão", () => {
31
+ render(_jsx(StatCard, { value: 1, label: "x" }));
32
+ expect(screen.queryByRole("button")).toBeNull();
33
+ });
34
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,79 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { describe, expect, it, vi } from "vitest";
3
+ import { fireEvent, render, screen } from "@testing-library/react";
4
+ import Tabs from "../components/Tabs/Tabs";
5
+ import Tab from "../components/Tabs/Tab";
6
+ describe("Tab · count", () => {
7
+ it("mostra o contador em pílula e ele entra no nome da aba", () => {
8
+ render(_jsxs(Tabs, { value: "0", ariaLabel: "Tarefas", children: [_jsx(Tab, { label: "Todas", index: "0", count: 31 }), _jsx(Tab, { label: "Atribu\u00EDdas", index: "1", count: 9 })] }));
9
+ expect(screen.getByRole("tab", { name: "Todas 31" })).toBeInTheDocument();
10
+ expect(screen.getByRole("tab", { name: "Atribuídas 9" })).toBeInTheDocument();
11
+ });
12
+ it("o 0 aparece; sem count, não há pílula", () => {
13
+ const { container } = render(_jsxs(Tabs, { value: "0", children: [_jsx(Tab, { label: "Conclu\u00EDdas", index: "0", count: 0 }), _jsx(Tab, { label: "Sem contador", index: "1" })] }));
14
+ const pilulas = container.querySelectorAll(".umds-tabs__count");
15
+ expect(pilulas).toHaveLength(1);
16
+ expect(pilulas[0].textContent).toBe("0");
17
+ });
18
+ it("a pílula fica dentro da aba selecionada (que a pinta de azul pelo CSS)", () => {
19
+ const { container } = render(_jsxs(Tabs, { value: "1", children: [_jsx(Tab, { label: "A", index: "0", count: 1 }), _jsx(Tab, { label: "B", index: "1", count: 2 })] }));
20
+ const selecionada = container.querySelector(".umds-tabs__tab--selected");
21
+ expect(selecionada?.querySelector(".umds-tabs__count")?.textContent).toBe("2");
22
+ });
23
+ });
24
+ describe("Tab · closable", () => {
25
+ it("o × é um botão irmão da aba, com o nome 'Fechar <aba>'", () => {
26
+ render(_jsx(Tabs, { value: "0", children: _jsx(Tab, { label: "Documento 1", index: "0", closable: true }) }));
27
+ const fechar = screen.getByRole("button", { name: "Fechar Documento 1" });
28
+ const aba = screen.getByRole("tab");
29
+ expect(aba.contains(fechar)).toBe(false);
30
+ expect(fechar.parentElement).toBe(aba.parentElement);
31
+ });
32
+ it("o rótulo do × vem por prop", () => {
33
+ render(_jsx(Tabs, { value: "0", children: _jsx(Tab, { label: "Doc 1", index: "0", closable: true, closeLabel: "Close" }) }));
34
+ expect(screen.getByRole("button", { name: "Close Doc 1" })).toBeInTheDocument();
35
+ });
36
+ it("clicar no × chama onClose com o index e não troca de aba", () => {
37
+ const aoFechar = vi.fn();
38
+ const aoTrocar = vi.fn();
39
+ render(_jsxs(Tabs, { value: "0", onChange: aoTrocar, children: [_jsx(Tab, { label: "Um", index: "0" }), _jsx(Tab, { label: "Dois", index: "1", closable: true, onClose: aoFechar })] }));
40
+ fireEvent.click(screen.getByRole("button", { name: "Fechar Dois" }));
41
+ expect(aoFechar).toHaveBeenCalledTimes(1);
42
+ expect(aoFechar.mock.calls[0][0]).toBe("1");
43
+ expect(aoTrocar).not.toHaveBeenCalled();
44
+ });
45
+ it("Delete na aba focada fecha; sem closable, não", () => {
46
+ const aoFechar = vi.fn();
47
+ render(_jsxs(Tabs, { value: "0", children: [_jsx(Tab, { label: "Fech\u00E1vel", index: "0", closable: true, onClose: aoFechar }), _jsx(Tab, { label: "Fixa", index: "1", onClose: aoFechar })] }));
48
+ fireEvent.keyDown(screen.getByRole("tab", { name: "Fechável" }), { key: "Delete" });
49
+ expect(aoFechar).toHaveBeenCalledTimes(1);
50
+ fireEvent.keyDown(screen.getByRole("tab", { name: "Fixa" }), { key: "Delete" });
51
+ expect(aoFechar).toHaveBeenCalledTimes(1);
52
+ });
53
+ it("sem closable não há ×", () => {
54
+ render(_jsx(Tabs, { value: "0", children: _jsx(Tab, { label: "Fixa", index: "0" }) }));
55
+ expect(screen.queryByRole("button")).toBeNull();
56
+ });
57
+ it("o tablist só tem abas como filhas de papel (o × não é tab)", () => {
58
+ render(_jsxs(Tabs, { value: "0", children: [_jsx(Tab, { label: "A", index: "0", closable: true }), _jsx(Tab, { label: "B", index: "1", closable: true })] }));
59
+ expect(screen.getAllByRole("tab")).toHaveLength(2);
60
+ });
61
+ });
62
+ describe("Tabs · onAdd", () => {
63
+ it("mostra o + fora do tablist e chama onAdd", () => {
64
+ const aoAdicionar = vi.fn();
65
+ render(_jsx(Tabs, { value: "0", onAdd: aoAdicionar, children: _jsx(Tab, { label: "A", index: "0" }) }));
66
+ const mais = screen.getByRole("button", { name: "Adicionar aba" });
67
+ expect(screen.getByRole("tablist").contains(mais)).toBe(false);
68
+ fireEvent.click(mais);
69
+ expect(aoAdicionar).toHaveBeenCalledTimes(1);
70
+ });
71
+ it("o rótulo do + vem por prop", () => {
72
+ render(_jsx(Tabs, { value: "0", onAdd: () => { }, addLabel: "New tab", children: _jsx(Tab, { label: "A", index: "0" }) }));
73
+ expect(screen.getByRole("button", { name: "New tab" })).toBeInTheDocument();
74
+ });
75
+ it("sem onAdd não há botão", () => {
76
+ render(_jsx(Tabs, { value: "0", children: _jsx(Tab, { label: "A", index: "0" }) }));
77
+ expect(screen.queryByRole("button")).toBeNull();
78
+ });
79
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,109 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { describe, expect, it, vi } from "vitest";
3
+ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
4
+ import { renderToString } from "react-dom/server";
5
+ import Tooltip from "../components/Tooltip/Tooltip";
6
+ import Modal from "../components/Modal/Modal";
7
+ import Drawer from "../components/Drawer/Drawer";
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
+ describe("Tooltip · descrição no elemento focável", () => {
16
+ it("o botão dentro do Tooltip é descrito pelo texto", () => {
17
+ render(_jsx(Tooltip, { content: "Salva o documento atual", children: _jsx("button", { type: "button", children: "Salvar" }) }));
18
+ expect(screen.getByRole("button", { name: "Salvar" })).toHaveAccessibleDescription("Salva o documento atual");
19
+ });
20
+ it("título e conteúdo entram os dois na descrição", () => {
21
+ render(_jsx(Tooltip, { title: "Atalho", content: "Ctrl+S", children: _jsx("button", { type: "button", children: "Salvar" }) }));
22
+ expect(screen.getByRole("button")).toHaveAccessibleDescription("Atalho Ctrl+S");
23
+ });
24
+ it("soma ao aria-describedby que o filho já tinha", () => {
25
+ render(_jsxs(_Fragment, { children: [_jsx("span", { id: "dica-externa", children: "Obrigat\u00F3rio" }), _jsx(Tooltip, { content: "Formato DD/MM/AAAA", children: _jsx("input", { "aria-label": "Data", "aria-describedby": "dica-externa" }) })] }));
26
+ expect(screen.getByRole("textbox")).toHaveAccessibleDescription("Obrigatório Formato DD/MM/AAAA");
27
+ });
28
+ it("disabled não descreve", () => {
29
+ render(_jsx(Tooltip, { content: "Bloqueado", disabled: true, children: _jsx("button", { type: "button", children: "Publicar" }) }));
30
+ expect(screen.getByRole("button")).not.toHaveAttribute("aria-describedby");
31
+ });
32
+ it("ao ficar disabled, o filho volta ao aria-describedby original", () => {
33
+ const { rerender } = render(_jsx(Tooltip, { content: "Dica", children: _jsx("input", { "aria-label": "Nome", "aria-describedby": "erro" }) }));
34
+ const campo = screen.getByRole("textbox");
35
+ expect(campo.getAttribute("aria-describedby")).toMatch(/^erro umds-tooltip-/);
36
+ rerender(_jsx(Tooltip, { content: "Dica", disabled: true, children: _jsx("input", { "aria-label": "Nome", "aria-describedby": "erro" }) }));
37
+ expect(campo).toHaveAttribute("aria-describedby", "erro");
38
+ });
39
+ });
40
+ describe("Tooltip · foco e Esc", () => {
41
+ it("abre no foco do filho e fecha no Esc", async () => {
42
+ render(_jsx(Tooltip, { content: "Salva o documento atual", children: _jsx("button", { type: "button", children: "Salvar" }) }));
43
+ screen.getByRole("button").focus();
44
+ await waitFor(() => expect(screen.getByRole("tooltip")).toHaveClass("umds-tooltip__bubble--open"));
45
+ fireEvent.keyDown(document.activeElement, { key: "Escape" });
46
+ await waitFor(() => expect(screen.queryByRole("tooltip")).not.toBeInTheDocument());
47
+ });
48
+ it("fecha quando o foco sai do gatilho", async () => {
49
+ render(_jsxs(_Fragment, { children: [_jsx(Tooltip, { content: "Dica", children: _jsx("button", { type: "button", children: "Com dica" }) }), _jsx("button", { type: "button", children: "Outro" })] }));
50
+ screen.getByRole("button", { name: "Com dica" }).focus();
51
+ await waitFor(() => expect(screen.getByRole("tooltip")).toBeInTheDocument());
52
+ screen.getByRole("button", { name: "Outro" }).focus();
53
+ await waitFor(() => expect(screen.queryByRole("tooltip")).not.toBeInTheDocument());
54
+ });
55
+ });
56
+ describe("Tooltip · Esc dentro de overlay", () => {
57
+ /**
58
+ * Modal, Drawer e BottomSheet fecham no Esc escutando o document. Com um
59
+ * tooltip aberto dentro deles, o primeiro Esc tem de fechar SÓ o tooltip:
60
+ * quem aperta Esc para dispensar a dica não espera perder o formulário
61
+ * inteiro. O segundo Esc, já sem tooltip, volta a ser do overlay.
62
+ */
63
+ const abrirDica = async (nome) => {
64
+ screen.getByRole("button", { name: nome }).focus();
65
+ await waitFor(() => expect(screen.getByRole("tooltip")).toHaveClass("umds-tooltip__bubble--open"));
66
+ };
67
+ it("com o tooltip aberto, o Esc fecha a dica e não fecha o Modal", async () => {
68
+ const aoFechar = vi.fn();
69
+ render(_jsx(Modal, { open: true, title: "Editar processo", onClose: aoFechar, children: _jsx(Tooltip, { content: "Salva sem enviar", children: _jsx("button", { type: "button", children: "Salvar rascunho" }) }) }));
70
+ await abrirDica("Salvar rascunho");
71
+ fireEvent.keyDown(document.activeElement, { key: "Escape" });
72
+ await waitFor(() => expect(screen.queryByRole("tooltip")).not.toBeInTheDocument());
73
+ expect(aoFechar).not.toHaveBeenCalled();
74
+ });
75
+ it("com o tooltip fechado, o Esc segue para o Modal", async () => {
76
+ const aoFechar = vi.fn();
77
+ render(_jsx(Modal, { open: true, title: "Editar processo", onClose: aoFechar, children: _jsx(Tooltip, { content: "Salva sem enviar", children: _jsx("button", { type: "button", children: "Salvar rascunho" }) }) }));
78
+ await abrirDica("Salvar rascunho");
79
+ // Primeiro Esc: só a dica.
80
+ fireEvent.keyDown(document.activeElement, { key: "Escape" });
81
+ await waitFor(() => expect(screen.queryByRole("tooltip")).not.toBeInTheDocument());
82
+ expect(aoFechar).not.toHaveBeenCalled();
83
+ // Segundo Esc, sem tooltip: agora é do Modal.
84
+ fireEvent.keyDown(document.activeElement, { key: "Escape" });
85
+ expect(aoFechar).toHaveBeenCalledTimes(1);
86
+ });
87
+ it("com o tooltip aberto, o Esc não fecha o Drawer", async () => {
88
+ const aoFechar = vi.fn();
89
+ render(_jsx(Drawer, { open: true, title: "Detalhes", onClose: aoFechar, children: _jsx(Tooltip, { content: "Copia o n\u00FAmero do processo", children: _jsx("button", { type: "button", children: "Copiar" }) }) }));
90
+ await abrirDica("Copiar");
91
+ fireEvent.keyDown(document.activeElement, { key: "Escape" });
92
+ await waitFor(() => expect(screen.queryByRole("tooltip")).not.toBeInTheDocument());
93
+ expect(aoFechar).not.toHaveBeenCalled();
94
+ fireEvent.keyDown(document.activeElement, { key: "Escape" });
95
+ expect(aoFechar).toHaveBeenCalledTimes(1);
96
+ });
97
+ it("tooltip que nunca abriu não engole o Esc", () => {
98
+ const aoFechar = vi.fn();
99
+ render(_jsx(Modal, { open: true, title: "Editar processo", onClose: aoFechar, children: _jsx(Tooltip, { content: "Salva sem enviar", children: _jsx("button", { type: "button", children: "Salvar rascunho" }) }) }));
100
+ fireEvent.keyDown(document.body, { key: "Escape" });
101
+ expect(aoFechar).toHaveBeenCalledTimes(1);
102
+ });
103
+ });
104
+ describe("Tooltip · servidor", () => {
105
+ it("o HTML do servidor é determinístico", () => {
106
+ const html = () => renderToString(_jsx(Tooltip, { content: "Dica", children: _jsx("button", { type: "button", children: "Salvar" }) }));
107
+ expect(html()).toBe(html());
108
+ });
109
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { describe, expect, it } from "vitest";
3
+ import { render, screen, waitFor } from "@testing-library/react";
4
+ import Tooltip from "../components/Tooltip/Tooltip";
5
+ async function abrir(ui) {
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
+ describe("Tooltip · variant dark e maxWidth", () => {
12
+ it("dark escreve a classe no balão e no wrapper flutuante", async () => {
13
+ const balao = await abrir(_jsx(Tooltip, { content: "Atualizar", variant: "dark", children: _jsx("button", { type: "button", children: "Atualizar" }) }));
14
+ expect(balao).toHaveClass("umds-tooltip__bubble--dark");
15
+ expect(balao.parentElement).toHaveClass("umds-tooltip__floating--dark");
16
+ });
17
+ it("sem variant o balão é o padrão", async () => {
18
+ const balao = await abrir(_jsx(Tooltip, { content: "Atualizar", children: _jsx("button", { type: "button", children: "Atualizar" }) }));
19
+ expect(balao).not.toHaveClass("umds-tooltip__bubble--dark");
20
+ expect(balao.parentElement).not.toHaveClass("umds-tooltip__floating--dark");
21
+ expect(balao.parentElement?.style.maxWidth).toBe("");
22
+ });
23
+ it("maxWidth vai ao wrapper flutuante, em qualquer variante", async () => {
24
+ const balao = await abrir(_jsx(Tooltip, { content: "Texto longo", variant: "dark", maxWidth: "140px", children: _jsx("button", { type: "button", children: "x" }) }));
25
+ expect(balao.parentElement?.style.maxWidth).toBe("140px");
26
+ });
27
+ it("a descrição acessível não muda com a variante", () => {
28
+ render(_jsx(Tooltip, { content: "Atualizar", variant: "dark", children: _jsx("button", { type: "button", children: "Recarregar" }) }));
29
+ expect(screen.getByRole("button")).toHaveAccessibleDescription("Atualizar");
30
+ });
31
+ });
@@ -0,0 +1,28 @@
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
+ /** `Date` local → `YYYY-MM-DD`. */
10
+ export declare const toIso: (data: Date) => string;
11
+ /** `YYYY-MM-DD` → `Date` à meia-noite local. */
12
+ export declare const parseIso: (iso: string) => Date;
13
+ /** Aceita `Date` ou texto (`2026-05-15` e `2026-05-15T10:00`) e devolve `YYYY-MM-DD`; "" se não der. */
14
+ export declare const normalizeIso: (valor: any) => string;
15
+ export declare const addDays: (iso: string, dias: number) => string;
16
+ /** Soma meses mantendo o dia, ou o último dia do mês de chegada (31/01 + 1 mês = 28/02). */
17
+ export declare const addMonths: (iso: string, meses: number) => string;
18
+ /** Dia da semana: 0 = domingo. */
19
+ export declare const weekdayOf: (iso: string) => number;
20
+ /** Primeiro dia da semana que contém `iso`, dado o dia em que a semana começa (0 = domingo, 1 = segunda). */
21
+ export declare const startOfWeek: (iso: string, comecaEm: number) => string;
22
+ export declare const startOfMonth: (iso: string) => string;
23
+ export declare const endOfMonth: (iso: string) => string;
24
+ export declare const isSameMonth: (a: string, b: string) => boolean;
25
+ /** Os sete dias da semana que contém `iso`. */
26
+ export declare const weekDays: (iso: string, comecaEm: number) => string[];
27
+ /** O mês de `iso` em semanas inteiras (de 4 a 6 linhas), com os dias dos meses vizinhos que completam as pontas. */
28
+ export declare const monthMatrix: (iso: string, comecaEm: number) => string[][];
@@ -0,0 +1,73 @@
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
+ const doisDigitos = (n) => n < 10 ? "0" + n : String(n);
10
+ /** `Date` local → `YYYY-MM-DD`. */
11
+ export const toIso = (data) => data.getFullYear() + "-" + doisDigitos(data.getMonth() + 1) + "-" + doisDigitos(data.getDate());
12
+ /** `YYYY-MM-DD` → `Date` à meia-noite local. */
13
+ export const parseIso = (iso) => {
14
+ const partes = iso.slice(0, 10).split("-");
15
+ return new Date(Number(partes[0]), Number(partes[1]) - 1, Number(partes[2]));
16
+ };
17
+ /** Aceita `Date` ou texto (`2026-05-15` e `2026-05-15T10:00`) e devolve `YYYY-MM-DD`; "" se não der. */
18
+ export const normalizeIso = (valor) => {
19
+ if (!valor)
20
+ return "";
21
+ if (typeof valor === "string")
22
+ return /^\d{4}-\d{2}-\d{2}/.test(valor) ? valor.slice(0, 10) : "";
23
+ if (valor instanceof Date && !isNaN(valor.getTime()))
24
+ return toIso(valor);
25
+ return "";
26
+ };
27
+ export const addDays = (iso, dias) => {
28
+ const data = parseIso(iso);
29
+ data.setDate(data.getDate() + dias);
30
+ return toIso(data);
31
+ };
32
+ /** Soma meses mantendo o dia, ou o último dia do mês de chegada (31/01 + 1 mês = 28/02). */
33
+ export const addMonths = (iso, meses) => {
34
+ const data = parseIso(iso);
35
+ const dia = data.getDate();
36
+ data.setDate(1);
37
+ data.setMonth(data.getMonth() + meses);
38
+ const ultimo = new Date(data.getFullYear(), data.getMonth() + 1, 0).getDate();
39
+ data.setDate(Math.min(dia, ultimo));
40
+ return toIso(data);
41
+ };
42
+ /** Dia da semana: 0 = domingo. */
43
+ export const weekdayOf = (iso) => parseIso(iso).getDay();
44
+ /** Primeiro dia da semana que contém `iso`, dado o dia em que a semana começa (0 = domingo, 1 = segunda). */
45
+ export const startOfWeek = (iso, comecaEm) => {
46
+ const recuo = (weekdayOf(iso) - comecaEm + 7) % 7;
47
+ return addDays(iso, -recuo);
48
+ };
49
+ export const startOfMonth = (iso) => iso.slice(0, 8) + "01";
50
+ export const endOfMonth = (iso) => {
51
+ const data = parseIso(iso);
52
+ return toIso(new Date(data.getFullYear(), data.getMonth() + 1, 0));
53
+ };
54
+ export const isSameMonth = (a, b) => a.slice(0, 7) === b.slice(0, 7);
55
+ /** Os sete dias da semana que contém `iso`. */
56
+ export const weekDays = (iso, comecaEm) => {
57
+ const inicio = startOfWeek(iso, comecaEm);
58
+ const dias = [];
59
+ for (let i = 0; i < 7; i++)
60
+ dias.push(addDays(inicio, i));
61
+ return dias;
62
+ };
63
+ /** O mês de `iso` em semanas inteiras (de 4 a 6 linhas), com os dias dos meses vizinhos que completam as pontas. */
64
+ export const monthMatrix = (iso, comecaEm) => {
65
+ const ultimo = endOfMonth(iso);
66
+ let cursor = startOfWeek(startOfMonth(iso), comecaEm);
67
+ const semanas = [];
68
+ while (cursor <= ultimo) {
69
+ semanas.push(weekDays(cursor, comecaEm));
70
+ cursor = addDays(cursor, 7);
71
+ }
72
+ return semanas;
73
+ };
@@ -180,6 +180,10 @@ export interface ChartEntradaCartesiana extends ChartFormato {
180
180
  ariaLabel?: string;
181
181
  showLegend?: any;
182
182
  showGrid?: any;
183
+ /** Nome do tipo de gráfico (legenda da tabela sem título e começo do resumo automático). */
184
+ typeLabel?: string;
185
+ /** Nome da lista de alvos focáveis; o título, se houver, vem depois de " — ". */
186
+ valuesListLabel?: string;
183
187
  }
184
188
  export interface ChartGradeLinha {
185
189
  chave: string;
@@ -392,7 +392,8 @@ export function modeloCartesiano(tipo, entrada, largura, altura) {
392
392
  serie: j + 1,
393
393
  nome: s.name
394
394
  }));
395
- const nomeDoTipo = tipo === "barras" ? "Gráfico de barras" : "Gráfico de linhas";
395
+ const nomeDoTipo = entrada.typeLabel || (tipo === "barras" ? "Gráfico de barras" : "Gráfico de linhas");
396
+ const nomeDaLista = entrada.valuesListLabel || "Valores por categoria";
396
397
  const base = {
397
398
  vazio: true,
398
399
  viewBox: `0 0 ${largo} ${alto}`,
@@ -413,7 +414,7 @@ export function modeloCartesiano(tipo, entrada, largura, altura) {
413
414
  pontos: [],
414
415
  legenda,
415
416
  resumo: entrada.ariaLabel || `${nomeDoTipo} sem dados.`,
416
- rotuloDaLista: entrada.title ? `Valores por categoria — ${entrada.title}` : "Valores por categoria",
417
+ rotuloDaLista: entrada.title ? `${nomeDaLista} — ${entrada.title}` : nomeDaLista,
417
418
  legendaDaTabela: entrada.title || nomeDoTipo,
418
419
  cabecalhos: legenda,
419
420
  tabela: []
@@ -0,0 +1,27 @@
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
+ * Endereço de link que o editor aceita: `http:`, `https:`, `mailto:`, `tel:`,
16
+ * caminho (`/x`, `./x`, `../x`) ou âncora (`#x`). Devolve o endereço limpo, ou ""
17
+ * se for perigoso (`javascript:`, `data:`, `vbscript:`...).
18
+ */
19
+ export declare const enderecoSeguro: (bruto: string) => string;
20
+ /** Devolve o endereço pronto para `href`: o seguro como veio, ou `https://` + o texto quando não tem esquema. "" se perigoso ou vazio. */
21
+ export declare const normalizarEndereco: (bruto: string) => string;
22
+ /** Reduz `html` ao que o editor escreve. Sem DOM, devolve o texto escapado. */
23
+ export declare const sanitizarHtml: (html: string) => string;
24
+ /** O editor está "vazio": sem texto, sem lista e sem quebra solta (o `<br>` que o navegador deixa). */
25
+ export declare const htmlVazio: (html: string) => boolean;
26
+ /** O `<a>` que o editor escreve quando o link não tem texto escolhido: o endereço (já seguro) é o href e o texto. */
27
+ export declare const htmlDoLink: (endereco: string) => string;
@@ -0,0 +1,97 @@
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
+ const PERMITIDAS = ["A", "B", "STRONG", "I", "EM", "U", "S", "STRIKE", "DEL", "UL", "OL", "LI", "P", "BR", "DIV", "SPAN", "H1", "H2", "H3", "H4", "BLOCKQUOTE"];
15
+ const DESCARTADAS = ["SCRIPT", "STYLE", "IFRAME", "OBJECT", "EMBED", "LINK", "META", "TEMPLATE", "NOSCRIPT", "SVG", "MATH", "FORM", "INPUT", "BUTTON", "TEXTAREA", "SELECT"];
16
+ const escapar = (texto) => texto.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
17
+ /**
18
+ * Endereço de link que o editor aceita: `http:`, `https:`, `mailto:`, `tel:`,
19
+ * caminho (`/x`, `./x`, `../x`) ou âncora (`#x`). Devolve o endereço limpo, ou ""
20
+ * se for perigoso (`javascript:`, `data:`, `vbscript:`...).
21
+ */
22
+ export const enderecoSeguro = (bruto) => {
23
+ const texto = (bruto || "").trim();
24
+ if (!texto)
25
+ return "";
26
+ // Caracteres de controle e espaços no meio do esquema são o truque clássico (`java\nscript:`).
27
+ const semControle = texto.replace(/[\u0000-\u001f\u007f\s]+/g, "");
28
+ if (/^(https?:\/\/|mailto:|tel:|\/|\.\.?\/|#)/i.test(semControle))
29
+ return texto;
30
+ if (/^[a-z][a-z0-9+.-]*:/i.test(semControle))
31
+ return "";
32
+ return "";
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) => {
36
+ const texto = (bruto || "").trim();
37
+ if (!texto)
38
+ return "";
39
+ const seguro = enderecoSeguro(texto);
40
+ if (seguro)
41
+ return seguro;
42
+ const semControle = texto.replace(/[\u0000-\u001f\u007f\s]+/g, "");
43
+ if (/^[a-z][a-z0-9+.-]*:/i.test(semControle))
44
+ return "";
45
+ return "https://" + texto;
46
+ };
47
+ const limparNo = (no, saida, documento) => {
48
+ const filhos = Array.prototype.slice.call(no.childNodes);
49
+ for (let i = 0; i < filhos.length; i++) {
50
+ const filho = filhos[i];
51
+ if (filho.nodeType === 3) {
52
+ saida.appendChild(documento.createTextNode(filho.nodeValue));
53
+ continue;
54
+ }
55
+ if (filho.nodeType !== 1)
56
+ continue;
57
+ const tag = String(filho.tagName).toUpperCase();
58
+ if (DESCARTADAS.indexOf(tag) !== -1)
59
+ continue;
60
+ if (PERMITIDAS.indexOf(tag) === -1) {
61
+ // Tag desconhecida: some a tag, fica o conteúdo.
62
+ limparNo(filho, saida, documento);
63
+ continue;
64
+ }
65
+ const novo = documento.createElement(tag.toLowerCase());
66
+ if (tag === "A") {
67
+ const href = enderecoSeguro(filho.getAttribute("href") || "");
68
+ if (href) {
69
+ novo.setAttribute("href", href);
70
+ novo.setAttribute("rel", "noopener noreferrer");
71
+ }
72
+ }
73
+ limparNo(filho, novo, documento);
74
+ saida.appendChild(novo);
75
+ }
76
+ };
77
+ /** Reduz `html` ao que o editor escreve. Sem DOM, devolve o texto escapado. */
78
+ export const sanitizarHtml = (html) => {
79
+ const bruto = html || "";
80
+ if (typeof document === "undefined")
81
+ return escapar(bruto);
82
+ const molde = document.createElement("template");
83
+ molde.innerHTML = bruto;
84
+ const saida = document.createElement("div");
85
+ limparNo(molde.content, saida, document);
86
+ return saida.innerHTML;
87
+ };
88
+ /** O editor está "vazio": sem texto, sem lista e sem quebra solta (o `<br>` que o navegador deixa). */
89
+ export const htmlVazio = (html) => {
90
+ const texto = (html || "").replace(/<br\s*\/?>/gi, "").replace(/<\/?(div|p|span)[^>]*>/gi, "").trim();
91
+ return texto === "";
92
+ };
93
+ /** O `<a>` que o editor escreve quando o link não tem texto escolhido: o endereço (já seguro) é o href e o texto. */
94
+ export const htmlDoLink = (endereco) => {
95
+ const seguro = escapar(endereco);
96
+ return '<a href="' + seguro + '">' + seguro + "</a>";
97
+ };
@@ -0,0 +1,2 @@
1
+ /** Próximo id da página para o prefixo dado, ex.: `umds-select-3`. */
2
+ export declare const gerarId: (prefixo: string) => string;
@@ -0,0 +1,23 @@
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
+ /** Próximo id da página para o prefixo dado, ex.: `umds-select-3`. */
20
+ export const gerarId = (prefixo) => {
21
+ contador += 1;
22
+ return prefixo + "-" + contador;
23
+ };