@1doc/1ds-react 0.0.1 → 2.7.1

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 (101) hide show
  1. package/CHANGELOG.md +667 -0
  2. package/README.md +138 -6
  3. package/package.json +43 -10
  4. package/src/index.ts +161 -16
  5. package/src/teste/AttachmentCard.test.tsx +161 -0
  6. package/src/teste/Chart.test.tsx +233 -0
  7. package/src/teste/Combobox.test.tsx +251 -0
  8. package/src/teste/DataTable.test.tsx +70 -0
  9. package/src/teste/DatePicker.test.tsx +86 -0
  10. package/src/teste/Drawer.test.tsx +265 -0
  11. package/src/teste/Dropdown.test.tsx +217 -0
  12. package/src/teste/Input.test.tsx +64 -0
  13. package/src/teste/Kanban.test.tsx +333 -0
  14. package/src/teste/MultiSelect.test.tsx +73 -0
  15. package/src/teste/Podium.test.tsx +107 -0
  16. package/src/teste/Popover.test.tsx +221 -0
  17. package/src/teste/SegmentedControl.test.tsx +183 -0
  18. package/src/teste/Timeline.test.tsx +100 -0
  19. package/src/teste/Tree.test.tsx +74 -0
  20. package/src/teste/setup.ts +7 -0
  21. package/src/utils/bandeiras.ts +22 -0
  22. package/src/utils/chart.ts +682 -0
  23. package/src/utils/density.ts +27 -0
  24. package/src/utils/overlay-stack.ts +26 -0
  25. package/src/utils/theme.ts +63 -47
  26. package/src/utils/toast.ts +89 -0
  27. package/vitest.config.ts +28 -0
  28. package/dist/components/Alert/Alert.css +0 -188
  29. package/dist/components/Alert/Alert.d.ts +0 -65
  30. package/dist/components/Alert/Alert.js +0 -56
  31. package/dist/components/Avatar/Avatar.css +0 -88
  32. package/dist/components/Avatar/Avatar.d.ts +0 -34
  33. package/dist/components/Avatar/Avatar.js +0 -16
  34. package/dist/components/Avatar/AvatarGroup.d.ts +0 -12
  35. package/dist/components/Avatar/AvatarGroup.js +0 -8
  36. package/dist/components/Badge/Badge.css +0 -155
  37. package/dist/components/Badge/Badge.d.ts +0 -46
  38. package/dist/components/Badge/Badge.js +0 -17
  39. package/dist/components/Button/Button.css +0 -199
  40. package/dist/components/Button/Button.d.ts +0 -23
  41. package/dist/components/Button/Button.js +0 -62
  42. package/dist/components/Checkbox/Checkbox.css +0 -122
  43. package/dist/components/Checkbox/Checkbox.d.ts +0 -21
  44. package/dist/components/Checkbox/Checkbox.js +0 -49
  45. package/dist/components/Divider/Divider.css +0 -81
  46. package/dist/components/Divider/Divider.d.ts +0 -18
  47. package/dist/components/Divider/Divider.js +0 -26
  48. package/dist/components/Grid/Grid.css +0 -154
  49. package/dist/components/Grid/Grid.d.ts +0 -21
  50. package/dist/components/Grid/Grid.js +0 -53
  51. package/dist/components/Icon/Icon.css +0 -41
  52. package/dist/components/Icon/Icon.d.ts +0 -12
  53. package/dist/components/Icon/Icon.js +0 -29
  54. package/dist/components/Input/Input.css +0 -190
  55. package/dist/components/Input/Input.d.ts +0 -32
  56. package/dist/components/Input/Input.js +0 -58
  57. package/dist/components/Link/Link.css +0 -76
  58. package/dist/components/Link/Link.d.ts +0 -15
  59. package/dist/components/Link/Link.js +0 -19
  60. package/dist/components/Logo/Logo.css +0 -32
  61. package/dist/components/Logo/Logo.d.ts +0 -10
  62. package/dist/components/Logo/Logo.js +0 -16
  63. package/dist/components/Modal/Modal.css +0 -155
  64. package/dist/components/Modal/Modal.d.ts +0 -36
  65. package/dist/components/Modal/Modal.js +0 -55
  66. package/dist/components/Radio/Radio.css +0 -114
  67. package/dist/components/Radio/Radio.d.ts +0 -19
  68. package/dist/components/Radio/Radio.js +0 -39
  69. package/dist/components/Select/Select.css +0 -291
  70. package/dist/components/Select/Select.d.ts +0 -38
  71. package/dist/components/Select/Select.js +0 -246
  72. package/dist/components/Spinner/Spinner.css +0 -60
  73. package/dist/components/Spinner/Spinner.d.ts +0 -16
  74. package/dist/components/Spinner/Spinner.js +0 -16
  75. package/dist/components/Switch/Switch.css +0 -127
  76. package/dist/components/Switch/Switch.d.ts +0 -18
  77. package/dist/components/Switch/Switch.js +0 -57
  78. package/dist/components/Tabs/Tab.d.ts +0 -19
  79. package/dist/components/Tabs/Tab.js +0 -76
  80. package/dist/components/Tabs/TabContent.d.ts +0 -15
  81. package/dist/components/Tabs/TabContent.js +0 -11
  82. package/dist/components/Tabs/Tabs.css +0 -124
  83. package/dist/components/Tabs/Tabs.d.ts +0 -20
  84. package/dist/components/Tabs/Tabs.js +0 -14
  85. package/dist/components/Tabs/TabsContent.d.ts +0 -16
  86. package/dist/components/Tabs/TabsContent.js +0 -12
  87. package/dist/components/Tabs/tabs.context.d.ts +0 -2
  88. package/dist/components/Tabs/tabs.context.js +0 -2
  89. package/dist/components/Tag/Tag.css +0 -140
  90. package/dist/components/Tag/Tag.d.ts +0 -63
  91. package/dist/components/Tag/Tag.js +0 -48
  92. package/dist/components/Text/Text.css +0 -1213
  93. package/dist/components/Text/Text.d.ts +0 -70
  94. package/dist/components/Text/Text.js +0 -89
  95. package/dist/components/Textarea/Textarea.css +0 -133
  96. package/dist/components/Textarea/Textarea.d.ts +0 -28
  97. package/dist/components/Textarea/Textarea.js +0 -72
  98. package/dist/index.d.ts +0 -25
  99. package/dist/index.js +0 -27
  100. package/dist/utils/theme.d.ts +0 -30
  101. package/dist/utils/theme.js +0 -106
@@ -0,0 +1,221 @@
1
+ import { describe, expect, it, vi, beforeAll } from "vitest";
2
+ import { render, screen, waitFor } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+ import Popover from "../components/Popover/Popover";
5
+ import Button from "../components/Button/Button";
6
+
7
+ /**
8
+ * O `@floating-ui/dom` observa o tamanho do gatilho para reposicionar o painel.
9
+ * O jsdom não implementa `ResizeObserver`, e sem este stub o `autoUpdate`
10
+ * estoura no primeiro `open()` — o teste falharia por falta de ambiente, não
11
+ * por defeito do componente.
12
+ */
13
+ beforeAll(() => {
14
+ if (!(globalThis as any).ResizeObserver) {
15
+ (globalThis as any).ResizeObserver = class {
16
+ observe() {}
17
+ unobserve() {}
18
+ disconnect() {}
19
+ };
20
+ }
21
+ });
22
+
23
+ /**
24
+ * Três contratos que só existem em runtime e que a foto não pega:
25
+ *
26
+ * - o foco VOLTA ao gatilho ao fechar pelo teclado. É o caso que quebrou de
27
+ * verdade: o wrapper do gatilho é um `<span>` sem tabindex quando o slot
28
+ * traz um `Button`, e `focus()` num span sem tabindex é no-op;
29
+ * - `aria-expanded` e `aria-haspopup` moram no elemento INTERATIVO, não no
30
+ * wrapper genérico — num `<span>` de role genérico o axe reprova e o
31
+ * leitor de tela nunca anuncia o estado;
32
+ * - o diálogo nunca fica sem nome.
33
+ */
34
+
35
+ const abrir = () => (
36
+ <Popover title="Sobre o prazo" closable content={<p>Corpo do painel</p>}>
37
+ <Button variant="secondary">Abrir</Button>
38
+ </Popover>
39
+ );
40
+
41
+ const gatilho = () => screen.getByRole("button", { name: "Abrir" });
42
+
43
+ describe("Popover · abrir e fechar", () => {
44
+ it("o clique no gatilho abre o diálogo", async () => {
45
+ const usuario = userEvent.setup();
46
+ render(abrir());
47
+
48
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
49
+ await usuario.click(gatilho());
50
+
51
+ await waitFor(() =>
52
+ expect(screen.getByRole("dialog", { name: "Sobre o prazo" })).toBeInTheDocument(),
53
+ );
54
+ });
55
+
56
+ /**
57
+ * O defeito real: fechava, mas largava o foco no nada, porque quem recebia
58
+ * o `focus()` era o `<span>` wrapper — sem tabindex, e portanto no-op.
59
+ */
60
+ it("o Escape fecha E devolve o foco ao gatilho", async () => {
61
+ const usuario = userEvent.setup();
62
+ render(abrir());
63
+
64
+ await usuario.click(gatilho());
65
+ await waitFor(() => expect(screen.getByRole("dialog")).toBeInTheDocument());
66
+
67
+ await usuario.keyboard("{Escape}");
68
+ await waitFor(() =>
69
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument(),
70
+ );
71
+ expect(gatilho()).toHaveFocus();
72
+ });
73
+
74
+ it("o botão de fechar também devolve o foco ao gatilho", async () => {
75
+ const usuario = userEvent.setup();
76
+ render(abrir());
77
+
78
+ await usuario.click(gatilho());
79
+ await waitFor(() => expect(screen.getByRole("dialog")).toBeInTheDocument());
80
+
81
+ await usuario.click(screen.getByRole("button", { name: "Fechar" }));
82
+ await waitFor(() =>
83
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument(),
84
+ );
85
+ expect(gatilho()).toHaveFocus();
86
+ });
87
+
88
+ it("avisa o pai quando abre e quando fecha", async () => {
89
+ const usuario = userEvent.setup();
90
+ const aoMudar = vi.fn();
91
+ render(
92
+ <Popover title="X" content={<p>corpo</p>} onOpenChange={aoMudar}>
93
+ <Button variant="secondary">Abrir</Button>
94
+ </Popover>,
95
+ );
96
+
97
+ await usuario.click(gatilho());
98
+ await waitFor(() => expect(aoMudar).toHaveBeenCalledWith(true));
99
+
100
+ await usuario.keyboard("{Escape}");
101
+ await waitFor(() => expect(aoMudar).toHaveBeenLastCalledWith(false));
102
+ });
103
+ });
104
+
105
+ describe("Popover · semântica do gatilho", () => {
106
+ /**
107
+ * `aria-expanded` e `aria-haspopup` não são atributos globais: num `<span>`
108
+ * de role genérico o axe reprova, e quem recebe o foco é o botão de dentro —
109
+ * que nunca anunciaria o estado.
110
+ */
111
+ it("os atributos de estado ficam no botão, não no wrapper", async () => {
112
+ const usuario = userEvent.setup();
113
+ const { container } = render(abrir());
114
+
115
+ const wrapper = container.querySelector(".umds-popover__trigger")!;
116
+ expect(wrapper.hasAttribute("aria-haspopup")).toBe(false);
117
+ expect(wrapper.hasAttribute("aria-expanded")).toBe(false);
118
+
119
+ expect(gatilho()).toHaveAttribute("aria-haspopup", "dialog");
120
+ expect(gatilho()).toHaveAttribute("aria-expanded", "false");
121
+
122
+ await usuario.click(gatilho());
123
+ await waitFor(() =>
124
+ expect(gatilho()).toHaveAttribute("aria-expanded", "true"),
125
+ );
126
+ });
127
+ });
128
+
129
+ describe("Popover · nome acessível", () => {
130
+ it("com título, o nome é o título", async () => {
131
+ const usuario = userEvent.setup();
132
+ render(abrir());
133
+ await usuario.click(gatilho());
134
+ await waitFor(() =>
135
+ expect(
136
+ screen.getByRole("dialog", { name: "Sobre o prazo" }),
137
+ ).toBeInTheDocument(),
138
+ );
139
+ });
140
+
141
+ it("sem título, o nome é o ariaLabel", async () => {
142
+ const usuario = userEvent.setup();
143
+ render(
144
+ <Popover ariaLabel="Detalhes do prazo" content={<p>corpo</p>}>
145
+ <Button variant="secondary">Abrir</Button>
146
+ </Popover>,
147
+ );
148
+ await usuario.click(gatilho());
149
+ await waitFor(() =>
150
+ expect(
151
+ screen.getByRole("dialog", { name: "Detalhes do prazo" }),
152
+ ).toBeInTheDocument(),
153
+ );
154
+ });
155
+
156
+ /**
157
+ * Sem título e sem ariaLabel o diálogo ficaria ANÔNIMO, que o axe reprova
158
+ * (aria-dialog-name). O nome cai no texto do gatilho, que sempre existe.
159
+ */
160
+ it("sem título e sem ariaLabel, o nome vem do gatilho", async () => {
161
+ const usuario = userEvent.setup();
162
+ render(
163
+ <Popover content={<p>corpo</p>}>
164
+ <Button variant="secondary">Abrir</Button>
165
+ </Popover>,
166
+ );
167
+ await usuario.click(gatilho());
168
+ await waitFor(() => {
169
+ const dialogo = screen.getByRole("dialog");
170
+ expect(dialogo).toHaveAccessibleName();
171
+ });
172
+ });
173
+ });
174
+
175
+ describe("Popover · modo controlado", () => {
176
+ it("nasce aberto quando o pai manda", async () => {
177
+ render(
178
+ <Popover open title="Aberto pelo pai" content={<p>corpo</p>}>
179
+ <Button variant="secondary">Abrir</Button>
180
+ </Popover>,
181
+ );
182
+ await waitFor(() =>
183
+ expect(
184
+ screen.getByRole("dialog", { name: "Aberto pelo pai" }),
185
+ ).toBeInTheDocument(),
186
+ );
187
+ });
188
+
189
+ /**
190
+ * No modo controlado o gatilho PEDE para fechar; quem fecha é o pai, trocando
191
+ * `open`. O que precisa ser garantido é que o pedido chega e que o painel
192
+ * segue a prop — não em que tick a remontagem acontece.
193
+ */
194
+ it("o gatilho pede para fechar, e o painel segue a prop do pai", async () => {
195
+ const usuario = userEvent.setup();
196
+ const aoMudar = vi.fn();
197
+ const { rerender } = render(
198
+ <Popover open title="X" content={<p>corpo</p>} onOpenChange={aoMudar}>
199
+ <Button variant="secondary">Abrir</Button>
200
+ </Popover>,
201
+ );
202
+ await waitFor(() => expect(screen.getByRole("dialog")).toBeInTheDocument());
203
+
204
+ await usuario.click(gatilho());
205
+ expect(aoMudar).toHaveBeenCalledWith(false);
206
+
207
+ rerender(
208
+ <Popover
209
+ open={false}
210
+ title="X"
211
+ content={<p>corpo</p>}
212
+ onOpenChange={aoMudar}
213
+ >
214
+ <Button variant="secondary">Abrir</Button>
215
+ </Popover>,
216
+ );
217
+ await waitFor(() =>
218
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument(),
219
+ );
220
+ });
221
+ });
@@ -0,0 +1,183 @@
1
+ import { 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 { faList } from "@fortawesome/free-solid-svg-icons";
5
+ import SegmentedControl from "../components/SegmentedControl/SegmentedControl";
6
+
7
+ /**
8
+ * A aposta do componente é usar `<input type="radio">` nativo escondido em vez
9
+ * de `<button role="radio">` com roving tabindex. O que precisa ser provado é
10
+ * justamente que essa aposta entrega o que prometeu:
11
+ *
12
+ * - as SETAS funcionam sem uma linha de código de teclado;
13
+ * - clicar no segmento marca, apesar de o input ter `pointer-events: none`;
14
+ * - o `name` compartilhado agrupa de verdade (submit nativo).
15
+ */
16
+
17
+ const POR_PAGINA = [
18
+ { label: "25", value: "25" },
19
+ { label: "50", value: "50" },
20
+ { label: "100", value: "100" },
21
+ ];
22
+
23
+ describe("SegmentedControl · escolha", () => {
24
+ it("clicar no segmento marca, mesmo o input tendo pointer-events: none", async () => {
25
+ const usuario = userEvent.setup();
26
+ const aoMudar = vi.fn();
27
+ render(
28
+ <SegmentedControl
29
+ label="Itens"
30
+ options={POR_PAGINA}
31
+ defaultValue="25"
32
+ onChange={aoMudar}
33
+ />,
34
+ );
35
+
36
+ await usuario.click(screen.getByRole("radio", { name: "100" }));
37
+
38
+ expect(aoMudar).toHaveBeenCalledWith("100");
39
+ expect(screen.getByRole("radio", { name: "100" })).toBeChecked();
40
+ expect(screen.getByRole("radio", { name: "25" })).not.toBeChecked();
41
+ });
42
+
43
+ /**
44
+ * O motivo de existir o input nativo: a navegação por seta é do navegador.
45
+ * Se um dia alguém trocar por `<button role="radio">`, este teste cai.
46
+ */
47
+ it("a seta move a escolha, sem código de teclado no componente", async () => {
48
+ const usuario = userEvent.setup();
49
+ render(
50
+ <SegmentedControl label="Itens" options={POR_PAGINA} defaultValue="25" />,
51
+ );
52
+
53
+ screen.getByRole("radio", { name: "25" }).focus();
54
+ await usuario.keyboard("{ArrowRight}");
55
+
56
+ await waitFor(() =>
57
+ expect(screen.getByRole("radio", { name: "50" })).toBeChecked(),
58
+ );
59
+ });
60
+
61
+ it("no modo controlado, quem manda é a prop", async () => {
62
+ const usuario = userEvent.setup();
63
+ const aoMudar = vi.fn();
64
+ const { rerender } = render(
65
+ <SegmentedControl
66
+ label="Itens"
67
+ options={POR_PAGINA}
68
+ value="25"
69
+ onChange={aoMudar}
70
+ />,
71
+ );
72
+
73
+ await usuario.click(screen.getByRole("radio", { name: "50" }));
74
+ expect(aoMudar).toHaveBeenCalledWith("50");
75
+
76
+ rerender(
77
+ <SegmentedControl
78
+ label="Itens"
79
+ options={POR_PAGINA}
80
+ value="50"
81
+ onChange={aoMudar}
82
+ />,
83
+ );
84
+ expect(screen.getByRole("radio", { name: "50" })).toBeChecked();
85
+ });
86
+ });
87
+
88
+ describe("SegmentedControl · desabilitado", () => {
89
+ it("o grupo desabilitado desabilita todos os radios", () => {
90
+ render(
91
+ <SegmentedControl label="Itens" options={POR_PAGINA} disabled />,
92
+ );
93
+ screen.getAllByRole("radio").forEach((r) => expect(r).toBeDisabled());
94
+ });
95
+
96
+ it("um segmento desabilitado não é escolhido no clique", async () => {
97
+ const usuario = userEvent.setup();
98
+ const aoMudar = vi.fn();
99
+ render(
100
+ <SegmentedControl
101
+ label="Itens"
102
+ defaultValue="25"
103
+ onChange={aoMudar}
104
+ options={[
105
+ { label: "25", value: "25" },
106
+ { label: "100", value: "100", disabled: true },
107
+ ]}
108
+ />,
109
+ );
110
+
111
+ await usuario.click(screen.getByRole("radio", { name: "100" }));
112
+
113
+ expect(aoMudar).not.toHaveBeenCalled();
114
+ expect(screen.getByRole("radio", { name: "25" })).toBeChecked();
115
+ });
116
+ });
117
+
118
+ describe("SegmentedControl · acessibilidade", () => {
119
+ it("o grupo é um radiogroup nomeado pelo rótulo", () => {
120
+ render(
121
+ <SegmentedControl label="Itens por página" options={POR_PAGINA} />,
122
+ );
123
+ expect(
124
+ screen.getByRole("radiogroup", { name: /Itens por página/ }),
125
+ ).toBeInTheDocument();
126
+ });
127
+
128
+ /** Segmento só com ícone depende do `ariaLabel` para ter nome. */
129
+ it("segmento só com ícone recebe o nome do ariaLabel", () => {
130
+ render(
131
+ <SegmentedControl
132
+ label="Visualização"
133
+ options={[{ value: "lista", icon: faList, ariaLabel: "Lista" }]}
134
+ />,
135
+ );
136
+ expect(screen.getByRole("radio", { name: "Lista" })).toBeInTheDocument();
137
+ });
138
+
139
+ it("a mensagem de erro é ligada ao grupo", () => {
140
+ render(
141
+ <SegmentedControl
142
+ label="Itens"
143
+ options={POR_PAGINA}
144
+ errorMessage="Escolha um tamanho."
145
+ />,
146
+ );
147
+ const grupo = screen.getByRole("radiogroup");
148
+ expect(grupo).toHaveAttribute("aria-invalid", "true");
149
+ const id = grupo.getAttribute("aria-describedby");
150
+ expect(id).toBeTruthy();
151
+ expect(document.getElementById(id!)).toHaveTextContent("Escolha um tamanho.");
152
+ });
153
+
154
+ /**
155
+ * `name` compartilhado é o que faz o grupo existir para o navegador — sem
156
+ * ele as setas atravessariam para outros radios da página.
157
+ */
158
+ it("os radios do grupo compartilham o mesmo name", () => {
159
+ const { container } = render(
160
+ <SegmentedControl label="Itens" options={POR_PAGINA} name="porPagina" />,
161
+ );
162
+ const nomes = new Set(
163
+ Array.from(container.querySelectorAll("input[type=radio]")).map((r) =>
164
+ r.getAttribute("name"),
165
+ ),
166
+ );
167
+ expect(nomes).toEqual(new Set(["porPagina"]));
168
+ });
169
+
170
+ it("sem name, um é gerado e ainda assim é único por grupo", () => {
171
+ const { container } = render(
172
+ <>
173
+ <SegmentedControl label="A" options={POR_PAGINA} />
174
+ <SegmentedControl label="B" options={POR_PAGINA} />
175
+ </>,
176
+ );
177
+ const nomes = Array.from(
178
+ container.querySelectorAll("input[type=radio]"),
179
+ ).map((r) => r.getAttribute("name"));
180
+ // Três de cada grupo, e os dois grupos com nomes diferentes.
181
+ expect(new Set(nomes).size).toBe(2);
182
+ });
183
+ });
@@ -0,0 +1,100 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { faCheck } from "@fortawesome/free-solid-svg-icons";
4
+ import Timeline from "../components/Timeline/Timeline";
5
+
6
+ /**
7
+ * A Timeline é quase toda renderização, e disso cuidam a esteira visual e o
8
+ * axe. O que não se vê numa foto é a regra do conector: N eventos têm N-1
9
+ * conectores, porque o último não tem para onde seguir. É a regra que quebra
10
+ * quando alguém troca o `index === length - 1` por um `>=` ou esquece que a
11
+ * lista pode ter um item só.
12
+ */
13
+
14
+ const EVENTOS = [
15
+ { title: "Criado", timestamp: "18/09 09:12" },
16
+ { title: "Em análise", timestamp: "19/09 10:05" },
17
+ { title: "Concluído", timestamp: "22/09 08:30" },
18
+ ];
19
+
20
+ const conectores = (c: HTMLElement) =>
21
+ c.querySelectorAll(".umds-timeline__connector").length;
22
+
23
+ describe("Timeline · trilha", () => {
24
+ it("N eventos têm N-1 conectores — o último não tem para onde seguir", () => {
25
+ const { container } = render(<Timeline items={EVENTOS} />);
26
+ expect(screen.getAllByRole("listitem")).toHaveLength(3);
27
+ expect(conectores(container)).toBe(2);
28
+ });
29
+
30
+ it("um evento só não tem conector nenhum", () => {
31
+ const { container } = render(<Timeline items={[EVENTOS[0]]} />);
32
+ expect(conectores(container)).toBe(0);
33
+ });
34
+
35
+ it("lista vazia não quebra e não rende evento", () => {
36
+ const { container } = render(<Timeline items={[]} />);
37
+ expect(screen.queryAllByRole("listitem")).toHaveLength(0);
38
+ expect(conectores(container)).toBe(0);
39
+ });
40
+ });
41
+
42
+ describe("Timeline · marcador", () => {
43
+ /**
44
+ * Um evento com ícone e outro sem, na mesma lista, precisam do MESMO
45
+ * marcador — tamanho por item faria a trilha ziguezaguear.
46
+ */
47
+ it("o marcador tem a mesma classe de tamanho com e sem ícone", () => {
48
+ const { container } = render(
49
+ <Timeline
50
+ items={[
51
+ { title: "Sem ícone" },
52
+ { title: "Com ícone", icon: faCheck, variant: "positive" },
53
+ ]}
54
+ />,
55
+ );
56
+ const marcadores = container.querySelectorAll(".umds-timeline__marker");
57
+ expect(marcadores).toHaveLength(2);
58
+ // Nenhum dos dois carrega modificador de tamanho: o tamanho vem da lista.
59
+ marcadores.forEach((m) => {
60
+ expect(m.className).not.toMatch(/--sm|--md/);
61
+ });
62
+ expect(marcadores[1].className).toContain("umds-timeline__marker--icon");
63
+ expect(marcadores[0].className).not.toContain(
64
+ "umds-timeline__marker--icon",
65
+ );
66
+ });
67
+
68
+ it("a variante vira classe no marcador", () => {
69
+ const { container } = render(
70
+ <Timeline items={[{ title: "Recusado", variant: "negative" }]} />,
71
+ );
72
+ expect(
73
+ container.querySelector(".umds-timeline__marker--negative"),
74
+ ).toBeInTheDocument();
75
+ });
76
+
77
+ it("sem variante, o marcador é o padrão", () => {
78
+ const { container } = render(<Timeline items={[{ title: "Criado" }]} />);
79
+ expect(
80
+ container.querySelector(".umds-timeline__marker--default"),
81
+ ).toBeInTheDocument();
82
+ });
83
+ });
84
+
85
+ describe("Timeline · conteúdo", () => {
86
+ it("evento sem título, sem data e sem autor não rende cabeçalho vazio", () => {
87
+ const { container } = render(
88
+ <Timeline items={[{ title: "", description: "Só o texto." }]} />,
89
+ );
90
+ expect(container.querySelector(".umds-timeline__header")).toBeNull();
91
+ expect(screen.getByText("Só o texto.")).toBeInTheDocument();
92
+ });
93
+
94
+ it("a lista é nomeada para quem usa leitor de tela", () => {
95
+ render(<Timeline items={EVENTOS} ariaLabel="Despachos do processo" />);
96
+ expect(
97
+ screen.getByRole("list", { name: "Despachos do processo" }),
98
+ ).toBeInTheDocument();
99
+ });
100
+ });
@@ -0,0 +1,74 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+ import Tree from "../components/Tree/Tree";
5
+
6
+ /**
7
+ * O `Tree` é dos poucos componentes com navegação por teclado própria, e a
8
+ * especificação do padrão treeview é detalhada: ArrowRight abre, ArrowLeft
9
+ * fecha — e, em nó já fechado, sobe para o pai. É o tipo de regra que se perde
10
+ * numa refatoração e que nenhuma foto de tela pega.
11
+ */
12
+
13
+ const ITENS = [
14
+ {
15
+ id: "processo",
16
+ label: "Processo 964/2025",
17
+ children: [
18
+ { id: "d1", label: "Despacho 01" },
19
+ { id: "d2", label: "Despacho 02" },
20
+ ],
21
+ },
22
+ { id: "avulso", label: "Documento avulso" },
23
+ ];
24
+
25
+ describe("Tree · teclado e estado", () => {
26
+ it("ArrowRight abre o nó e ArrowLeft fecha", async () => {
27
+ const usuario = userEvent.setup();
28
+ render(<Tree items={ITENS} ariaLabel="Pasta" />);
29
+
30
+ const raiz = screen.getByRole("treeitem", { name: /Processo 964/ });
31
+ expect(raiz).toHaveAttribute("aria-expanded", "false");
32
+
33
+ raiz.focus();
34
+ await usuario.keyboard("{ArrowRight}");
35
+ expect(raiz).toHaveAttribute("aria-expanded", "true");
36
+
37
+ await usuario.keyboard("{ArrowLeft}");
38
+ expect(raiz).toHaveAttribute("aria-expanded", "false");
39
+ });
40
+
41
+ it("filho só existe no DOM quando o pai está aberto", async () => {
42
+ const usuario = userEvent.setup();
43
+ render(<Tree items={ITENS} ariaLabel="Pasta" />);
44
+
45
+ expect(screen.queryByRole("treeitem", { name: /Despacho 01/ })).not.toBeInTheDocument();
46
+
47
+ const raiz = screen.getByRole("treeitem", { name: /Processo 964/ });
48
+ raiz.focus();
49
+ await usuario.keyboard("{ArrowRight}");
50
+
51
+ expect(screen.getByRole("treeitem", { name: /Despacho 01/ })).toBeInTheDocument();
52
+ });
53
+
54
+ it("defaultExpandedIds abre o nó já na montagem", () => {
55
+ render(<Tree items={ITENS} ariaLabel="Pasta" defaultExpandedIds={["processo"]} />);
56
+ expect(screen.getByRole("treeitem", { name: /Despacho 02/ })).toBeInTheDocument();
57
+ });
58
+
59
+ it("seleção simples devolve o id escolhido", async () => {
60
+ const usuario = userEvent.setup();
61
+ const aoSelecionar = vi.fn();
62
+ render(
63
+ <Tree
64
+ items={ITENS}
65
+ ariaLabel="Pasta"
66
+ selectionMode="single"
67
+ onSelectionChange={aoSelecionar}
68
+ />,
69
+ );
70
+
71
+ await usuario.click(screen.getByRole("treeitem", { name: /Documento avulso/ }));
72
+ expect(aoSelecionar).toHaveBeenCalledWith(["avulso"]);
73
+ });
74
+ });
@@ -0,0 +1,7 @@
1
+ import "@testing-library/jest-dom/vitest";
2
+ import { cleanup } from "@testing-library/react";
3
+ import { afterEach } from "vitest";
4
+
5
+ // Cada teste monta o componente do zero. Sem isto, o DOM do anterior fica de pé
6
+ // e um `getByRole` encontra dois — falha que parece do componente e é do teste.
7
+ afterEach(() => cleanup());
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Bandeiras do `PhoneInput`, embutidas. GERADO — não edite à mão.
3
+ *
4
+ * Antes vinham de `flagcdn.com` em tempo de execução. Três problemas: a rede
5
+ * corporativa bloqueia e a bandeira some; cada usuário que abre o campo entrega
6
+ * o IP dele a um serviço de fora; e não há contrato de disponibilidade nenhum.
7
+ *
8
+ * Origem: os SVGs do pacote `flag-icons` (MIT, © 2013 Panayiotis Lipiridis),
9
+ * rasterizados em 40×30 por `scripts/rasterizar-bandeiras.mjs`.
10
+ */
11
+ export const BANDEIRAS: Record<string, string> = {
12
+ BR: "data:image/png;base64,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",
13
+ US: "data:image/png;base64,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",
14
+ PT: "data:image/png;base64,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",
15
+ ES: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAA8CAIAAAB+RarbAAAFgElEQVR4nOyaW2gcVRiA/3NmzszeL9nN5tLduE2bTTUmbdp6ean6IAqxqIhPRaiCWBCLIvQiBX0pVn0tFEUq+Cp4e2jzIFKbCkovJrVJm3ubNMlmTfY2O7uzcznnuGkVEZ+mKey62e9tbgzf/Oec/z/njPhV8ybYSGDYYDSE652GcL3TEK53GsL1TkO43mkI1zsbThjlz8GGotGk6537KaybUPvcT+FzNyNpVYDaRoT1wTkgLOdLDsqdy1pMMRgqGi4ZyUIeIahB1iEseGZSsizo6ZI3EUpinN/RTgRz1euFhRUZy1FCSm6R+Z05qCXuXXhqwdFUWs2NO4oOdm2UaFPewAfxsVOZ+aJk7tGCVrGpSU1uJc5NPgkrUDPcuzABev6I24d9dHuaGSBwa/7LC85W0K7IdLq88rsnpQad+wtdMQ61xL0Lt4Wy/hdBvw5UpTxCrZhDmxa1RRroI+oCuAIa316MJgBXfGupM9urtJgJs2PgEyDSu3bIGfz6E6yUIC2TWBiXl3TiQ7dNsR2QwzD6+iHccuc2DpkCBD2AayDr24vw0mUYVsDD4emtIDrApNDTD0PXgM+Zc8MgLIGnjYS3GCwCgWbwNYFpARFhfAimMuBF8MTzIFTb2Z4w74bmRRAJ6AiWfxOvnghhhKij1EIlZIoQg7ZDn8uyVLmTGbMzUicvTyboQakHvIsgOcFi/zdht0CIp4dx1S1P5xAiCJnRfO4hjaQgPNoiiljIg2BZbC7pkVIFURAedAMDlxCQQ48h/ocsDkO1sffBiastsO2UM/IWY2uHnPPiLg1vBms3aGG1cgZFguLtUnBBEEoPQmuHIK4VXg5vh6/zfVfopbtPVRd7EaYWLY1PlFOzyzIxVEAIlZQWJeJSWWu4cANcYOo6OLAVD5CKm1KGMKyO40K5XHSM4IAKbVB1bAgvXyJTPxb19LvYySeYu5xFa+ErlxzY5JjqJa6ldXxt1NnbJ5xfkFQm928x9fzwpz7iWAF8TGpiF52eR99U8XrL2XVh4+Ul1TJ0FHzvtK7fSBSPrU5KN7+QuL8wm93Xgc/Oetu/eWRf/MzVNzKZ2M492WxBAC4Q2P2OoqKt+c7P0t+dtEYHKavyjNSGcOdT3BJkvSWSHxy8NS+6EkxrZb7h0C42aBow1NHXnxjziLnxMzOCWxUqw3EaOFKicTZyUgkfNdw9scR2nUhQXWwIM6syTOHUhx9Zc9ObXoVF3ix2ZeWtOs2R0oQAWZ7TA99PDnysv82vXzAtTNw8f7lbOA7eMEkdOiLtahefrX6ZaaN9LV0hqSHDu/e50IkDkf6KPYgYy0QgInY7yDP+s91fXz165XhbS46tAM8BLVNB5JWO3n0gGPnkBHLJY6c9lgXVxUaEm7aZOdOD+3do6Yv/vepqZQ/Hb3jnDeQS57qbOpUVvlxJwn+V0bLfHxzYifi3QrUXCGxE2BWABx5nlFJ2J5+utU7+ryYqIEAyhTxvzxRYmluFMqpMHe7k3spTnLPel6HqqwL2UoSiJAupg5yp3AWhlvQyrQxB/yxkMV8QTSHRbaC0Tme8zr6S5g4QAsuZyULpdcyUSolSdWxE2KJoZGWLYS0Vy4WRZMfmmIH98Uq8714tA472vkKe3A+EOkJM3qZkfmnteiGpWWR0NaZZimLQ4aU4VBsbEbYoXEwNUMQwIy3Oud3R+YHXRn/+IapIASERL/f2XVZ4uauzSzrsSU5j182+wxPRzdnb2eCl1F6KBIIgpk70t9+CqmJvPmyakCz6m135oiGFPcbdk5SuzRMN3lw0/BiZbikj4wL5+0tqlsypmSu7m90FjKDqg1Zjq6Xe2Xi7h42fS+uchnC90xCudxrC9U5DuN5pCNc7DeF6Z8MJ/wkAAP///E9TZgAAAAZJREFUAwBfz1XvSIbzHwAAAABJRU5ErkJggg==",
16
+ FR: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAA8CAIAAAB+RarbAAAAdElEQVR4nOzPMRHCQBRAwT8MVhCDgmuxhIsMFcKoqWPiqpddB3ufec8+az2O4zn7/Nbr//nOPre5GOE64TrhOuE64TrhOuE64TrhOuE64TrhOuE64TrhOuE64TrhOuE64TrhOuE64TrhOuE64TrhOuE64boTAAD///80axAAAAAGSURBVAMAyTAHawxtAWUAAAAASUVORK5CYII=",
17
+ DE: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAA8CAIAAAB+RarbAAAAZ0lEQVR4nOzSAQ3AMAzAsO4af2Tn9PNIbQaRcmfObPLMMoLrBNcJrhNcJ7hOcJ3gOsF1guvON7tYuk5wneA6wXWC6wTXCa4TXCe4bl3wnXdWsXSd4DrBdYLrBNcJrhNcJ7hOcN264B8AAP//N19P1gAAAAZJREFUAwDcCAJInsKX1wAAAABJRU5ErkJggg==",
18
+ IT: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAA8CAIAAAB+RarbAAAAeklEQVR4nOzPQRVBURiAwf84qshh52ggiB0tpFDBQhYVRHgl7up7Mw3mOK/LrHM7nd/Xx6zzuz//n++sc5idEa4TrhOuE64TrhOuE64TrhOuE64TrhOuE64TrhOuE64TrhOuE64TrhOuE64TrhOuE64TrhOuE64TrtsAAAD//2b/whAAAAAGSURBVAMA1nQHq+jgoQAAAAAASUVORK5CYII=",
19
+ AR: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAA8CAIAAAB+RarbAAADq0lEQVR4nOyY22obRxiAZ2ZnD1qt1rZkyXZVKaZxcE1dxaRpC6WlUHrTkFIoDXmBQl+gfYk+QS8KpaE0hKYFBwK5KIVcNAanBzdWExvFiSU5kWydrF15tYc5ZE2eYfZitR97MezALt/OzP//++Ovbz4FkwQCE0YiHHcS4biTCMedRDjuJMJxJxGOO4lw3EmE404iHHcg5xxMEhhECGcMIhSMjzlhMBypaYQx4DwcgqiIVNh+9q+k54c7t7RChfi+290tnP/csw70uTdlVQORENGnJZ7X3LguGYt27bbEur7VgtSFpN2r3lQ0s7v1I/V9EAlRnOFQplNdnz77UXfrBwm6DKY4ZcGJg3RNRgRy11j50m7cm1v7QpJlIBjhKxx47uH2b+n5N/rb17BqUsKH+386hzu92h120rLbe1K6bO3cmDlzvvXXDSAesSscRqlhY1PR89aT20gx+7UNb7DHqO/0W8yzUjPzRq4M1TlzcdXv/jf7/rd0VM8ULwiNYWKDFoRQzeTDI4rYyThQsaJa/d3SmZG+QMNZx9lvNvpG8SIlGKdy1vZ35vIV0RFb7NNp4B49vItSRagVqdfr7vxRKlkyDv1AeMmYlcvW6Ohx9+G6S3PAbw1rvxPXBiIRLcz0uYpiniMnQ6dTD4739DR/+oh1WrT5hDb2aErn48GRJGc17MPCZ6lXP+RMAiIRKxxu6TDBUjJEGELJZNgMb66+jVNpyZhGK2unMZnjrGO7JKASVkjgi84ZoguPwDm4K0keQwYPmmnTcMdjRQVY5gCeTrsO17VxtrQg6RnvcIODdHZxFYhE7ArL+tTMuY+Ja2HWzi5fUg2tUTfsIcMy1FLQOmb1gyyRl5AEod8B3M9XPkVYASIRnpZGB5uyudR78BNW01bjvoSY09myW3WkZYwZU9azXFuaXnoP4IJZficY1jKld4UG6igqrea9a7PLH/Sqv8haJrDb/qhvPXuQyr8GguPTrJta4L6Vef2q83wzt3IpTF1AJFEIh4V0t7pulN6yd3/mFFGGSeAp4Z6WZEAs6ruzF78a1v8prF5G4kvLiP6HGSGHf38/dfaT/qNbiAzwzBqlY+g/51CdWr462L0zf+GKpIg9vS+JrgFAfM9p/69Mv3J0/7q2UJFVxWlXcyuXg/EgU6yAqIi04xHGsDA1h6vNvFH4VoRlrBkvuwIgKiavxfPNr/tgkkjatHEnEY47iXDcSYTjTiIcdxLhuJMIx51EOO4kwnFn4oRfAAAA//8nrMQoAAAABklEQVQDAEq8rD3UVWu3AAAAAElFTkSuQmCC",
20
+ MX: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAA8CAIAAAB+RarbAAAEVklEQVR4nOzZSUwbVxjA8TfjZWyPJ14xsVmMMRgwBsJWEloBSQkgKrokqumiVmqLBIeoVVKpbUIbKYcmolLVQ5VEuaQRh4RINCpCIApkoSkUTCFAFCgQjCEFjME29gxjzxjbzblnZiJN5qe5zuEvvaen7z0haKkF+6exuKKj6RzYP56rN9ZaL4H9A4OXDB/MdXww1/HBXMcHcx0fzHV8MNfxwVzHB3MdH8x1L12wELAuFotdP1ODxSJ79J5Eb0s7erK4ogawhaXgeDzm9zkxedLs79cBBOUffm38bt+uG5+ZGsiY+NP16O2TX3wPWMFS8Oz8IIjGPEs3J+79rVSJd7c3YXkCooyRqwF/VN7za6cSU7/+6ZeAeSztYVSqxdeHUcVq3TspcqE4LSeLpvZQRCwWwhCO0wTe3X5lZWoYMI+l4DRjUVnlaReZFkIrFGpkbmb12TPv3MKaVovtUrQ5UQ7D8Z7LbKxq5oPje57pix6Ps//uL2J15vLYiNu9sxUmJ50bZCRqPCBIMqgEMolSiUw7nX/0dgGGMR8MCbV5Xy27HEIqXFbWiOmMMAipsXiGHkszGpTphYFwjCDpoJ+WAgoP7gCGMRhM0SRNkyBKEPi2SpUsokOue5/nF8kmnZH5RW9hpmYFDxMy/bunLtiqP7AcqZVpzSqt7vmPBOEDjGEwuH/w2qJzjN5aFT0coMI4ml4QpKj5icHqqkIZKn/ijvh81KOhXjzoO9F8Bku1wJjGnFsw6ui8072fr2f/w2BwiCJkwqgIM25illlcRoYCG16hKe8Y5XcdstkEFARROB2F+tqvdvx0ts7+cUb5caVaQ4aJJL0FMIbBc7jh1VovQUAoen5lcWzDfa2hvmguiuneKvjsfUQAp05PdVy5oE62bS09mRx1BKOXGhrtCIKkaDTJiQbAGAaDIYV1fOznLYJuq6/7cWTkbE+fwh+Ud/429XT5jdIS83qXvVGJoCGAfbMiTaxdmBOnmu8P3Xi67PjovTbAGAaDJYhcodA5lxyF1tIfjmaOWkwPnP/Ssdi8L3h7eqZQln3rn1gElqVK58c3Hp4rL28RQ55tp+GgRSLBAGOYPZYqj9iz9cqhv7pgLGdXZtjW53oTrLcyif5XFEGxNA8Kdzc1HcvPbK0uaa6vWXDNlmbl1VU3AyYxGywQYdayU9v+9fabX+dI49+ZoDWKCITdEq/jzcBwY4LHPrFWnp7VUnW8o/P8na42rbFSIJQCJjE+PEAQXFZy4vHjPsnuuBiVX87Szmxalv2e7BzRFlB9a6CFhNe1OK1S6UqLWw9gOsAwNqalZIP1oAJ1uTd7B2+TJG6zVq1qTMXuQPSQaeB+mxNBG+pP11UehsSJgHksjYdC1JhhNqYk55LkTpCmsd4HS3C0KCL90H5RpTSIRBLAFlZvPBAEff6pADB+YioALwZ/icd1fDDX8cFcxwdzHR/MdXww1/HBXMcHcx0fzHV8MNf9BwAA///BQ+ZnAAAABklEQVQDAD9Lpu+qDQU+AAAAAElFTkSuQmCC",
21
+ GB: "data:image/png;base64,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"
22
+ }