@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.
- package/CHANGELOG.md +1106 -0
- package/dist/components/AILabel/AILabel.js +1 -1
- package/dist/components/Accordion/Accordion.d.ts +6 -2
- package/dist/components/AttachmentCard/AttachmentCard.css +16 -0
- package/dist/components/AttachmentCard/AttachmentCard.d.ts +19 -2
- package/dist/components/AttachmentCard/AttachmentCard.js +40 -3
- package/dist/components/Avatar/Avatar.css +7 -2
- package/dist/components/Avatar/AvatarGroup.d.ts +17 -0
- package/dist/components/Avatar/AvatarGroup.js +22 -8
- package/dist/components/Badge/Badge.css +54 -2
- package/dist/components/Badge/Badge.d.ts +14 -4
- package/dist/components/Badge/Badge.js +1 -0
- package/dist/components/Box/Box.css +83 -0
- package/dist/components/Box/Box.d.ts +82 -2
- package/dist/components/Box/Box.js +42 -2
- package/dist/components/Button/Button.css +29 -0
- package/dist/components/Button/Button.d.ts +4 -4
- package/dist/components/Button/Button.js +4 -1
- package/dist/components/Calendar/Calendar.css +245 -0
- package/dist/components/Calendar/Calendar.d.ts +125 -0
- package/dist/components/Calendar/Calendar.js +320 -0
- package/dist/components/Chips/Chips.css +38 -0
- package/dist/components/Chips/Chips.d.ts +5 -2
- package/dist/components/Chips/Chips.js +2 -1
- package/dist/components/Coachmark/Coachmark.css +118 -0
- package/dist/components/Coachmark/Coachmark.d.ts +75 -0
- package/dist/components/Coachmark/Coachmark.js +321 -0
- package/dist/components/CodeBlock/CodeBlock.css +96 -0
- package/dist/components/CodeBlock/CodeBlock.d.ts +47 -0
- package/dist/components/CodeBlock/CodeBlock.js +103 -0
- package/dist/components/ColorPicker/ColorPicker.css +133 -0
- package/dist/components/ColorPicker/ColorPicker.d.ts +73 -0
- package/dist/components/ColorPicker/ColorPicker.js +219 -0
- package/dist/components/Combobox/Combobox.css +8 -0
- package/dist/components/DataTable/DataTable.css +80 -0
- package/dist/components/DataTable/DataTable.d.ts +19 -4
- package/dist/components/DataTable/DataTable.js +59 -15
- package/dist/components/DatePicker/DatePicker.css +8 -0
- package/dist/components/DatePicker/DatePicker.js +10 -1
- package/dist/components/DescriptionList/DescriptionList.css +113 -0
- package/dist/components/DescriptionList/DescriptionList.d.ts +59 -0
- package/dist/components/DescriptionList/DescriptionList.js +51 -0
- package/dist/components/FileInput/FileInput.css +16 -0
- package/dist/components/FileInput/FileInput.d.ts +4 -2
- package/dist/components/FileInput/FileInput.js +8 -2
- package/dist/components/Grid/Grid.js +5 -5
- package/dist/components/IconPicker/IconPicker.css +107 -0
- package/dist/components/IconPicker/IconPicker.d.ts +74 -0
- package/dist/components/IconPicker/IconPicker.js +194 -0
- package/dist/components/Input/Input.css +38 -0
- package/dist/components/Input/Input.d.ts +10 -1
- package/dist/components/Input/Input.js +66 -5
- package/dist/components/Kanban/Kanban.css +4 -0
- package/dist/components/Kbd/Kbd.css +66 -0
- package/dist/components/Kbd/Kbd.d.ts +32 -0
- package/dist/components/Kbd/Kbd.js +40 -0
- package/dist/components/List/List.css +136 -0
- package/dist/components/List/List.d.ts +82 -0
- package/dist/components/List/List.js +53 -0
- package/dist/components/List/ListItem.d.ts +85 -0
- package/dist/components/List/ListItem.js +81 -0
- package/dist/components/Modal/Modal.css +80 -0
- package/dist/components/Modal/Modal.d.ts +33 -6
- package/dist/components/Modal/Modal.js +101 -6
- package/dist/components/MultiSelect/MultiSelect.css +8 -0
- package/dist/components/MultiSelect/MultiSelect.js +10 -1
- package/dist/components/OrgChart/OrgChart.css +149 -0
- package/dist/components/OrgChart/OrgChart.d.ts +83 -0
- package/dist/components/OrgChart/OrgChart.js +297 -0
- package/dist/components/OtpInput/OtpInput.css +93 -0
- package/dist/components/OtpInput/OtpInput.d.ts +60 -0
- package/dist/components/OtpInput/OtpInput.js +215 -0
- package/dist/components/PhoneInput/PhoneInput.css +8 -0
- package/dist/components/Popover/Popover.d.ts +7 -0
- package/dist/components/Popover/Popover.js +11 -1
- package/dist/components/RichTextEditor/RichTextEditor.css +203 -0
- package/dist/components/RichTextEditor/RichTextEditor.d.ts +83 -0
- package/dist/components/RichTextEditor/RichTextEditor.js +494 -0
- package/dist/components/SegmentedControl/SegmentedControl.js +7 -2
- package/dist/components/Select/Select.css +70 -0
- package/dist/components/Select/Select.d.ts +12 -0
- package/dist/components/Select/Select.js +59 -4
- package/dist/components/Stack/Stack.css +49 -0
- package/dist/components/Stack/Stack.d.ts +65 -7
- package/dist/components/Stack/Stack.js +30 -2
- package/dist/components/StatCard/StatCard.css +73 -0
- package/dist/components/StatCard/StatCard.d.ts +17 -0
- package/dist/components/StatCard/StatCard.js +4 -1
- package/dist/components/Tabs/Tab.d.ts +15 -0
- package/dist/components/Tabs/Tab.js +30 -1
- package/dist/components/Tabs/Tabs.css +132 -0
- package/dist/components/Tabs/Tabs.d.ts +7 -0
- package/dist/components/Tabs/Tabs.js +13 -2
- package/dist/components/Tag/Tag.css +85 -2
- package/dist/components/Tag/Tag.d.ts +13 -6
- package/dist/components/Tag/Tag.js +1 -0
- package/dist/components/Text/Text.js +2 -2
- package/dist/components/Textarea/Textarea.css +8 -0
- package/dist/components/TimePicker/TimePicker.css +8 -0
- package/dist/components/Tooltip/Tooltip.css +25 -0
- package/dist/components/Tooltip/Tooltip.d.ts +11 -0
- package/dist/components/Tooltip/Tooltip.js +18 -3
- package/dist/index.d.ts +25 -0
- package/dist/index.js +14 -0
- package/dist/teste/AILabel.test.d.ts +1 -0
- package/dist/teste/AILabel.test.js +33 -0
- package/dist/teste/AccordionConteudoLivre.test.d.ts +1 -0
- package/dist/teste/AccordionConteudoLivre.test.js +29 -0
- package/dist/teste/AtributosRepassados.test.d.ts +1 -0
- package/dist/teste/AtributosRepassados.test.js +52 -0
- package/dist/teste/AttachmentCardIA.test.d.ts +1 -0
- package/dist/teste/AttachmentCardIA.test.js +89 -0
- package/dist/teste/AvatarGroupOverlap.test.d.ts +1 -0
- package/dist/teste/AvatarGroupOverlap.test.js +56 -0
- package/dist/teste/BadgeTag.test.d.ts +1 -0
- package/dist/teste/BadgeTag.test.js +42 -0
- package/dist/teste/BoxItemFlex.test.d.ts +1 -0
- package/dist/teste/BoxItemFlex.test.js +54 -0
- package/dist/teste/BoxLayout.test.d.ts +1 -0
- package/dist/teste/BoxLayout.test.js +35 -0
- package/dist/teste/Button.test.d.ts +1 -0
- package/dist/teste/Button.test.js +27 -0
- package/dist/teste/Calendar.test.d.ts +1 -0
- package/dist/teste/Calendar.test.js +221 -0
- package/dist/teste/ChipsXs.test.d.ts +1 -0
- package/dist/teste/ChipsXs.test.js +31 -0
- package/dist/teste/Coachmark.test.d.ts +1 -0
- package/dist/teste/Coachmark.test.js +149 -0
- package/dist/teste/ColorPicker.test.d.ts +1 -0
- package/dist/teste/ColorPicker.test.js +132 -0
- package/dist/teste/DataTableCelulaLivre.test.d.ts +1 -0
- package/dist/teste/DataTableCelulaLivre.test.js +76 -0
- package/dist/teste/DataTableDocumento.test.d.ts +1 -0
- package/dist/teste/DataTableDocumento.test.js +92 -0
- package/dist/teste/DatePicker.test.js +23 -0
- package/dist/teste/FileInputTrigger.test.d.ts +1 -0
- package/dist/teste/FileInputTrigger.test.js +53 -0
- package/dist/teste/IconPicker.test.d.ts +1 -0
- package/dist/teste/IconPicker.test.js +130 -0
- package/dist/teste/InputMascaraEAfins.test.d.ts +1 -0
- package/dist/teste/InputMascaraEAfins.test.js +99 -0
- package/dist/teste/KbdCodeBlock.test.d.ts +1 -0
- package/dist/teste/KbdCodeBlock.test.js +95 -0
- package/dist/teste/List.test.d.ts +1 -0
- package/dist/teste/List.test.js +153 -0
- package/dist/teste/ModalVariantes.test.d.ts +1 -0
- package/dist/teste/ModalVariantes.test.js +151 -0
- package/dist/teste/NomeAcessivelSsr.test.d.ts +1 -0
- package/dist/teste/NomeAcessivelSsr.test.js +88 -0
- package/dist/teste/OrgChart.test.d.ts +1 -0
- package/dist/teste/OrgChart.test.js +162 -0
- package/dist/teste/OtpInput.test.d.ts +1 -0
- package/dist/teste/OtpInput.test.js +143 -0
- package/dist/teste/PopoverMaxHeight.test.d.ts +1 -0
- package/dist/teste/PopoverMaxHeight.test.js +38 -0
- package/dist/teste/RichTextEditor.test.d.ts +1 -0
- package/dist/teste/RichTextEditor.test.js +287 -0
- package/dist/teste/SelectPlacementFiltro.test.d.ts +1 -0
- package/dist/teste/SelectPlacementFiltro.test.js +88 -0
- package/dist/teste/Stack.test.d.ts +1 -0
- package/dist/teste/Stack.test.js +57 -0
- package/dist/teste/StatCardRotuloTopo.test.d.ts +1 -0
- package/dist/teste/StatCardRotuloTopo.test.js +34 -0
- package/dist/teste/TabsContadorFechavel.test.d.ts +1 -0
- package/dist/teste/TabsContadorFechavel.test.js +79 -0
- package/dist/teste/TooltipDark.test.d.ts +1 -0
- package/dist/teste/TooltipDark.test.js +31 -0
- package/dist/utils/calendario.d.ts +28 -0
- package/dist/utils/calendario.js +73 -0
- package/dist/utils/html-seguro.d.ts +27 -0
- package/dist/utils/html-seguro.js +97 -0
- package/dist/utils/paleta-de-cores.d.ts +26 -0
- package/dist/utils/paleta-de-cores.js +10 -0
- package/dist/utils/texto.d.ts +5 -0
- package/dist/utils/texto.js +5 -0
- package/package.json +3 -3
- package/src/index.ts +26 -0
- package/src/teste/AILabel.test.tsx +41 -0
- package/src/teste/AccordionConteudoLivre.test.tsx +39 -0
- package/src/teste/AtributosRepassados.test.tsx +73 -0
- package/src/teste/AttachmentCardIA.test.tsx +126 -0
- package/src/teste/AvatarGroupOverlap.test.tsx +74 -0
- package/src/teste/BadgeTag.test.tsx +64 -0
- package/src/teste/BoxItemFlex.test.tsx +81 -0
- package/src/teste/BoxLayout.test.tsx +50 -0
- package/src/teste/Button.test.tsx +39 -0
- package/src/teste/Calendar.test.tsx +282 -0
- package/src/teste/ChipsXs.test.tsx +41 -0
- package/src/teste/Coachmark.test.tsx +198 -0
- package/src/teste/ColorPicker.test.tsx +153 -0
- package/src/teste/DataTableCelulaLivre.test.tsx +103 -0
- package/src/teste/DataTableDocumento.test.tsx +102 -0
- package/src/teste/DatePicker.test.tsx +38 -0
- package/src/teste/FileInputTrigger.test.tsx +89 -0
- package/src/teste/IconPicker.test.tsx +145 -0
- package/src/teste/InputMascaraEAfins.test.tsx +124 -0
- package/src/teste/KbdCodeBlock.test.tsx +106 -0
- package/src/teste/List.test.tsx +269 -0
- package/src/teste/ModalVariantes.test.tsx +173 -0
- package/src/teste/NomeAcessivelSsr.test.tsx +110 -0
- package/src/teste/OrgChart.test.tsx +183 -0
- package/src/teste/OtpInput.test.tsx +163 -0
- package/src/teste/PopoverMaxHeight.test.tsx +47 -0
- package/src/teste/RichTextEditor.test.tsx +329 -0
- package/src/teste/SelectPlacementFiltro.test.tsx +110 -0
- package/src/teste/Stack.test.tsx +80 -0
- package/src/teste/StatCardRotuloTopo.test.tsx +44 -0
- package/src/teste/TabsContadorFechavel.test.tsx +144 -0
- package/src/teste/TooltipDark.test.tsx +51 -0
- package/src/utils/calendario.ts +78 -0
- package/src/utils/html-seguro.ts +94 -0
- package/src/utils/paleta-de-cores.ts +34 -0
- package/src/utils/texto.ts +5 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { render, screen, within } from "@testing-library/react";
|
|
5
|
+
import userEvent from "@testing-library/user-event";
|
|
6
|
+
import Calendar from "../components/Calendar/Calendar";
|
|
7
|
+
const HOJE = "2026-05-15";
|
|
8
|
+
const EVENTOS = [
|
|
9
|
+
{ id: "a", date: "2026-05-04", title: "Proc. Administrativo 002/2020", color: "--umds-primitive-blue-pure" },
|
|
10
|
+
{ id: "b", date: "2026-05-11", title: "Protocolo 005/2020", color: "--umds-primitive-green-pure", time: "09:00" },
|
|
11
|
+
{ id: "c", date: "2026-05-11", title: "Ofício 001/2020", color: "--umds-primitive-red-pure", time: "08:00" },
|
|
12
|
+
{ id: "d1", date: "2026-05-20", title: "Reunião 1" },
|
|
13
|
+
{ id: "d2", date: "2026-05-20", title: "Reunião 2" },
|
|
14
|
+
{ id: "d3", date: "2026-05-20", title: "Reunião 3" },
|
|
15
|
+
{ id: "d4", date: "2026-05-20", title: "Reunião 4" },
|
|
16
|
+
{ id: "d5", date: "2026-05-20", title: "Reunião 5" },
|
|
17
|
+
];
|
|
18
|
+
const celula = (container, iso) => container.querySelector(`[data-date="${iso}"]`);
|
|
19
|
+
describe("Calendar", () => {
|
|
20
|
+
it("é uma grade nomeada pelo mês, com sete cabeçalhos de dia", () => {
|
|
21
|
+
render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE }));
|
|
22
|
+
expect(screen.getByRole("grid", { name: "Maio 2026" })).toBeInTheDocument();
|
|
23
|
+
const cabecalhos = screen.getAllByRole("columnheader");
|
|
24
|
+
expect(cabecalhos).toHaveLength(7);
|
|
25
|
+
expect(cabecalhos[0]).toHaveAccessibleName("Domingo");
|
|
26
|
+
expect(screen.getByText("Maio 2026")).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
it("o mês de maio de 2026 ocupa seis semanas inteiras; dias de fora ganham a classe", () => {
|
|
29
|
+
const { container } = render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE }));
|
|
30
|
+
expect(container.querySelectorAll('[role="gridcell"]')).toHaveLength(42);
|
|
31
|
+
expect(celula(container, "2026-04-26")).toHaveClass("umds-calendar__cell--outside");
|
|
32
|
+
expect(celula(container, "2026-05-15")).not.toHaveClass("umds-calendar__cell--outside");
|
|
33
|
+
});
|
|
34
|
+
it("hoje é marcado com aria-current", () => {
|
|
35
|
+
const { container } = render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE }));
|
|
36
|
+
expect(celula(container, HOJE)).toHaveAttribute("aria-current", "date");
|
|
37
|
+
expect(celula(container, "2026-05-16")).not.toHaveAttribute("aria-current");
|
|
38
|
+
});
|
|
39
|
+
it("cada dia tem nome acessível com a data e a contagem de eventos", () => {
|
|
40
|
+
const { container } = render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE, events: EVENTOS }));
|
|
41
|
+
expect(celula(container, "2026-05-11")).toHaveAttribute("aria-label", "segunda-feira, 11 de maio de 2026, 2 eventos");
|
|
42
|
+
expect(celula(container, "2026-05-04")).toHaveAttribute("aria-label", "segunda-feira, 4 de maio de 2026, 1 evento");
|
|
43
|
+
expect(celula(container, "2026-05-12")).toHaveAttribute("aria-label", "terça-feira, 12 de maio de 2026, sem eventos");
|
|
44
|
+
});
|
|
45
|
+
it("dayLabel troca o nome do dia", () => {
|
|
46
|
+
const { container } = render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE, dayLabel: (iso, n) => `${iso}:${n}`, events: EVENTOS }));
|
|
47
|
+
expect(celula(container, "2026-05-11")).toHaveAttribute("aria-label", "2026-05-11:2");
|
|
48
|
+
});
|
|
49
|
+
it("os eventos caem no dia certo, em ordem de horário, com a cor do dado", () => {
|
|
50
|
+
const { container } = render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE, events: EVENTOS }));
|
|
51
|
+
const dia = celula(container, "2026-05-11");
|
|
52
|
+
const chips = within(dia).getAllByRole("button");
|
|
53
|
+
expect(chips).toHaveLength(2);
|
|
54
|
+
expect(chips[0]).toHaveTextContent("08:00");
|
|
55
|
+
expect(chips[0]).toHaveTextContent("Ofício 001/2020");
|
|
56
|
+
expect(chips[0].style.borderLeftColor).toBe("var(--umds-primitive-red-pure)");
|
|
57
|
+
});
|
|
58
|
+
it("evento sem cor usa o primário", () => {
|
|
59
|
+
const { container } = render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE, events: EVENTOS }));
|
|
60
|
+
const chip = within(celula(container, "2026-05-20")).getAllByRole("button")[0];
|
|
61
|
+
expect(chip.style.borderLeftColor).toBe("var(--umds-semantic-color-primary-pure)");
|
|
62
|
+
});
|
|
63
|
+
it("passou de maxVisible: mostra três e um +N, que avisa com os escondidos", async () => {
|
|
64
|
+
const usuario = userEvent.setup();
|
|
65
|
+
const aoVerMais = vi.fn();
|
|
66
|
+
const { container } = render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE, events: EVENTOS, onMoreClick: aoVerMais }));
|
|
67
|
+
const dia = celula(container, "2026-05-20");
|
|
68
|
+
expect(within(dia).getAllByText(/Reunião/)).toHaveLength(3);
|
|
69
|
+
const mais = within(dia).getByRole("button", { name: "2 eventos a mais em 20 de maio" });
|
|
70
|
+
expect(mais).toHaveTextContent("+2");
|
|
71
|
+
await usuario.click(mais);
|
|
72
|
+
expect(aoVerMais).toHaveBeenCalledTimes(1);
|
|
73
|
+
expect(aoVerMais.mock.calls[0][0]).toBe("2026-05-20");
|
|
74
|
+
expect(aoVerMais.mock.calls[0][1].map((e) => e.id)).toEqual(["d4", "d5"]);
|
|
75
|
+
});
|
|
76
|
+
it("maxVisible muda o corte", () => {
|
|
77
|
+
const { container } = render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE, events: EVENTOS, maxVisible: 1 }));
|
|
78
|
+
expect(within(celula(container, "2026-05-20")).getAllByText(/Reunião/)).toHaveLength(1);
|
|
79
|
+
expect(within(celula(container, "2026-05-20")).getByText("+4")).toBeInTheDocument();
|
|
80
|
+
});
|
|
81
|
+
it("clicar no evento avisa com o evento e não clica no dia", async () => {
|
|
82
|
+
const usuario = userEvent.setup();
|
|
83
|
+
const aoEvento = vi.fn();
|
|
84
|
+
const aoDia = vi.fn();
|
|
85
|
+
render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE, events: EVENTOS, onEventClick: aoEvento, onDayClick: aoDia }));
|
|
86
|
+
await usuario.click(screen.getByRole("button", { name: /Proc\. Administrativo 002\/2020/ }));
|
|
87
|
+
expect(aoEvento).toHaveBeenCalledWith(expect.objectContaining({ id: "a" }));
|
|
88
|
+
expect(aoDia).not.toHaveBeenCalled();
|
|
89
|
+
});
|
|
90
|
+
it("clicar no dia avisa com o ISO", async () => {
|
|
91
|
+
const usuario = userEvent.setup();
|
|
92
|
+
const aoDia = vi.fn();
|
|
93
|
+
const { container } = render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE, onDayClick: aoDia }));
|
|
94
|
+
await usuario.click(celula(container, "2026-05-12"));
|
|
95
|
+
expect(aoDia).toHaveBeenCalledWith("2026-05-12");
|
|
96
|
+
});
|
|
97
|
+
it("anterior, próximo e hoje navegam e avisam em onDateChange", async () => {
|
|
98
|
+
const usuario = userEvent.setup();
|
|
99
|
+
const aoMudar = vi.fn();
|
|
100
|
+
render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE, onDateChange: aoMudar }));
|
|
101
|
+
await usuario.click(screen.getByRole("button", { name: "Próximo" }));
|
|
102
|
+
expect(screen.getByText("Junho 2026")).toBeInTheDocument();
|
|
103
|
+
expect(aoMudar).toHaveBeenLastCalledWith("2026-06-15");
|
|
104
|
+
await usuario.click(screen.getByRole("button", { name: "Anterior" }));
|
|
105
|
+
await usuario.click(screen.getByRole("button", { name: "Anterior" }));
|
|
106
|
+
expect(screen.getByText("Abril 2026")).toBeInTheDocument();
|
|
107
|
+
await usuario.click(screen.getByRole("button", { name: "Hoje" }));
|
|
108
|
+
expect(screen.getByText("Maio 2026")).toBeInTheDocument();
|
|
109
|
+
expect(aoMudar).toHaveBeenLastCalledWith(HOJE);
|
|
110
|
+
});
|
|
111
|
+
it("só um dia entra na ordem de Tab (hoje), com os eventos dele", () => {
|
|
112
|
+
const { container } = render(_jsx(Calendar, { today: "2026-05-11", defaultDate: "2026-05-11", events: EVENTOS }));
|
|
113
|
+
const comTab = [...container.querySelectorAll('[role="gridcell"]')].filter((c) => c.getAttribute("tabindex") === "0");
|
|
114
|
+
expect(comTab).toHaveLength(1);
|
|
115
|
+
expect(comTab[0]).toHaveAttribute("data-date", "2026-05-11");
|
|
116
|
+
const chipsComTab = [...container.querySelectorAll(".umds-calendar__event")].filter((c) => c.getAttribute("tabindex") === "0");
|
|
117
|
+
expect(chipsComTab).toHaveLength(2);
|
|
118
|
+
});
|
|
119
|
+
it("setas movem o foco entre os dias; Home, End e Enter", async () => {
|
|
120
|
+
const usuario = userEvent.setup();
|
|
121
|
+
const aoDia = vi.fn();
|
|
122
|
+
const { container } = render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE, onDayClick: aoDia }));
|
|
123
|
+
celula(container, HOJE).focus();
|
|
124
|
+
await usuario.keyboard("{ArrowRight}");
|
|
125
|
+
expect(celula(container, "2026-05-16")).toHaveFocus();
|
|
126
|
+
await usuario.keyboard("{ArrowDown}");
|
|
127
|
+
expect(celula(container, "2026-05-23")).toHaveFocus();
|
|
128
|
+
await usuario.keyboard("{ArrowLeft}{ArrowUp}");
|
|
129
|
+
expect(celula(container, "2026-05-15")).toHaveFocus();
|
|
130
|
+
await usuario.keyboard("{Home}");
|
|
131
|
+
expect(celula(container, "2026-05-10")).toHaveFocus();
|
|
132
|
+
await usuario.keyboard("{End}");
|
|
133
|
+
expect(celula(container, "2026-05-16")).toHaveFocus();
|
|
134
|
+
await usuario.keyboard("{Enter}");
|
|
135
|
+
expect(aoDia).toHaveBeenCalledWith("2026-05-16");
|
|
136
|
+
});
|
|
137
|
+
it("a seta que sai do mês troca de mês e leva o foco", async () => {
|
|
138
|
+
const usuario = userEvent.setup();
|
|
139
|
+
const { container } = render(_jsx(Calendar, { today: HOJE, defaultDate: "2026-05-30" }));
|
|
140
|
+
celula(container, "2026-05-30").focus();
|
|
141
|
+
await usuario.keyboard("{ArrowRight}{ArrowRight}");
|
|
142
|
+
expect(screen.getByText("Junho 2026")).toBeInTheDocument();
|
|
143
|
+
expect(celula(container, "2026-06-01")).toHaveFocus();
|
|
144
|
+
});
|
|
145
|
+
it("PageDown e PageUp trocam de mês", async () => {
|
|
146
|
+
const usuario = userEvent.setup();
|
|
147
|
+
const { container } = render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE }));
|
|
148
|
+
celula(container, HOJE).focus();
|
|
149
|
+
await usuario.keyboard("{PageDown}");
|
|
150
|
+
expect(screen.getByText("Junho 2026")).toBeInTheDocument();
|
|
151
|
+
expect(celula(container, "2026-06-15")).toHaveFocus();
|
|
152
|
+
await usuario.keyboard("{PageUp}{PageUp}");
|
|
153
|
+
expect(screen.getByText("Abril 2026")).toBeInTheDocument();
|
|
154
|
+
});
|
|
155
|
+
it("a visão de semana mostra sete dias, todos os eventos, e avisa em onViewChange", async () => {
|
|
156
|
+
const usuario = userEvent.setup();
|
|
157
|
+
const aoVisao = vi.fn();
|
|
158
|
+
const { container } = render(_jsx(Calendar, { today: "2026-05-20", defaultDate: "2026-05-20", events: EVENTOS, onViewChange: aoVisao }));
|
|
159
|
+
await usuario.click(screen.getByRole("radio", { name: "Semana" }));
|
|
160
|
+
expect(aoVisao).toHaveBeenCalledWith("week");
|
|
161
|
+
expect(container.querySelectorAll('[role="gridcell"]')).toHaveLength(7);
|
|
162
|
+
expect(within(celula(container, "2026-05-20")).getAllByText(/Reunião/)).toHaveLength(5);
|
|
163
|
+
expect(screen.queryByText("+2")).toBeNull();
|
|
164
|
+
expect(container.querySelector(".umds-calendar--week")).not.toBeNull();
|
|
165
|
+
});
|
|
166
|
+
it("na semana, anterior e próximo andam sete dias", async () => {
|
|
167
|
+
const usuario = userEvent.setup();
|
|
168
|
+
const aoMudar = vi.fn();
|
|
169
|
+
render(_jsx(Calendar, { today: "2026-05-20", defaultDate: "2026-05-20", defaultView: "week", onDateChange: aoMudar }));
|
|
170
|
+
await usuario.click(screen.getByRole("button", { name: "Próximo" }));
|
|
171
|
+
expect(aoMudar).toHaveBeenLastCalledWith("2026-05-27");
|
|
172
|
+
await usuario.click(screen.getByRole("button", { name: "Anterior" }));
|
|
173
|
+
await usuario.click(screen.getByRole("button", { name: "Anterior" }));
|
|
174
|
+
expect(aoMudar).toHaveBeenLastCalledWith("2026-05-13");
|
|
175
|
+
});
|
|
176
|
+
it("weekStartsOn=1 começa a semana na segunda", () => {
|
|
177
|
+
render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE, weekStartsOn: 1 }));
|
|
178
|
+
expect(screen.getAllByRole("columnheader")[0]).toHaveAccessibleName("Segunda-feira");
|
|
179
|
+
});
|
|
180
|
+
it("controlado: date e view mandam", () => {
|
|
181
|
+
function Pai() {
|
|
182
|
+
const [data, setData] = useState("2026-08-10");
|
|
183
|
+
return _jsx(Calendar, { today: HOJE, date: data, onDateChange: setData, view: "month" });
|
|
184
|
+
}
|
|
185
|
+
render(_jsx(Pai, {}));
|
|
186
|
+
expect(screen.getByText("Agosto 2026")).toBeInTheDocument();
|
|
187
|
+
});
|
|
188
|
+
it("locale e textos por prop (inglês)", () => {
|
|
189
|
+
render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE, locale: "en-US", previousLabel: "Previous", nextLabel: "Next", todayLabel: "Today", monthLabel: "Month", weekLabel: "Week", viewLabel: "View" }));
|
|
190
|
+
expect(screen.getByText("May 2026")).toBeInTheDocument();
|
|
191
|
+
expect(screen.getByRole("button", { name: "Previous" })).toBeInTheDocument();
|
|
192
|
+
expect(screen.getByRole("button", { name: "Next" })).toBeInTheDocument();
|
|
193
|
+
expect(screen.getByRole("button", { name: "Today" })).toBeInTheDocument();
|
|
194
|
+
expect(screen.getByRole("radio", { name: "Week" })).toBeInTheDocument();
|
|
195
|
+
expect(screen.getByRole("group", { name: "View" })).toBeInTheDocument();
|
|
196
|
+
expect(screen.getAllByRole("columnheader")[0]).toHaveAccessibleName("Sunday");
|
|
197
|
+
});
|
|
198
|
+
it("moreLabel e moreAriaLabel traduzem o +N", () => {
|
|
199
|
+
render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE, events: EVENTOS, locale: "en-US", moreLabel: "{count} more", moreAriaLabel: "{count} more events on {date}" }));
|
|
200
|
+
expect(screen.getByRole("button", { name: "2 more events on May 20" })).toHaveTextContent("2 more");
|
|
201
|
+
});
|
|
202
|
+
it("hideViewSwitcher esconde o seletor; os slots do cabeçalho aparecem", () => {
|
|
203
|
+
render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE, hideViewSwitcher: true, headerStart: _jsx("span", { "data-testid": "filtro", children: "Todos os documentos" }), headerEnd: _jsx("span", { "data-testid": "legenda", children: "Legenda" }) }));
|
|
204
|
+
expect(screen.queryByRole("radio")).toBeNull();
|
|
205
|
+
expect(screen.getByTestId("filtro")).toBeInTheDocument();
|
|
206
|
+
expect(screen.getByTestId("legenda")).toBeInTheDocument();
|
|
207
|
+
});
|
|
208
|
+
it("selectedDate marca o dia", () => {
|
|
209
|
+
const { container } = render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE, selectedDate: "2026-05-12" }));
|
|
210
|
+
expect(celula(container, "2026-05-12")).toHaveAttribute("aria-selected", "true");
|
|
211
|
+
expect(celula(container, "2026-05-12")).toHaveClass("umds-calendar__cell--selected");
|
|
212
|
+
});
|
|
213
|
+
it("evento com Date e com data-hora entra no dia", () => {
|
|
214
|
+
const { container } = render(_jsx(Calendar, { today: HOJE, defaultDate: HOJE, events: [
|
|
215
|
+
{ date: new Date(2026, 4, 12), title: "Com Date" },
|
|
216
|
+
{ date: "2026-05-13T10:30:00", title: "Com hora" },
|
|
217
|
+
] }));
|
|
218
|
+
expect(within(celula(container, "2026-05-12")).getByText("Com Date")).toBeInTheDocument();
|
|
219
|
+
expect(within(celula(container, "2026-05-13")).getByText("Com hora")).toBeInTheDocument();
|
|
220
|
+
});
|
|
221
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
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 } from "@testing-library/react";
|
|
4
|
+
import Chips from "../components/Chips/Chips";
|
|
5
|
+
const raiz = (c) => c.firstElementChild;
|
|
6
|
+
describe("Chips · xs e × em círculo", () => {
|
|
7
|
+
it("size xs escreve a classe; sem size continua md", () => {
|
|
8
|
+
const { container } = render(_jsxs(_Fragment, { children: [_jsx(Chips, { label: "a", size: "xs" }), _jsx(Chips, { label: "b" })] }));
|
|
9
|
+
expect(container.children[0]).toHaveClass("umds-chip--xs");
|
|
10
|
+
expect(container.children[1]).toHaveClass("umds-chip--md");
|
|
11
|
+
});
|
|
12
|
+
it("circularRemove com removable escreve a classe do círculo", () => {
|
|
13
|
+
const { container } = render(_jsx(Chips, { label: "Marcador", removable: true, circularRemove: true }));
|
|
14
|
+
expect(raiz(container)).toHaveClass("umds-chip--remove-circle");
|
|
15
|
+
});
|
|
16
|
+
it("circularRemove sem removable não escreve a classe (não há ×)", () => {
|
|
17
|
+
const { container } = render(_jsx(Chips, { label: "Marcador", circularRemove: true }));
|
|
18
|
+
expect(raiz(container)).not.toHaveClass("umds-chip--remove-circle");
|
|
19
|
+
expect(screen.queryByRole("button")).toBeNull();
|
|
20
|
+
});
|
|
21
|
+
it("o × em círculo continua sendo o mesmo botão, com o mesmo nome e o mesmo clique", () => {
|
|
22
|
+
const aoRemover = vi.fn();
|
|
23
|
+
render(_jsx(Chips, { label: "Urgente", size: "xs", removable: true, circularRemove: true, onRemove: aoRemover }));
|
|
24
|
+
fireEvent.click(screen.getByRole("button", { name: "Remover Urgente" }));
|
|
25
|
+
expect(aoRemover).toHaveBeenCalledTimes(1);
|
|
26
|
+
});
|
|
27
|
+
it("sem circularRemove o chip removível é o de antes", () => {
|
|
28
|
+
const { container } = render(_jsx(Chips, { label: "x", removable: true }));
|
|
29
|
+
expect(raiz(container)).not.toHaveClass("umds-chip--remove-circle");
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { useRef, useState } from "react";
|
|
4
|
+
import { render, screen, waitFor } from "@testing-library/react";
|
|
5
|
+
import userEvent from "@testing-library/user-event";
|
|
6
|
+
import Coachmark from "../components/Coachmark/Coachmark";
|
|
7
|
+
const PASSOS = [
|
|
8
|
+
{ target: "#alvo-a", title: "Cada item é uma aba", content: "Fluxos e formulários abrem em abas." },
|
|
9
|
+
{ target: "#alvo-b", title: "Salvo sozinho", content: "Tudo fica neste navegador." },
|
|
10
|
+
{ title: "Pronto", content: "Sem alvo: o cartão fica no centro." },
|
|
11
|
+
];
|
|
12
|
+
function Pagina(props) {
|
|
13
|
+
return (_jsxs("div", { children: [_jsx("button", { id: "alvo-a", children: "A" }), _jsx("button", { id: "alvo-b", children: "B" }), _jsx(Coachmark, { steps: PASSOS, ...props })] }));
|
|
14
|
+
}
|
|
15
|
+
describe("Coachmark", () => {
|
|
16
|
+
it("fechado não desenha nada", () => {
|
|
17
|
+
render(_jsx(Pagina, { open: false }));
|
|
18
|
+
expect(screen.queryByRole("dialog")).toBeNull();
|
|
19
|
+
});
|
|
20
|
+
it("aberto é um diálogo modal nomeado pelo título, com contador e texto", () => {
|
|
21
|
+
render(_jsx(Pagina, { open: true }));
|
|
22
|
+
const dialogo = screen.getByRole("dialog", { name: "Cada item é uma aba" });
|
|
23
|
+
expect(dialogo).toHaveAttribute("aria-modal", "true");
|
|
24
|
+
expect(dialogo).toHaveAccessibleDescription("Fluxos e formulários abrem em abas.");
|
|
25
|
+
expect(screen.getAllByText("1 de 3").length).toBeGreaterThan(0);
|
|
26
|
+
expect(screen.getByRole("button", { name: "Próximo" })).toBeInTheDocument();
|
|
27
|
+
expect(screen.queryByRole("button", { name: "Anterior" })).toBeNull();
|
|
28
|
+
});
|
|
29
|
+
it("o foco entra no cartão ao abrir", () => {
|
|
30
|
+
render(_jsx(Pagina, { open: true }));
|
|
31
|
+
expect(screen.getByRole("dialog")).toHaveFocus();
|
|
32
|
+
});
|
|
33
|
+
it("Próximo e Anterior trocam de passo e avisam em onStepChange", async () => {
|
|
34
|
+
const usuario = userEvent.setup();
|
|
35
|
+
const aoMudar = vi.fn();
|
|
36
|
+
render(_jsx(Pagina, { open: true, onStepChange: aoMudar }));
|
|
37
|
+
await usuario.click(screen.getByRole("button", { name: "Próximo" }));
|
|
38
|
+
expect(aoMudar).toHaveBeenLastCalledWith(1);
|
|
39
|
+
expect(screen.getByRole("dialog", { name: "Salvo sozinho" })).toBeInTheDocument();
|
|
40
|
+
await usuario.click(screen.getByRole("button", { name: "Anterior" }));
|
|
41
|
+
expect(aoMudar).toHaveBeenLastCalledWith(0);
|
|
42
|
+
expect(screen.getByRole("dialog", { name: "Cada item é uma aba" })).toBeInTheDocument();
|
|
43
|
+
});
|
|
44
|
+
it("setas direita e esquerda avançam e voltam", async () => {
|
|
45
|
+
const usuario = userEvent.setup();
|
|
46
|
+
render(_jsx(Pagina, { open: true }));
|
|
47
|
+
await usuario.keyboard("{ArrowRight}");
|
|
48
|
+
expect(screen.getByRole("dialog", { name: "Salvo sozinho" })).toBeInTheDocument();
|
|
49
|
+
await usuario.keyboard("{ArrowLeft}");
|
|
50
|
+
expect(screen.getByRole("dialog", { name: "Cada item é uma aba" })).toBeInTheDocument();
|
|
51
|
+
});
|
|
52
|
+
it("o último passo troca Próximo por Concluir e fecha com 'finish'", async () => {
|
|
53
|
+
const usuario = userEvent.setup();
|
|
54
|
+
const aoFechar = vi.fn();
|
|
55
|
+
render(_jsx(Pagina, { open: true, defaultStep: 2, onClose: aoFechar }));
|
|
56
|
+
expect(screen.queryByRole("button", { name: "Próximo" })).toBeNull();
|
|
57
|
+
await usuario.click(screen.getByRole("button", { name: "Concluir" }));
|
|
58
|
+
expect(aoFechar).toHaveBeenCalledWith("finish");
|
|
59
|
+
});
|
|
60
|
+
it("Sair do tour fecha com 'skip' e hideSkip esconde o botão", async () => {
|
|
61
|
+
const usuario = userEvent.setup();
|
|
62
|
+
const aoFechar = vi.fn();
|
|
63
|
+
const { rerender } = render(_jsx(Pagina, { open: true, onClose: aoFechar }));
|
|
64
|
+
await usuario.click(screen.getByRole("button", { name: "Sair do tour" }));
|
|
65
|
+
expect(aoFechar).toHaveBeenCalledWith("skip");
|
|
66
|
+
rerender(_jsx(Pagina, { open: true, hideSkip: true, onClose: aoFechar }));
|
|
67
|
+
expect(screen.queryByRole("button", { name: "Sair do tour" })).toBeNull();
|
|
68
|
+
});
|
|
69
|
+
it("Esc fecha com 'escape'", async () => {
|
|
70
|
+
const usuario = userEvent.setup();
|
|
71
|
+
const aoFechar = vi.fn();
|
|
72
|
+
render(_jsx(Pagina, { open: true, onClose: aoFechar }));
|
|
73
|
+
await usuario.keyboard("{Escape}");
|
|
74
|
+
expect(aoFechar).toHaveBeenCalledWith("escape");
|
|
75
|
+
});
|
|
76
|
+
it("o foco fica preso no cartão: Tab gira e Shift+Tab volta", async () => {
|
|
77
|
+
const usuario = userEvent.setup();
|
|
78
|
+
render(_jsx(Pagina, { open: true }));
|
|
79
|
+
const sair = screen.getByRole("button", { name: "Sair do tour" });
|
|
80
|
+
const proximo = screen.getByRole("button", { name: "Próximo" });
|
|
81
|
+
proximo.focus();
|
|
82
|
+
await usuario.tab();
|
|
83
|
+
expect(sair).toHaveFocus();
|
|
84
|
+
await usuario.tab({ shift: true });
|
|
85
|
+
expect(proximo).toHaveFocus();
|
|
86
|
+
});
|
|
87
|
+
it("todos os textos vêm por prop", () => {
|
|
88
|
+
render(_jsx(Pagina, { open: true, defaultStep: 1, nextLabel: "Next", previousLabel: "Back", skipLabel: "Skip tour", counterLabel: "{current} of {total}" }));
|
|
89
|
+
expect(screen.getByRole("button", { name: "Next" })).toBeInTheDocument();
|
|
90
|
+
expect(screen.getByRole("button", { name: "Back" })).toBeInTheDocument();
|
|
91
|
+
expect(screen.getByRole("button", { name: "Skip tour" })).toBeInTheDocument();
|
|
92
|
+
expect(screen.getAllByText("2 of 3").length).toBeGreaterThan(0);
|
|
93
|
+
});
|
|
94
|
+
it("finishLabel troca o texto do último passo", () => {
|
|
95
|
+
render(_jsx(Pagina, { open: true, defaultStep: 2, finishLabel: "Done" }));
|
|
96
|
+
expect(screen.getByRole("button", { name: "Done" })).toBeInTheDocument();
|
|
97
|
+
});
|
|
98
|
+
it("com alvo, desenha o recorte; sem alvo (ou alvo que não existe), não", () => {
|
|
99
|
+
const { container, rerender } = render(_jsx(Pagina, { open: true }));
|
|
100
|
+
expect(document.querySelector(".umds-coachmark__cutout")).not.toBeNull();
|
|
101
|
+
rerender(_jsx(Pagina, { open: true, step: 2 }));
|
|
102
|
+
expect(document.querySelector(".umds-coachmark__cutout")).toBeNull();
|
|
103
|
+
expect(document.querySelector(".umds-coachmark--no-target")).not.toBeNull();
|
|
104
|
+
void container;
|
|
105
|
+
});
|
|
106
|
+
it("alvo por ref do React e por elemento", () => {
|
|
107
|
+
function ComRef() {
|
|
108
|
+
const ref = useRef(null);
|
|
109
|
+
return (_jsxs("div", { children: [_jsx("button", { ref: ref, children: "Alvo" }), _jsx(Coachmark, { open: true, steps: [{ target: ref, title: "Por ref" }] })] }));
|
|
110
|
+
}
|
|
111
|
+
render(_jsx(ComRef, {}));
|
|
112
|
+
expect(screen.getByRole("dialog", { name: "Por ref" })).toBeInTheDocument();
|
|
113
|
+
expect(document.querySelector(".umds-coachmark__cutout")).not.toBeNull();
|
|
114
|
+
});
|
|
115
|
+
it("step controlado manda no passo atual", () => {
|
|
116
|
+
render(_jsx(Pagina, { open: true, step: 1 }));
|
|
117
|
+
expect(screen.getByRole("dialog", { name: "Salvo sozinho" })).toBeInTheDocument();
|
|
118
|
+
});
|
|
119
|
+
it("ao fechar, o foco volta ao elemento de antes", async () => {
|
|
120
|
+
const usuario = userEvent.setup();
|
|
121
|
+
function Dono() {
|
|
122
|
+
const [aberto, setAberto] = useState(false);
|
|
123
|
+
return (_jsxs("div", { children: [_jsx("button", { id: "abrir", onClick: () => setAberto(true), children: "Fazer o tour" }), _jsx(Coachmark, { open: aberto, steps: PASSOS, onClose: () => setAberto(false) })] }));
|
|
124
|
+
}
|
|
125
|
+
render(_jsx(Dono, {}));
|
|
126
|
+
const abrir = screen.getByRole("button", { name: "Fazer o tour" });
|
|
127
|
+
await usuario.click(abrir);
|
|
128
|
+
expect(screen.getByRole("dialog")).toBeInTheDocument();
|
|
129
|
+
// O foco entra no cartão depois da montagem (um quadro de animação): na CI,
|
|
130
|
+
// mais lenta, o Esc chegava antes e caía no body.
|
|
131
|
+
await waitFor(() => expect(screen.getByRole("dialog").contains(document.activeElement)).toBe(true));
|
|
132
|
+
await usuario.keyboard("{Escape}");
|
|
133
|
+
await waitFor(() => expect(screen.queryByRole("dialog")).toBeNull());
|
|
134
|
+
expect(abrir).toHaveFocus();
|
|
135
|
+
});
|
|
136
|
+
it("reabrir recomeça do primeiro passo", async () => {
|
|
137
|
+
const usuario = userEvent.setup();
|
|
138
|
+
function Dono() {
|
|
139
|
+
const [aberto, setAberto] = useState(true);
|
|
140
|
+
return (_jsxs("div", { children: [_jsx("button", { onClick: () => setAberto(true), children: "Reabrir" }), _jsx(Coachmark, { open: aberto, steps: PASSOS, onClose: () => setAberto(false) })] }));
|
|
141
|
+
}
|
|
142
|
+
render(_jsx(Dono, {}));
|
|
143
|
+
await usuario.keyboard("{ArrowRight}");
|
|
144
|
+
expect(screen.getByRole("dialog", { name: "Salvo sozinho" })).toBeInTheDocument();
|
|
145
|
+
await usuario.keyboard("{Escape}");
|
|
146
|
+
await usuario.click(screen.getByRole("button", { name: "Reabrir" }));
|
|
147
|
+
expect(screen.getByRole("dialog", { name: "Cada item é uma aba" })).toBeInTheDocument();
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { render, screen } from "@testing-library/react";
|
|
5
|
+
import userEvent from "@testing-library/user-event";
|
|
6
|
+
import ColorPicker from "../components/ColorPicker/ColorPicker";
|
|
7
|
+
import { COLOR_PALETTE } from "../utils/paleta-de-cores";
|
|
8
|
+
describe("ColorPicker", () => {
|
|
9
|
+
it("é um radiogroup com uma amostra por primitivo de cor, cada uma com nome", () => {
|
|
10
|
+
render(_jsx(ColorPicker, { ariaLabel: "Cor do marcador" }));
|
|
11
|
+
const grupo = screen.getByRole("radiogroup", { name: "Cor do marcador" });
|
|
12
|
+
expect(grupo.querySelectorAll('[role="radio"]')).toHaveLength(COLOR_PALETTE.length);
|
|
13
|
+
expect(COLOR_PALETTE).toHaveLength(31);
|
|
14
|
+
expect(screen.getByRole("radio", { name: "Azul escuro" })).toBeInTheDocument();
|
|
15
|
+
expect(screen.getByRole("radio", { name: "Cinza muito claro" })).toBeInTheDocument();
|
|
16
|
+
});
|
|
17
|
+
it("pinta cada amostra com a variável do token", () => {
|
|
18
|
+
render(_jsx(ColorPicker, { ariaLabel: "Cor" }));
|
|
19
|
+
const azul = screen.getByRole("radio", { name: "Azul" });
|
|
20
|
+
expect(azul.style.backgroundColor).toBe("var(--umds-primitive-blue-pure)");
|
|
21
|
+
});
|
|
22
|
+
it("clicar escolhe e entrega o NOME do token em onChange", async () => {
|
|
23
|
+
const usuario = userEvent.setup();
|
|
24
|
+
const aoMudar = vi.fn();
|
|
25
|
+
render(_jsx(ColorPicker, { ariaLabel: "Cor", onChange: aoMudar }));
|
|
26
|
+
await usuario.click(screen.getByRole("radio", { name: "Verde" }));
|
|
27
|
+
expect(aoMudar).toHaveBeenCalledWith("--umds-primitive-green-pure");
|
|
28
|
+
expect(screen.getByRole("radio", { name: "Verde" })).toHaveAttribute("aria-checked", "true");
|
|
29
|
+
});
|
|
30
|
+
it("não escolhido tem aria-checked false e só a escolhida mostra a marcação", () => {
|
|
31
|
+
const { container } = render(_jsx(ColorPicker, { ariaLabel: "Cor", defaultValue: "--umds-primitive-red-pure" }));
|
|
32
|
+
expect(screen.getByRole("radio", { name: "Vermelho" })).toHaveAttribute("aria-checked", "true");
|
|
33
|
+
expect(screen.getByRole("radio", { name: "Azul" })).toHaveAttribute("aria-checked", "false");
|
|
34
|
+
expect(container.querySelectorAll(".umds-color-picker__swatch svg")).toHaveLength(1);
|
|
35
|
+
});
|
|
36
|
+
it("Tab entra na amostra escolhida (roving tabindex)", () => {
|
|
37
|
+
render(_jsx(ColorPicker, { ariaLabel: "Cor", value: "--umds-primitive-orange-pure" }));
|
|
38
|
+
const radios = screen.getAllByRole("radio");
|
|
39
|
+
const comTab = radios.filter((r) => r.getAttribute("tabindex") === "0");
|
|
40
|
+
expect(comTab).toHaveLength(1);
|
|
41
|
+
expect(comTab[0]).toHaveAccessibleName("Laranja");
|
|
42
|
+
});
|
|
43
|
+
it("sem valor, Tab entra na primeira amostra", () => {
|
|
44
|
+
render(_jsx(ColorPicker, { ariaLabel: "Cor" }));
|
|
45
|
+
expect(screen.getAllByRole("radio")[0]).toHaveAttribute("tabindex", "0");
|
|
46
|
+
});
|
|
47
|
+
it("setas movem a escolha e o foco; Home e End vão às pontas; dá a volta", async () => {
|
|
48
|
+
const usuario = userEvent.setup();
|
|
49
|
+
const aoMudar = vi.fn();
|
|
50
|
+
render(_jsx(ColorPicker, { ariaLabel: "Cor", families: ["blue"], defaultValue: "--umds-primitive-blue-dark", onChange: aoMudar }));
|
|
51
|
+
const radios = screen.getAllByRole("radio");
|
|
52
|
+
radios[0].focus();
|
|
53
|
+
await usuario.keyboard("{ArrowRight}");
|
|
54
|
+
expect(aoMudar).toHaveBeenLastCalledWith("--umds-primitive-blue-pure");
|
|
55
|
+
expect(radios[1]).toHaveFocus();
|
|
56
|
+
await usuario.keyboard("{ArrowLeft}{ArrowLeft}");
|
|
57
|
+
expect(aoMudar).toHaveBeenLastCalledWith("--umds-primitive-blue-light");
|
|
58
|
+
expect(radios[3]).toHaveFocus();
|
|
59
|
+
await usuario.keyboard("{Home}");
|
|
60
|
+
expect(radios[0]).toHaveFocus();
|
|
61
|
+
await usuario.keyboard("{End}");
|
|
62
|
+
expect(radios[3]).toHaveFocus();
|
|
63
|
+
await usuario.keyboard("{ArrowDown}");
|
|
64
|
+
expect(radios[0]).toHaveFocus();
|
|
65
|
+
});
|
|
66
|
+
it("pula amostra desabilitada no teclado", async () => {
|
|
67
|
+
const usuario = userEvent.setup();
|
|
68
|
+
const aoMudar = vi.fn();
|
|
69
|
+
render(_jsx(ColorPicker, { ariaLabel: "Cor", defaultValue: "a", onChange: aoMudar, swatches: [
|
|
70
|
+
{ value: "a", label: "A" },
|
|
71
|
+
{ value: "b", label: "B", disabled: true },
|
|
72
|
+
{ value: "c", label: "C" },
|
|
73
|
+
] }));
|
|
74
|
+
screen.getByRole("radio", { name: "A" }).focus();
|
|
75
|
+
await usuario.keyboard("{ArrowRight}");
|
|
76
|
+
expect(aoMudar).toHaveBeenLastCalledWith("c");
|
|
77
|
+
});
|
|
78
|
+
it("families recorta a paleta", () => {
|
|
79
|
+
render(_jsx(ColorPicker, { ariaLabel: "Cor", families: ["blue", "gray"] }));
|
|
80
|
+
expect(screen.getAllByRole("radio")).toHaveLength(11);
|
|
81
|
+
});
|
|
82
|
+
it("swatches substitui a paleta (nomes traduzidos)", () => {
|
|
83
|
+
render(_jsx(ColorPicker, { ariaLabel: "Color", swatches: [{ value: "--umds-primitive-blue-pure", label: "Blue" }] }));
|
|
84
|
+
expect(screen.getAllByRole("radio")).toHaveLength(1);
|
|
85
|
+
expect(screen.getByRole("radio", { name: "Blue" })).toBeInTheDocument();
|
|
86
|
+
});
|
|
87
|
+
it("disabled desabilita todas as amostras", () => {
|
|
88
|
+
render(_jsx(ColorPicker, { ariaLabel: "Cor", disabled: true }));
|
|
89
|
+
for (const r of screen.getAllByRole("radio"))
|
|
90
|
+
expect(r).toBeDisabled();
|
|
91
|
+
});
|
|
92
|
+
it("label visível nomeia o grupo", () => {
|
|
93
|
+
render(_jsx(ColorPicker, { label: "Cor de fundo", id: "cp" }));
|
|
94
|
+
expect(screen.getByRole("radiogroup", { name: "Cor de fundo" })).toBeInTheDocument();
|
|
95
|
+
});
|
|
96
|
+
it("cor livre vem desligada: sem campo de hexadecimal", () => {
|
|
97
|
+
render(_jsx(ColorPicker, { ariaLabel: "Cor" }));
|
|
98
|
+
expect(screen.queryByLabelText(/hexadecimal/i)).toBeNull();
|
|
99
|
+
});
|
|
100
|
+
it("allowCustom: hexadecimal completo vira #RRGGBB em maiúsculas; incompleto não emite", async () => {
|
|
101
|
+
const usuario = userEvent.setup();
|
|
102
|
+
const aoMudar = vi.fn();
|
|
103
|
+
render(_jsx(ColorPicker, { ariaLabel: "Cor", allowCustom: true, onChange: aoMudar }));
|
|
104
|
+
const campo = screen.getByLabelText("Cor livre (hexadecimal)");
|
|
105
|
+
await usuario.type(campo, "0058d");
|
|
106
|
+
expect(aoMudar).not.toHaveBeenCalled();
|
|
107
|
+
await usuario.type(campo, "b");
|
|
108
|
+
expect(aoMudar).toHaveBeenCalledTimes(1);
|
|
109
|
+
expect(aoMudar).toHaveBeenCalledWith("#0058DB");
|
|
110
|
+
});
|
|
111
|
+
it("allowCustom: hexadecimal inválido mostra a mensagem ao sair do campo", async () => {
|
|
112
|
+
const usuario = userEvent.setup();
|
|
113
|
+
render(_jsx(ColorPicker, { ariaLabel: "Cor", allowCustom: true, customErrorMessage: "Formato inv\u00E1lido" }));
|
|
114
|
+
const campo = screen.getByLabelText("Cor livre (hexadecimal)");
|
|
115
|
+
await usuario.type(campo, "xyz");
|
|
116
|
+
expect(screen.queryByText("Formato inválido")).toBeNull();
|
|
117
|
+
await usuario.tab();
|
|
118
|
+
expect(screen.getByText("Formato inválido")).toBeInTheDocument();
|
|
119
|
+
});
|
|
120
|
+
it("controlado: reflete o value e escolher uma amostra limpa o campo livre", async () => {
|
|
121
|
+
const usuario = userEvent.setup();
|
|
122
|
+
function Controlado() {
|
|
123
|
+
const [valor, setValor] = useState("#336699");
|
|
124
|
+
return _jsx(ColorPicker, { ariaLabel: "Cor", allowCustom: true, value: valor, onChange: setValor });
|
|
125
|
+
}
|
|
126
|
+
render(_jsx(Controlado, {}));
|
|
127
|
+
const campo = screen.getByLabelText("Cor livre (hexadecimal)");
|
|
128
|
+
expect(campo.value).toBe("#336699");
|
|
129
|
+
await usuario.click(screen.getByRole("radio", { name: "Azul" }));
|
|
130
|
+
expect(campo.value).toBe("");
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { render, screen, within } from "@testing-library/react";
|
|
4
|
+
import DataTable from "../components/DataTable/DataTable";
|
|
5
|
+
/**
|
|
6
|
+
* Célula livre (`render` devolvendo nó) e linha de grupo (`rowGroup`).
|
|
7
|
+
* No Vue o equivalente é o slot `#cell-<key>`: ver `datatable-slots.test.mjs`.
|
|
8
|
+
*/
|
|
9
|
+
const USUARIOS = [
|
|
10
|
+
{ id: 1, nome: "Lucas Andrade", setor: "SADM", perfil: "Administrador" },
|
|
11
|
+
{ id: 2, nome: "Marina Souza", setor: "SADM", perfil: "Nível 1" },
|
|
12
|
+
{ id: 3, nome: "Rafael Pereira", setor: "SOBR", perfil: "Nível 2" },
|
|
13
|
+
];
|
|
14
|
+
describe("DataTable · célula livre", () => {
|
|
15
|
+
it("render devolvendo elemento desenha o nó, não texto", () => {
|
|
16
|
+
const colunas = [
|
|
17
|
+
{ key: "nome", label: "Usuário", render: (row) => _jsx("strong", { "data-testid": "nome", children: row.nome }) },
|
|
18
|
+
{ key: "perfil", label: "Perfil" },
|
|
19
|
+
];
|
|
20
|
+
render(_jsx(DataTable, { columns: colunas, data: USUARIOS }));
|
|
21
|
+
const nos = screen.getAllByTestId("nome");
|
|
22
|
+
expect(nos).toHaveLength(3);
|
|
23
|
+
expect(nos[0].tagName).toBe("STRONG");
|
|
24
|
+
expect(nos[0]).toHaveTextContent("Lucas Andrade");
|
|
25
|
+
});
|
|
26
|
+
it("render recebe a linha e o índice", () => {
|
|
27
|
+
const colunas = [
|
|
28
|
+
{ key: "nome", label: "Usuário", render: (row, i) => `${i + 1}. ${row.nome}` },
|
|
29
|
+
];
|
|
30
|
+
render(_jsx(DataTable, { columns: colunas, data: USUARIOS }));
|
|
31
|
+
expect(screen.getByText("2. Marina Souza")).toBeInTheDocument();
|
|
32
|
+
});
|
|
33
|
+
it("DataTableColumn é exportado e tipa a coluna com render de nó", () => {
|
|
34
|
+
const coluna = {
|
|
35
|
+
key: "acoes",
|
|
36
|
+
label: "Ações",
|
|
37
|
+
render: () => _jsx("button", { type: "button", children: "Editar" }),
|
|
38
|
+
};
|
|
39
|
+
render(_jsx(DataTable, { columns: [coluna], data: USUARIOS }));
|
|
40
|
+
expect(screen.getAllByRole("button", { name: "Editar" })).toHaveLength(3);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe("DataTable · linha de grupo (rowGroup)", () => {
|
|
44
|
+
const colunas = [
|
|
45
|
+
{ key: "nome", label: "Usuário" },
|
|
46
|
+
{ key: "perfil", label: "Perfil" },
|
|
47
|
+
];
|
|
48
|
+
it("sem rowGroup não desenha linha de grupo", () => {
|
|
49
|
+
const { container } = render(_jsx(DataTable, { columns: colunas, data: USUARIOS }));
|
|
50
|
+
expect(container.querySelector("th[scope='rowgroup']")).toBeNull();
|
|
51
|
+
});
|
|
52
|
+
it("rowGroup por campo abre um grupo por valor consecutivo", () => {
|
|
53
|
+
const { container } = render(_jsx(DataTable, { columns: colunas, data: USUARIOS, rowGroup: "setor" }));
|
|
54
|
+
const grupos = container.querySelectorAll("th[scope='rowgroup']");
|
|
55
|
+
expect(Array.from(grupos).map((g) => g.textContent)).toEqual(["SADM", "SOBR"]);
|
|
56
|
+
// a linha de grupo atravessa as colunas todas
|
|
57
|
+
expect(grupos[0]).toHaveAttribute("colspan", "2");
|
|
58
|
+
});
|
|
59
|
+
it("rowGroup por função e colspan com selectable", () => {
|
|
60
|
+
const { container } = render(_jsx(DataTable, { columns: colunas, data: USUARIOS, selectable: true, rowGroup: (row) => (row.id === 3 ? undefined : "Equipe A") }));
|
|
61
|
+
const grupos = container.querySelectorAll("th[scope='rowgroup']");
|
|
62
|
+
expect(grupos).toHaveLength(1);
|
|
63
|
+
expect(grupos[0]).toHaveTextContent("Equipe A");
|
|
64
|
+
expect(grupos[0]).toHaveAttribute("colspan", "3");
|
|
65
|
+
});
|
|
66
|
+
it("renderRowGroup devolve nó", () => {
|
|
67
|
+
render(_jsx(DataTable, { columns: colunas, data: USUARIOS, rowGroup: "setor", renderRowGroup: (rotulo) => _jsxs("em", { "data-testid": "grupo", children: [rotulo, "!"] }) }));
|
|
68
|
+
expect(screen.getAllByTestId("grupo").map((n) => n.textContent)).toEqual(["SADM!", "SOBR!"]);
|
|
69
|
+
});
|
|
70
|
+
it("a linha de grupo não entra na seleção por índice", async () => {
|
|
71
|
+
const { container } = render(_jsx(DataTable, { columns: colunas, data: USUARIOS, rowGroup: "setor", selectable: true, selectedRows: [2] }));
|
|
72
|
+
const selecionadas = container.querySelectorAll(".umds-datatable__row--selected");
|
|
73
|
+
expect(selecionadas).toHaveLength(1);
|
|
74
|
+
expect(within(selecionadas[0]).getByText("Rafael Pereira")).toBeInTheDocument();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|