@1doc/1ds-react 2.8.0 → 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 (213) hide show
  1. package/CHANGELOG.md +1106 -0
  2. package/dist/components/AILabel/AILabel.js +1 -1
  3. package/dist/components/Accordion/Accordion.d.ts +6 -2
  4. package/dist/components/AttachmentCard/AttachmentCard.css +16 -0
  5. package/dist/components/AttachmentCard/AttachmentCard.d.ts +19 -2
  6. package/dist/components/AttachmentCard/AttachmentCard.js +40 -3
  7. package/dist/components/Avatar/Avatar.css +7 -2
  8. package/dist/components/Avatar/AvatarGroup.d.ts +17 -0
  9. package/dist/components/Avatar/AvatarGroup.js +22 -8
  10. package/dist/components/Badge/Badge.css +54 -2
  11. package/dist/components/Badge/Badge.d.ts +14 -4
  12. package/dist/components/Badge/Badge.js +1 -0
  13. package/dist/components/Box/Box.css +83 -0
  14. package/dist/components/Box/Box.d.ts +82 -2
  15. package/dist/components/Box/Box.js +42 -2
  16. package/dist/components/Button/Button.css +29 -0
  17. package/dist/components/Button/Button.d.ts +4 -4
  18. package/dist/components/Button/Button.js +4 -1
  19. package/dist/components/Calendar/Calendar.css +245 -0
  20. package/dist/components/Calendar/Calendar.d.ts +125 -0
  21. package/dist/components/Calendar/Calendar.js +320 -0
  22. package/dist/components/Chips/Chips.css +38 -0
  23. package/dist/components/Chips/Chips.d.ts +5 -2
  24. package/dist/components/Chips/Chips.js +2 -1
  25. package/dist/components/Coachmark/Coachmark.css +118 -0
  26. package/dist/components/Coachmark/Coachmark.d.ts +75 -0
  27. package/dist/components/Coachmark/Coachmark.js +321 -0
  28. package/dist/components/CodeBlock/CodeBlock.css +96 -0
  29. package/dist/components/CodeBlock/CodeBlock.d.ts +47 -0
  30. package/dist/components/CodeBlock/CodeBlock.js +103 -0
  31. package/dist/components/ColorPicker/ColorPicker.css +133 -0
  32. package/dist/components/ColorPicker/ColorPicker.d.ts +73 -0
  33. package/dist/components/ColorPicker/ColorPicker.js +219 -0
  34. package/dist/components/Combobox/Combobox.css +8 -0
  35. package/dist/components/DataTable/DataTable.css +80 -0
  36. package/dist/components/DataTable/DataTable.d.ts +19 -4
  37. package/dist/components/DataTable/DataTable.js +59 -15
  38. package/dist/components/DatePicker/DatePicker.css +8 -0
  39. package/dist/components/DatePicker/DatePicker.js +10 -1
  40. package/dist/components/DescriptionList/DescriptionList.css +113 -0
  41. package/dist/components/DescriptionList/DescriptionList.d.ts +59 -0
  42. package/dist/components/DescriptionList/DescriptionList.js +51 -0
  43. package/dist/components/FileInput/FileInput.css +16 -0
  44. package/dist/components/FileInput/FileInput.d.ts +4 -2
  45. package/dist/components/FileInput/FileInput.js +8 -2
  46. package/dist/components/Grid/Grid.js +5 -5
  47. package/dist/components/IconPicker/IconPicker.css +107 -0
  48. package/dist/components/IconPicker/IconPicker.d.ts +74 -0
  49. package/dist/components/IconPicker/IconPicker.js +194 -0
  50. package/dist/components/Input/Input.css +38 -0
  51. package/dist/components/Input/Input.d.ts +10 -1
  52. package/dist/components/Input/Input.js +66 -5
  53. package/dist/components/Kanban/Kanban.css +4 -0
  54. package/dist/components/Kbd/Kbd.css +66 -0
  55. package/dist/components/Kbd/Kbd.d.ts +32 -0
  56. package/dist/components/Kbd/Kbd.js +40 -0
  57. package/dist/components/List/List.css +136 -0
  58. package/dist/components/List/List.d.ts +82 -0
  59. package/dist/components/List/List.js +53 -0
  60. package/dist/components/List/ListItem.d.ts +85 -0
  61. package/dist/components/List/ListItem.js +81 -0
  62. package/dist/components/Modal/Modal.css +80 -0
  63. package/dist/components/Modal/Modal.d.ts +33 -6
  64. package/dist/components/Modal/Modal.js +101 -6
  65. package/dist/components/MultiSelect/MultiSelect.css +8 -0
  66. package/dist/components/MultiSelect/MultiSelect.js +10 -1
  67. package/dist/components/OrgChart/OrgChart.css +149 -0
  68. package/dist/components/OrgChart/OrgChart.d.ts +83 -0
  69. package/dist/components/OrgChart/OrgChart.js +297 -0
  70. package/dist/components/OtpInput/OtpInput.css +93 -0
  71. package/dist/components/OtpInput/OtpInput.d.ts +60 -0
  72. package/dist/components/OtpInput/OtpInput.js +215 -0
  73. package/dist/components/PhoneInput/PhoneInput.css +8 -0
  74. package/dist/components/Popover/Popover.d.ts +7 -0
  75. package/dist/components/Popover/Popover.js +11 -1
  76. package/dist/components/RichTextEditor/RichTextEditor.css +203 -0
  77. package/dist/components/RichTextEditor/RichTextEditor.d.ts +83 -0
  78. package/dist/components/RichTextEditor/RichTextEditor.js +494 -0
  79. package/dist/components/SegmentedControl/SegmentedControl.js +7 -2
  80. package/dist/components/Select/Select.css +70 -0
  81. package/dist/components/Select/Select.d.ts +12 -0
  82. package/dist/components/Select/Select.js +59 -4
  83. package/dist/components/Stack/Stack.css +49 -0
  84. package/dist/components/Stack/Stack.d.ts +65 -7
  85. package/dist/components/Stack/Stack.js +30 -2
  86. package/dist/components/StatCard/StatCard.css +73 -0
  87. package/dist/components/StatCard/StatCard.d.ts +17 -0
  88. package/dist/components/StatCard/StatCard.js +4 -1
  89. package/dist/components/Tabs/Tab.d.ts +15 -0
  90. package/dist/components/Tabs/Tab.js +30 -1
  91. package/dist/components/Tabs/Tabs.css +132 -0
  92. package/dist/components/Tabs/Tabs.d.ts +7 -0
  93. package/dist/components/Tabs/Tabs.js +13 -2
  94. package/dist/components/Tag/Tag.css +85 -2
  95. package/dist/components/Tag/Tag.d.ts +13 -6
  96. package/dist/components/Tag/Tag.js +1 -0
  97. package/dist/components/Text/Text.js +2 -2
  98. package/dist/components/Textarea/Textarea.css +8 -0
  99. package/dist/components/TimePicker/TimePicker.css +8 -0
  100. package/dist/components/Tooltip/Tooltip.css +25 -0
  101. package/dist/components/Tooltip/Tooltip.d.ts +11 -0
  102. package/dist/components/Tooltip/Tooltip.js +18 -3
  103. package/dist/index.d.ts +25 -0
  104. package/dist/index.js +14 -0
  105. package/dist/teste/AILabel.test.d.ts +1 -0
  106. package/dist/teste/AILabel.test.js +33 -0
  107. package/dist/teste/AccordionConteudoLivre.test.d.ts +1 -0
  108. package/dist/teste/AccordionConteudoLivre.test.js +29 -0
  109. package/dist/teste/AtributosRepassados.test.d.ts +1 -0
  110. package/dist/teste/AtributosRepassados.test.js +52 -0
  111. package/dist/teste/AttachmentCardIA.test.d.ts +1 -0
  112. package/dist/teste/AttachmentCardIA.test.js +89 -0
  113. package/dist/teste/AvatarGroupOverlap.test.d.ts +1 -0
  114. package/dist/teste/AvatarGroupOverlap.test.js +56 -0
  115. package/dist/teste/BadgeTag.test.d.ts +1 -0
  116. package/dist/teste/BadgeTag.test.js +42 -0
  117. package/dist/teste/BoxItemFlex.test.d.ts +1 -0
  118. package/dist/teste/BoxItemFlex.test.js +54 -0
  119. package/dist/teste/BoxLayout.test.d.ts +1 -0
  120. package/dist/teste/BoxLayout.test.js +35 -0
  121. package/dist/teste/Button.test.d.ts +1 -0
  122. package/dist/teste/Button.test.js +27 -0
  123. package/dist/teste/Calendar.test.d.ts +1 -0
  124. package/dist/teste/Calendar.test.js +221 -0
  125. package/dist/teste/ChipsXs.test.d.ts +1 -0
  126. package/dist/teste/ChipsXs.test.js +31 -0
  127. package/dist/teste/Coachmark.test.d.ts +1 -0
  128. package/dist/teste/Coachmark.test.js +149 -0
  129. package/dist/teste/ColorPicker.test.d.ts +1 -0
  130. package/dist/teste/ColorPicker.test.js +132 -0
  131. package/dist/teste/DataTableCelulaLivre.test.d.ts +1 -0
  132. package/dist/teste/DataTableCelulaLivre.test.js +76 -0
  133. package/dist/teste/DataTableDocumento.test.d.ts +1 -0
  134. package/dist/teste/DataTableDocumento.test.js +92 -0
  135. package/dist/teste/DatePicker.test.js +23 -0
  136. package/dist/teste/FileInputTrigger.test.d.ts +1 -0
  137. package/dist/teste/FileInputTrigger.test.js +53 -0
  138. package/dist/teste/IconPicker.test.d.ts +1 -0
  139. package/dist/teste/IconPicker.test.js +130 -0
  140. package/dist/teste/InputMascaraEAfins.test.d.ts +1 -0
  141. package/dist/teste/InputMascaraEAfins.test.js +99 -0
  142. package/dist/teste/KbdCodeBlock.test.d.ts +1 -0
  143. package/dist/teste/KbdCodeBlock.test.js +95 -0
  144. package/dist/teste/List.test.d.ts +1 -0
  145. package/dist/teste/List.test.js +153 -0
  146. package/dist/teste/ModalVariantes.test.d.ts +1 -0
  147. package/dist/teste/ModalVariantes.test.js +151 -0
  148. package/dist/teste/NomeAcessivelSsr.test.d.ts +1 -0
  149. package/dist/teste/NomeAcessivelSsr.test.js +88 -0
  150. package/dist/teste/OrgChart.test.d.ts +1 -0
  151. package/dist/teste/OrgChart.test.js +162 -0
  152. package/dist/teste/OtpInput.test.d.ts +1 -0
  153. package/dist/teste/OtpInput.test.js +143 -0
  154. package/dist/teste/PopoverMaxHeight.test.d.ts +1 -0
  155. package/dist/teste/PopoverMaxHeight.test.js +38 -0
  156. package/dist/teste/RichTextEditor.test.d.ts +1 -0
  157. package/dist/teste/RichTextEditor.test.js +287 -0
  158. package/dist/teste/SelectPlacementFiltro.test.d.ts +1 -0
  159. package/dist/teste/SelectPlacementFiltro.test.js +88 -0
  160. package/dist/teste/Stack.test.d.ts +1 -0
  161. package/dist/teste/Stack.test.js +57 -0
  162. package/dist/teste/StatCardRotuloTopo.test.d.ts +1 -0
  163. package/dist/teste/StatCardRotuloTopo.test.js +34 -0
  164. package/dist/teste/TabsContadorFechavel.test.d.ts +1 -0
  165. package/dist/teste/TabsContadorFechavel.test.js +79 -0
  166. package/dist/teste/TooltipDark.test.d.ts +1 -0
  167. package/dist/teste/TooltipDark.test.js +31 -0
  168. package/dist/utils/calendario.d.ts +28 -0
  169. package/dist/utils/calendario.js +73 -0
  170. package/dist/utils/html-seguro.d.ts +27 -0
  171. package/dist/utils/html-seguro.js +97 -0
  172. package/dist/utils/paleta-de-cores.d.ts +26 -0
  173. package/dist/utils/paleta-de-cores.js +10 -0
  174. package/dist/utils/texto.d.ts +5 -0
  175. package/dist/utils/texto.js +5 -0
  176. package/package.json +3 -3
  177. package/src/index.ts +26 -0
  178. package/src/teste/AILabel.test.tsx +41 -0
  179. package/src/teste/AccordionConteudoLivre.test.tsx +39 -0
  180. package/src/teste/AtributosRepassados.test.tsx +73 -0
  181. package/src/teste/AttachmentCardIA.test.tsx +126 -0
  182. package/src/teste/AvatarGroupOverlap.test.tsx +74 -0
  183. package/src/teste/BadgeTag.test.tsx +64 -0
  184. package/src/teste/BoxItemFlex.test.tsx +81 -0
  185. package/src/teste/BoxLayout.test.tsx +50 -0
  186. package/src/teste/Button.test.tsx +39 -0
  187. package/src/teste/Calendar.test.tsx +282 -0
  188. package/src/teste/ChipsXs.test.tsx +41 -0
  189. package/src/teste/Coachmark.test.tsx +198 -0
  190. package/src/teste/ColorPicker.test.tsx +153 -0
  191. package/src/teste/DataTableCelulaLivre.test.tsx +103 -0
  192. package/src/teste/DataTableDocumento.test.tsx +102 -0
  193. package/src/teste/DatePicker.test.tsx +38 -0
  194. package/src/teste/FileInputTrigger.test.tsx +89 -0
  195. package/src/teste/IconPicker.test.tsx +145 -0
  196. package/src/teste/InputMascaraEAfins.test.tsx +124 -0
  197. package/src/teste/KbdCodeBlock.test.tsx +106 -0
  198. package/src/teste/List.test.tsx +269 -0
  199. package/src/teste/ModalVariantes.test.tsx +173 -0
  200. package/src/teste/NomeAcessivelSsr.test.tsx +110 -0
  201. package/src/teste/OrgChart.test.tsx +183 -0
  202. package/src/teste/OtpInput.test.tsx +163 -0
  203. package/src/teste/PopoverMaxHeight.test.tsx +47 -0
  204. package/src/teste/RichTextEditor.test.tsx +329 -0
  205. package/src/teste/SelectPlacementFiltro.test.tsx +110 -0
  206. package/src/teste/Stack.test.tsx +80 -0
  207. package/src/teste/StatCardRotuloTopo.test.tsx +44 -0
  208. package/src/teste/TabsContadorFechavel.test.tsx +144 -0
  209. package/src/teste/TooltipDark.test.tsx +51 -0
  210. package/src/utils/calendario.ts +78 -0
  211. package/src/utils/html-seguro.ts +94 -0
  212. package/src/utils/paleta-de-cores.ts +34 -0
  213. package/src/utils/texto.ts +5 -0
@@ -0,0 +1,39 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import Accordion from "../components/Accordion/Accordion";
4
+
5
+ describe("Accordion · conteúdo livre por item (React)", () => {
6
+ it("o content do item aceita qualquer nó (campos, botões)", () => {
7
+ render(
8
+ <Accordion
9
+ items={[
10
+ {
11
+ id: "s1",
12
+ title: "Seção 01",
13
+ defaultOpen: true,
14
+ content: (
15
+ <label>
16
+ Nome <input defaultValue="Maria" />
17
+ </label>
18
+ ),
19
+ },
20
+ { id: "s2", title: "Seção 02", content: <button type="button">Anexar arquivo</button> },
21
+ ]}
22
+ />,
23
+ );
24
+ expect(screen.getByDisplayValue("Maria")).toBeInTheDocument();
25
+ expect(screen.getByRole("button", { name: "Anexar arquivo", hidden: true })).toBeInTheDocument();
26
+ });
27
+
28
+ it("o título do item é o botão do painel, sem cor própria (herda a neutra do gatilho)", () => {
29
+ const { container } = render(<Accordion items={[{ id: "a", title: "Seção", content: "x" }]} />);
30
+ const titulo = container.querySelector(".umds-accordion__title") as HTMLElement;
31
+ expect(titulo.closest("button")).toHaveClass("umds-accordion__trigger");
32
+ expect(titulo.getAttribute("style")).toBeNull();
33
+ });
34
+
35
+ it("o texto simples continua funcionando", () => {
36
+ render(<Accordion items={[{ id: "a", title: "Seção", content: "Texto do painel", defaultOpen: true }]} />);
37
+ expect(screen.getByText("Texto do painel")).toBeInTheDocument();
38
+ });
39
+ });
@@ -0,0 +1,73 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { render } from "@testing-library/react";
3
+ import Text from "../components/Text/Text";
4
+ import Grid from "../components/Grid/Grid";
5
+ import Logo from "../components/Logo/Logo";
6
+ import Button from "../components/Button/Button";
7
+ import DataTable from "../components/DataTable/DataTable";
8
+
9
+ /**
10
+ * Paridade com o Vue: no Vue gerado, Text, Grid e Logo descartavam
11
+ * `title`, `style` e `aria-*`, o Button escrevia `autofocus="false"` e o
12
+ * DataTable a classe `--selected.value`. O React nunca teve o defeito; o teste
13
+ * garante que a correção do Vue não o introduza aqui (mesma fonte Mitosis).
14
+ */
15
+ describe("atributos repassados ao elemento raiz", () => {
16
+ it("Text repassa title, style e aria-*", () => {
17
+ const { container } = render(
18
+ <Text title="Dica" style={{ marginTop: 4 }} aria-live="polite" data-x="1">
19
+ Olá
20
+ </Text>,
21
+ );
22
+ const el = container.firstElementChild as HTMLElement;
23
+ expect(el.getAttribute("title")).toBe("Dica");
24
+ expect(el.style.marginTop).toBe("4px");
25
+ expect(el.getAttribute("aria-live")).toBe("polite");
26
+ expect(el.getAttribute("data-x")).toBe("1");
27
+ });
28
+
29
+ it("Grid repassa title, style e aria-*", () => {
30
+ const { container } = render(
31
+ <Grid container spacing="xxxs" title="Dica" style={{ marginTop: 4 }} aria-label="Área">
32
+ x
33
+ </Grid>,
34
+ );
35
+ const el = container.firstElementChild as HTMLElement;
36
+ expect(el.getAttribute("title")).toBe("Dica");
37
+ expect(el.style.marginTop).toBe("4px");
38
+ expect(el.getAttribute("aria-label")).toBe("Área");
39
+ });
40
+
41
+ it("Logo repassa title, style e aria-*", () => {
42
+ const { container } = render(
43
+ <Logo title="Dica" style={{ width: 80 }} aria-hidden="true" />,
44
+ );
45
+ const el = container.firstElementChild as HTMLElement;
46
+ expect(el.getAttribute("title")).toBe("Dica");
47
+ expect(el.style.width).toBe("80px");
48
+ expect(el.getAttribute("aria-hidden")).toBe("true");
49
+ });
50
+ });
51
+
52
+ describe("Button · autoFocus", () => {
53
+ it("não escreve autofocus quando falso ou ausente", () => {
54
+ const { container } = render(<Button>Salvar</Button>);
55
+ expect(container.querySelector("[autofocus]")).toBeNull();
56
+ });
57
+ });
58
+
59
+ describe("DataTable · linha selecionada", () => {
60
+ it("a classe --selected vai limpa, só na linha selecionada", () => {
61
+ const { container } = render(
62
+ <DataTable
63
+ columns={[{ key: "nome", label: "Nome" }]}
64
+ data={[{ nome: "Cris Lima" }, { nome: "Ana Paula" }]}
65
+ selectable
66
+ selectedRows={[0]}
67
+ />,
68
+ );
69
+ const marcadas = container.querySelectorAll(".umds-datatable__row--selected");
70
+ expect(marcadas).toHaveLength(1);
71
+ expect(container.innerHTML).not.toContain(".value");
72
+ });
73
+ });
@@ -0,0 +1,126 @@
1
+ import { beforeAll, describe, expect, it, vi } from "vitest";
2
+ import { render, screen, waitFor } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+ import AttachmentCard from "../components/AttachmentCard/AttachmentCard";
5
+
6
+ beforeAll(() => {
7
+ if (!(globalThis as any).ResizeObserver) {
8
+ (globalThis as any).ResizeObserver = class {
9
+ observe() {}
10
+ unobserve() {}
11
+ disconnect() {}
12
+ };
13
+ }
14
+ });
15
+
16
+ function cartaoDeIA(extra: Record<string, any> = {}) {
17
+ const aoAplicar = vi.fn();
18
+ const ui = (
19
+ <AttachmentCard
20
+ fileName="protocolo.pdf"
21
+ documentType="Protocolo"
22
+ status="ai"
23
+ statusLabel="Recursos IA"
24
+ onOpen={() => {}}
25
+ panel={
26
+ <div>
27
+ <p>A IA sugere um assunto.</p>
28
+ <button type="button" onClick={aoAplicar}>
29
+ Aplicar sugestão
30
+ </button>
31
+ </div>
32
+ }
33
+ {...extra}
34
+ />
35
+ );
36
+ return { aoAplicar, ...render(ui) };
37
+ }
38
+
39
+ describe("AttachmentCard · status ai", () => {
40
+ it("escreve a classe do selo de IA e usa a varinha por padrão", () => {
41
+ const { container } = render(<AttachmentCard fileName="x.pdf" status="ai" statusLabel="Recursos IA" />);
42
+ const selo = container.querySelector(".umds-attachment-card__status") as HTMLElement;
43
+ expect(selo).toHaveClass("umds-attachment-card__status--ai");
44
+ expect(selo.querySelector("svg")).not.toBeNull();
45
+ });
46
+
47
+ it("só o selo de IA ganha ícone por padrão", () => {
48
+ const { container } = render(
49
+ <AttachmentCard fileName="x.pdf" status="positive" statusLabel="Ok" />,
50
+ );
51
+ expect(container.querySelector(".umds-attachment-card__status svg")).toBeNull();
52
+ });
53
+ });
54
+
55
+ describe("AttachmentCard · slot panel", () => {
56
+ it("o painel livre substitui o resultado da conferência, e o selo vira botão sem statusDetails", async () => {
57
+ const usuario = userEvent.setup();
58
+ cartaoDeIA();
59
+ const selo = screen.getByRole("button", { name: /^Recursos IA/ });
60
+ expect(selo).toHaveAttribute("aria-haspopup", "dialog");
61
+ await usuario.click(selo);
62
+ const painel = await screen.findByRole("dialog");
63
+ expect(painel).toHaveTextContent("A IA sugere um assunto.");
64
+ expect(screen.getByRole("button", { name: "Aplicar sugestão" })).toBeInTheDocument();
65
+ // sem título interno: o painel é nomeado pelo selo
66
+ expect(painel).not.toHaveAttribute("aria-labelledby");
67
+ expect(painel).toHaveAccessibleName(/Recursos IA/);
68
+ });
69
+
70
+ it("sem panel nem statusDetails o selo não é botão", () => {
71
+ render(<AttachmentCard fileName="x.pdf" status="ai" statusLabel="Recursos IA" />);
72
+ expect(screen.queryByRole("button")).toBeNull();
73
+ });
74
+
75
+ it("panelWidth escreve a largura do painel; sem ela não há style", async () => {
76
+ const usuario = userEvent.setup();
77
+ cartaoDeIA({ panelWidth: 280 });
78
+ await usuario.click(screen.getByRole("button", { name: /^Recursos IA/ }));
79
+ expect((await screen.findByRole("dialog")).style.width).toBe("280px");
80
+ });
81
+
82
+ it("sem panelWidth o painel não leva style", async () => {
83
+ const usuario = userEvent.setup();
84
+ cartaoDeIA();
85
+ await usuario.click(screen.getByRole("button", { name: /^Recursos IA/ }));
86
+ expect((await screen.findByRole("dialog")).getAttribute("style")).toBeNull();
87
+ });
88
+ });
89
+
90
+ describe("AttachmentCard · closePanelOnAction", () => {
91
+ it("acionar um botão do painel fecha, devolve o foco ao selo e a ação chega ao app", async () => {
92
+ const usuario = userEvent.setup();
93
+ const { aoAplicar } = cartaoDeIA({ closePanelOnAction: true });
94
+ const selo = screen.getByRole("button", { name: /^Recursos IA/ });
95
+ await usuario.click(selo);
96
+ await usuario.click(await screen.findByRole("button", { name: "Aplicar sugestão" }));
97
+ expect(aoAplicar).toHaveBeenCalledTimes(1);
98
+ await waitFor(() => expect(screen.queryByRole("dialog")).toBeNull());
99
+ expect(selo).toHaveFocus();
100
+ });
101
+
102
+ it("sem a prop o painel continua aberto depois da ação", async () => {
103
+ const usuario = userEvent.setup();
104
+ const { aoAplicar } = cartaoDeIA();
105
+ await usuario.click(screen.getByRole("button", { name: /^Recursos IA/ }));
106
+ await usuario.click(await screen.findByRole("button", { name: "Aplicar sugestão" }));
107
+ expect(aoAplicar).toHaveBeenCalledTimes(1);
108
+ expect(screen.getByRole("dialog")).toBeInTheDocument();
109
+ });
110
+
111
+ it("o botão de copiar um campo não fecha o painel", async () => {
112
+ const usuario = userEvent.setup();
113
+ render(
114
+ <AttachmentCard
115
+ fileName="cpf.pdf"
116
+ status="positive"
117
+ statusLabel="Pré-aprovado"
118
+ closePanelOnAction
119
+ statusDetails={{ fields: [{ label: "CPF", value: "384.221.760-05", copyable: true }] }}
120
+ />,
121
+ );
122
+ await usuario.click(screen.getByRole("button", { name: /^Pré-aprovado/ }));
123
+ await usuario.click(await screen.findByRole("button", { name: "Copiar CPF" }));
124
+ expect(screen.getByRole("dialog")).toBeInTheDocument();
125
+ });
126
+ });
@@ -0,0 +1,74 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import AvatarGroup from "../components/Avatar/AvatarGroup";
4
+
5
+ const PESSOAS = [
6
+ { name: "Ana Souza" },
7
+ { name: "Bruno Lima" },
8
+ { name: "Carla Dias" },
9
+ { name: "Diego Ferraz" },
10
+ { name: "Eduarda Neves" },
11
+ ];
12
+
13
+ const grupo = (c: HTMLElement) => c.firstElementChild as HTMLElement;
14
+
15
+ describe("AvatarGroup · overflowCount", () => {
16
+ it("sem a prop, o +N é o que passou de max (como antes)", () => {
17
+ render(<AvatarGroup avatars={PESSOAS} max={3} />);
18
+ expect(screen.getByText("+2")).toBeInTheDocument();
19
+ });
20
+
21
+ it("overflowCount vira o +N sem itens-fantasma", () => {
22
+ render(<AvatarGroup avatars={PESSOAS.slice(0, 3)} max={3} overflowCount={12} />);
23
+ expect(screen.getByText("+12")).toBeInTheDocument();
24
+ });
25
+
26
+ it("soma com o que passou de max dentro de avatars", () => {
27
+ render(<AvatarGroup avatars={PESSOAS} max={3} overflowCount={10} />);
28
+ expect(screen.getByText("+12")).toBeInTheDocument();
29
+ });
30
+
31
+ it("o rótulo traduzido recebe o total escondido", () => {
32
+ render(
33
+ <AvatarGroup
34
+ avatars={PESSOAS.slice(0, 2)}
35
+ max={2}
36
+ overflowCount={7}
37
+ overflowTitle={(n) => n + " others"}
38
+ overflowAriaLabel={(n) => n + " more avatars"}
39
+ />,
40
+ );
41
+ expect(screen.getByTitle("7 others")).toBeInTheDocument();
42
+ });
43
+
44
+ it("sem excedente nenhum, não há +N", () => {
45
+ render(<AvatarGroup avatars={PESSOAS.slice(0, 2)} max={4} />);
46
+ expect(screen.queryByText(/^\+/)).toBeNull();
47
+ });
48
+
49
+ it("overflowCount 0 não cria +0", () => {
50
+ render(<AvatarGroup avatars={PESSOAS.slice(0, 2)} max={4} overflowCount={0} />);
51
+ expect(screen.queryByText(/^\+/)).toBeNull();
52
+ });
53
+ });
54
+
55
+ describe("AvatarGroup · overlap e ring", () => {
56
+ it("escrevem as variáveis do grupo em px", () => {
57
+ const { container } = render(<AvatarGroup avatars={PESSOAS} overlap={6} ring={1} />);
58
+ const el = grupo(container);
59
+ expect(el.style.getPropertyValue("--umds-avatar-group-overlap")).toBe("6px");
60
+ expect(el.style.getPropertyValue("--umds-avatar-group-ring")).toBe("1px");
61
+ });
62
+
63
+ it("só overlap não mexe no anel", () => {
64
+ const { container } = render(<AvatarGroup avatars={PESSOAS} overlap={4} />);
65
+ const el = grupo(container);
66
+ expect(el.style.getPropertyValue("--umds-avatar-group-overlap")).toBe("4px");
67
+ expect(el.style.getPropertyValue("--umds-avatar-group-ring")).toBe("");
68
+ });
69
+
70
+ it("sem as props, nenhum style: valem os padrões do CSS", () => {
71
+ const { container } = render(<AvatarGroup avatars={PESSOAS} />);
72
+ expect(grupo(container).getAttribute("style")).toBeNull();
73
+ });
74
+ });
@@ -0,0 +1,64 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { render } from "@testing-library/react";
3
+ import Badge from "../components/Badge/Badge";
4
+ import Tag from "../components/Tag/Tag";
5
+
6
+ const raiz = (c: HTMLElement) => c.firstElementChild as HTMLElement;
7
+
8
+ describe("Badge compacto, novidade e colado", () => {
9
+ it("size 2xs escreve a classe; sem size continua md", () => {
10
+ const { container } = render(
11
+ <>
12
+ <Badge size="2xs">A</Badge>
13
+ <Badge>B</Badge>
14
+ </>,
15
+ );
16
+ expect(container.children[0]).toHaveClass("umds-badge--2xs");
17
+ expect(container.children[1]).toHaveClass("umds-badge--md");
18
+ });
19
+
20
+ it("variant new escreve a classe da variante", () => {
21
+ const { container } = render(<Badge variant="new">Novidade</Badge>);
22
+ expect(raiz(container)).toHaveClass("umds-badge--new");
23
+ });
24
+
25
+ it("attached escreve o modificador; sem ele não há", () => {
26
+ const { container } = render(
27
+ <>
28
+ <Badge attached>A</Badge>
29
+ <Badge>B</Badge>
30
+ </>,
31
+ );
32
+ expect(container.children[0]).toHaveClass("umds-badge--attached");
33
+ expect(container.children[1]).not.toHaveClass("umds-badge--attached");
34
+ });
35
+ });
36
+
37
+ describe("Tag compacta, soft, pílula e novidade", () => {
38
+ it("size xs escreve a classe e sm continua", () => {
39
+ const { container } = render(
40
+ <>
41
+ <Tag label="a" size="xs" />
42
+ <Tag label="b" size="sm" />
43
+ </>,
44
+ );
45
+ expect(container.children[0]).toHaveClass("umds-tag--xs");
46
+ expect(container.children[1]).toHaveClass("umds-tag--sm");
47
+ });
48
+
49
+ it("appearance soft e pill escrevem as classes", () => {
50
+ const { container } = render(<Tag label="SOBR" appearance="soft" variant="primary" pill />);
51
+ expect(raiz(container)).toHaveClass("umds-tag--soft", "umds-tag--primary", "umds-tag--pill");
52
+ });
53
+
54
+ it("sem pill não há a classe; padrão segue outlined", () => {
55
+ const { container } = render(<Tag label="x" />);
56
+ expect(raiz(container)).toHaveClass("umds-tag--outlined");
57
+ expect(raiz(container)).not.toHaveClass("umds-tag--pill");
58
+ });
59
+
60
+ it("variant new escreve a classe", () => {
61
+ const { container } = render(<Tag label="Dado simulado" variant="new" appearance="soft" />);
62
+ expect(raiz(container)).toHaveClass("umds-tag--new", "umds-tag--soft");
63
+ });
64
+ });
@@ -0,0 +1,81 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { fireEvent, render, screen } from "@testing-library/react";
3
+ import Box from "../components/Box/Box";
4
+
5
+ describe("Box como item flex e com `as`", () => {
6
+ it("sem `as` continua um div", () => {
7
+ const { container } = render(<Box padding="md">x</Box>);
8
+ expect(container.firstElementChild?.tagName).toBe("DIV");
9
+ });
10
+
11
+ for (const tag of ["div", "span", "ul", "ol", "li", "section", "nav", "header", "footer", "form"]) {
12
+ it(`as="${tag}" renderiza <${tag}> e só ele`, () => {
13
+ const { container } = render(<Box as={tag as any}>x</Box>);
14
+ expect(container.firstElementChild?.tagName).toBe(tag.toUpperCase());
15
+ expect(container.children).toHaveLength(1);
16
+ });
17
+ }
18
+
19
+ it("ul e ol mantêm a semântica de lista (role=list)", () => {
20
+ render(
21
+ <>
22
+ <Box as="ul" aria-label="Itens">
23
+ <Box as="li">a</Box>
24
+ </Box>
25
+ <Box as="ol" aria-label="Passos">
26
+ <Box as="li">b</Box>
27
+ </Box>
28
+ </>,
29
+ );
30
+ expect(screen.getByRole("list", { name: "Itens" }).tagName).toBe("UL");
31
+ expect(screen.getByRole("list", { name: "Passos" }).tagName).toBe("OL");
32
+ });
33
+
34
+ it("nav com aria-label vira região de navegação nomeada", () => {
35
+ render(
36
+ <Box as="nav" aria-label="Atalhos">
37
+ x
38
+ </Box>,
39
+ );
40
+ expect(screen.getByRole("navigation", { name: "Atalhos" })).toBeInTheDocument();
41
+ });
42
+
43
+ it("as=form chama onSubmit", () => {
44
+ const aoEnviar = vi.fn((e: Event) => e.preventDefault());
45
+ const { container } = render(
46
+ <Box as="form" onSubmit={aoEnviar}>
47
+ <button type="submit">Enviar</button>
48
+ </Box>,
49
+ );
50
+ fireEvent.submit(container.querySelector("form") as HTMLFormElement);
51
+ expect(aoEnviar).toHaveBeenCalledTimes(1);
52
+ });
53
+
54
+ it("escreve as classes de item flex", () => {
55
+ const { container } = render(
56
+ <Box grow shrink="0" basis="50" minWidth="auto" overflow="hidden" overflowX="auto" overflowY="scroll" fullHeight fullWidth>
57
+ x
58
+ </Box>,
59
+ );
60
+ const el = container.firstElementChild as HTMLElement;
61
+ for (const c of [
62
+ "umds-box--grow",
63
+ "umds-box--shrink-0",
64
+ "umds-box--basis-50",
65
+ "umds-box--min-w-auto",
66
+ "umds-box--overflow-hidden",
67
+ "umds-box--overflow-x-auto",
68
+ "umds-box--overflow-y-scroll",
69
+ "umds-box--full-height",
70
+ "umds-box--full",
71
+ ]) {
72
+ expect(el).toHaveClass(c);
73
+ }
74
+ });
75
+
76
+ it("sem as props de item não escreve nenhuma classe delas", () => {
77
+ const { container } = render(<Box padding="md">x</Box>);
78
+ const classes = (container.firstElementChild as HTMLElement).className;
79
+ expect(classes).not.toMatch(/grow|shrink|basis|min-w|overflow|full-height/);
80
+ });
81
+ });
@@ -0,0 +1,50 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import Box from "../components/Box/Box";
4
+
5
+ describe("Box de layout", () => {
6
+ it("sem props de layout não vira flex", () => {
7
+ const { container } = render(
8
+ <Box padding="md" bordered>
9
+ x
10
+ </Box>,
11
+ );
12
+ expect(container.firstElementChild).not.toHaveClass("umds-box--flex");
13
+ });
14
+
15
+ it("com direction, gap, align, justify e wrap vira flex", () => {
16
+ const { container } = render(
17
+ <Box direction="horizontal" gap="xxxs" align="center" justify="between" wrap padding="md">
18
+ x
19
+ </Box>,
20
+ );
21
+ const el = container.firstElementChild as HTMLElement;
22
+ for (const c of [
23
+ "umds-box--flex",
24
+ "umds-box--horizontal",
25
+ "umds-box--gap-xxxs",
26
+ "umds-box--align-center",
27
+ "umds-box--justify-between",
28
+ "umds-box--wrap",
29
+ "umds-box--p-md",
30
+ ]) {
31
+ expect(el).toHaveClass(c);
32
+ }
33
+ });
34
+
35
+ it("só com gap vira flex vertical", () => {
36
+ const { container } = render(<Box gap="xs">x</Box>);
37
+ expect(container.firstElementChild).toHaveClass("umds-box--flex", "umds-box--vertical");
38
+ });
39
+
40
+ it("repassa id, role, aria-label e style", () => {
41
+ render(
42
+ <Box id="r" role="region" aria-label="Resumo" style={{ marginTop: 4 }}>
43
+ x
44
+ </Box>,
45
+ );
46
+ const el = screen.getByRole("region", { name: "Resumo" });
47
+ expect(el.id).toBe("r");
48
+ expect(el.style.marginTop).toBe("4px");
49
+ });
50
+ });
@@ -0,0 +1,39 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { faPlus } from "@fortawesome/free-solid-svg-icons";
4
+ import Button from "../components/Button/Button";
5
+
6
+ describe("Button: variante neutral-text e tamanho xs", () => {
7
+ it("neutral-text escreve a classe da variante", () => {
8
+ render(<Button variant="neutral-text">Simular</Button>);
9
+ expect(screen.getByRole("button", { name: "Simular" })).toHaveClass("umds-button--neutral-text");
10
+ });
11
+
12
+ it("neutral continua sendo o contornado cinza", () => {
13
+ render(<Button variant="neutral">Ordenar</Button>);
14
+ const botao = screen.getByRole("button", { name: "Ordenar" });
15
+ expect(botao).toHaveClass("umds-button--neutral");
16
+ expect(botao).not.toHaveClass("umds-button--neutral-text");
17
+ });
18
+
19
+ it("size xs escreve a classe e o botão sem size continua md", () => {
20
+ render(
21
+ <>
22
+ <Button size="xs">Resolver</Button>
23
+ <Button>Padrão</Button>
24
+ </>,
25
+ );
26
+ expect(screen.getByRole("button", { name: "Resolver" })).toHaveClass("umds-button--xs");
27
+ expect(screen.getByRole("button", { name: "Padrão" })).toHaveClass("umds-button--md");
28
+ });
29
+
30
+ it("no xs o ícone usa o tamanho sm do Icon (o CSS do botão o reduz)", () => {
31
+ const { container } = render(
32
+ <Button size="xs" startIcon={faPlus}>
33
+ Novo
34
+ </Button>,
35
+ );
36
+ expect(container.querySelector(".umds-icon")).toHaveClass("umds-icon--sm");
37
+ expect(container.querySelector(".umds-icon--xs")).toBeNull();
38
+ });
39
+ });