@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,153 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { render, screen } from "@testing-library/react";
|
|
4
|
+
import userEvent from "@testing-library/user-event";
|
|
5
|
+
import { faInbox } from "@fortawesome/free-solid-svg-icons";
|
|
6
|
+
import List from "../components/List/List";
|
|
7
|
+
import ListItem from "../components/List/ListItem";
|
|
8
|
+
describe("ListItem", () => {
|
|
9
|
+
it("sem as nem href é um button dentro de um li", () => {
|
|
10
|
+
const { container } = render(_jsx("ul", { children: _jsx(ListItem, { title: "Memorando 114/2025" }) }));
|
|
11
|
+
const botao = screen.getByRole("button", { name: /Memorando 114\/2025/ });
|
|
12
|
+
expect(botao.tagName).toBe("BUTTON");
|
|
13
|
+
expect(botao).toHaveAttribute("type", "button");
|
|
14
|
+
expect(botao.parentElement?.tagName).toBe("LI");
|
|
15
|
+
expect(container.querySelectorAll("li")).toHaveLength(1);
|
|
16
|
+
});
|
|
17
|
+
it("com href vira link; target _blank ganha rel seguro", () => {
|
|
18
|
+
render(_jsx("ul", { children: _jsx(ListItem, { title: "Of\u00EDcio", href: "/oficios/31", target: "_blank" }) }));
|
|
19
|
+
const link = screen.getByRole("link", { name: /Ofício/ });
|
|
20
|
+
expect(link).toHaveAttribute("href", "/oficios/31");
|
|
21
|
+
expect(link).toHaveAttribute("rel", "noopener noreferrer");
|
|
22
|
+
});
|
|
23
|
+
it("as='div' sem onClick é só leitura: sem role nem tabindex", () => {
|
|
24
|
+
const { container } = render(_jsx("ul", { children: _jsx(ListItem, { as: "div", title: "Somente leitura" }) }));
|
|
25
|
+
const linha = container.querySelector(".umds-list-item");
|
|
26
|
+
expect(linha.tagName).toBe("DIV");
|
|
27
|
+
expect(linha).not.toHaveAttribute("role");
|
|
28
|
+
expect(linha).not.toHaveAttribute("tabindex");
|
|
29
|
+
expect(linha).not.toHaveClass("umds-list-item--interactive");
|
|
30
|
+
});
|
|
31
|
+
it("as='div' com onClick é acionável por Enter e Espaço", async () => {
|
|
32
|
+
const usuario = userEvent.setup();
|
|
33
|
+
const aoClicar = vi.fn();
|
|
34
|
+
render(_jsx("ul", { children: _jsx(ListItem, { as: "div", title: "Acion\u00E1vel", onClick: aoClicar }) }));
|
|
35
|
+
const linha = screen.getByRole("button", { name: /Acionável/ });
|
|
36
|
+
linha.focus();
|
|
37
|
+
await usuario.keyboard("{Enter}");
|
|
38
|
+
await usuario.keyboard(" ");
|
|
39
|
+
expect(aoClicar).toHaveBeenCalledTimes(2);
|
|
40
|
+
});
|
|
41
|
+
it("monta título, subtítulo, meta, início e fim nos lugares certos", () => {
|
|
42
|
+
const { container } = render(_jsx("ul", { children: _jsx(ListItem, { title: "Memorando 114/2025", subtitle: "Novo contrato - Governo", meta: "23 de agosto de 2025", start: _jsx("span", { "data-testid": "inicio", children: "AV" }), end: _jsx("span", { "data-testid": "fim", children: "novo" }), children: _jsx("span", { "data-testid": "livre", children: "conte\u00FAdo livre" }) }) }));
|
|
43
|
+
expect(container.querySelector(".umds-list-item__title")).toHaveTextContent("Memorando 114/2025");
|
|
44
|
+
expect(container.querySelector(".umds-list-item__subtitle")).toHaveTextContent("Novo contrato - Governo");
|
|
45
|
+
expect(container.querySelector(".umds-list-item__meta")).toHaveTextContent("23 de agosto de 2025");
|
|
46
|
+
expect(container.querySelector(".umds-list-item__start")).toContainElement(screen.getByTestId("inicio"));
|
|
47
|
+
expect(container.querySelector(".umds-list-item__end")).toContainElement(screen.getByTestId("fim"));
|
|
48
|
+
expect(container.querySelector(".umds-list-item__body")).toContainElement(screen.getByTestId("livre"));
|
|
49
|
+
});
|
|
50
|
+
it("title aceita nó e não vira o atributo title do HTML", () => {
|
|
51
|
+
const { container } = render(_jsx("ul", { children: _jsx(ListItem, { title: _jsx("b", { "data-testid": "rico", children: "Rico" }) }) }));
|
|
52
|
+
expect(screen.getByTestId("rico")).toBeInTheDocument();
|
|
53
|
+
expect(container.querySelector("[title]")).toBeNull();
|
|
54
|
+
});
|
|
55
|
+
it("sem start, end e textos, não desenha as regiões vazias", () => {
|
|
56
|
+
const { container } = render(_jsx("ul", { children: _jsx(ListItem, { title: "S\u00F3 t\u00EDtulo" }) }));
|
|
57
|
+
expect(container.querySelector(".umds-list-item__start")).toBeNull();
|
|
58
|
+
expect(container.querySelector(".umds-list-item__end")).toBeNull();
|
|
59
|
+
expect(container.querySelector(".umds-list-item__subtitle")).toBeNull();
|
|
60
|
+
expect(container.querySelector(".umds-list-item__meta")).toBeNull();
|
|
61
|
+
});
|
|
62
|
+
it("selected marca aria-current e a classe de seleção", () => {
|
|
63
|
+
render(_jsx("ul", { children: _jsx(ListItem, { title: "Atual", selected: true }) }));
|
|
64
|
+
const botao = screen.getByRole("button", { name: /Atual/ });
|
|
65
|
+
expect(botao).toHaveAttribute("aria-current", "true");
|
|
66
|
+
expect(botao).toHaveClass("umds-list-item--selected");
|
|
67
|
+
});
|
|
68
|
+
it("disabled bloqueia o clique e o foco do botão", async () => {
|
|
69
|
+
const usuario = userEvent.setup();
|
|
70
|
+
const aoClicar = vi.fn();
|
|
71
|
+
render(_jsx("ul", { children: _jsx(ListItem, { title: "Bloqueada", disabled: true, onClick: aoClicar }) }));
|
|
72
|
+
const botao = screen.getByRole("button", { name: /Bloqueada/ });
|
|
73
|
+
expect(botao).toBeDisabled();
|
|
74
|
+
await usuario.click(botao);
|
|
75
|
+
expect(aoClicar).not.toHaveBeenCalled();
|
|
76
|
+
});
|
|
77
|
+
it("link disabled perde o href e declara aria-disabled", () => {
|
|
78
|
+
render(_jsx("ul", { children: _jsx(ListItem, { title: "Link bloqueado", href: "/x", disabled: true }) }));
|
|
79
|
+
const link = screen.getByText("Link bloqueado").closest("a");
|
|
80
|
+
expect(link).not.toHaveAttribute("href");
|
|
81
|
+
expect(link).toHaveAttribute("aria-disabled", "true");
|
|
82
|
+
});
|
|
83
|
+
it("clique chama onClick com o evento", async () => {
|
|
84
|
+
const usuario = userEvent.setup();
|
|
85
|
+
const aoClicar = vi.fn();
|
|
86
|
+
render(_jsx("ul", { children: _jsx(ListItem, { title: "Abrir", onClick: aoClicar }) }));
|
|
87
|
+
await usuario.click(screen.getByRole("button", { name: /Abrir/ }));
|
|
88
|
+
expect(aoClicar).toHaveBeenCalledTimes(1);
|
|
89
|
+
});
|
|
90
|
+
it("repassa aria-label, id, data-* e style ao elemento interativo", () => {
|
|
91
|
+
render(_jsx("ul", { children: _jsx(ListItem, { "aria-label": "Abrir memorando", id: "item-1", "data-origem": "home", style: { marginTop: 4 } }) }));
|
|
92
|
+
const botao = screen.getByRole("button", { name: "Abrir memorando" });
|
|
93
|
+
expect(botao).toHaveAttribute("id", "item-1");
|
|
94
|
+
expect(botao).toHaveAttribute("data-origem", "home");
|
|
95
|
+
expect(botao).toHaveStyle({ marginTop: "4px" });
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
describe("List", () => {
|
|
99
|
+
it("é uma ul com role list, nome acessível e gap padrão", () => {
|
|
100
|
+
render(_jsxs(List, { ariaLabel: "Documentos n\u00E3o lidos", children: [_jsx(ListItem, { title: "A" }), _jsx(ListItem, { title: "B" })] }));
|
|
101
|
+
const lista = screen.getByRole("list", { name: "Documentos não lidos" });
|
|
102
|
+
expect(lista.tagName).toBe("UL");
|
|
103
|
+
expect(lista).toHaveClass("umds-list--gap-quarck");
|
|
104
|
+
expect(screen.getAllByRole("listitem")).toHaveLength(2);
|
|
105
|
+
});
|
|
106
|
+
it("as='ol' e gap trocam o elemento e a classe", () => {
|
|
107
|
+
render(_jsx(List, { as: "ol", gap: "nano", ariaLabel: "Passos", children: _jsx(ListItem, { title: "A" }) }));
|
|
108
|
+
const lista = screen.getByRole("list", { name: "Passos" });
|
|
109
|
+
expect(lista.tagName).toBe("OL");
|
|
110
|
+
expect(lista).toHaveClass("umds-list--gap-nano");
|
|
111
|
+
});
|
|
112
|
+
it("ariaLabelledby aponta para o título visível", () => {
|
|
113
|
+
render(_jsxs(_Fragment, { children: [_jsx("h2", { id: "titulo", children: "Prazos a vencer" }), _jsx(List, { ariaLabelledby: "titulo", children: _jsx(ListItem, { title: "A" }) })] }));
|
|
114
|
+
expect(screen.getByRole("list", { name: "Prazos a vencer" })).toBeInTheDocument();
|
|
115
|
+
});
|
|
116
|
+
it("sem isEmpty mostra os itens e nunca o estado vazio", () => {
|
|
117
|
+
render(_jsx(List, { ariaLabel: "Lista", emptyTitle: "Nada aqui", children: _jsx(ListItem, { title: "A" }) }));
|
|
118
|
+
expect(screen.queryByText("Nada aqui")).toBeNull();
|
|
119
|
+
});
|
|
120
|
+
it("isEmpty troca os itens pelo EmptyState dentro da própria lista", () => {
|
|
121
|
+
const aoAgir = vi.fn();
|
|
122
|
+
const { container } = render(_jsx(List, { ariaLabel: "Lista", isEmpty: true, emptyTitle: "Nenhum documento", emptyDescription: "Tudo em dia.", emptyIcon: faInbox, emptyActionLabel: "Atualizar", onEmptyAction: aoAgir, children: _jsx(ListItem, { title: "N\u00E3o aparece" }) }));
|
|
123
|
+
expect(screen.queryByRole("list")).toBeNull();
|
|
124
|
+
expect(screen.queryByText("Não aparece")).toBeNull();
|
|
125
|
+
const raiz = container.firstElementChild;
|
|
126
|
+
expect(raiz).toHaveAttribute("role", "status");
|
|
127
|
+
expect(raiz).toHaveClass("umds-list--empty");
|
|
128
|
+
expect(raiz.querySelector(".umds-empty-state")).not.toBeNull();
|
|
129
|
+
expect(screen.getByText("Nenhum documento")).toBeInTheDocument();
|
|
130
|
+
expect(screen.getByText("Tudo em dia.")).toBeInTheDocument();
|
|
131
|
+
expect(raiz.querySelector(".umds-empty-state--sm")).not.toBeNull();
|
|
132
|
+
screen.getByRole("button", { name: "Atualizar" }).click();
|
|
133
|
+
expect(aoAgir).toHaveBeenCalledTimes(1);
|
|
134
|
+
});
|
|
135
|
+
it("isEmpty sem título usa o texto padrão", () => {
|
|
136
|
+
render(_jsx(List, { ariaLabel: "Lista", isEmpty: true }));
|
|
137
|
+
expect(screen.getByText("Nenhum item encontrado")).toBeInTheDocument();
|
|
138
|
+
});
|
|
139
|
+
it("o conteúdo ao redor da lista não é desmontado ao esvaziar", () => {
|
|
140
|
+
const Tela = ({ vazia }) => (_jsxs("section", { children: [_jsx("h2", { children: "Documentos" }), _jsx("input", { "aria-label": "Buscar" }), _jsx(List, { ariaLabel: "Documentos", isEmpty: vazia, children: _jsx(ListItem, { title: "A" }) }), _jsx("nav", { "aria-label": "Pagina\u00E7\u00E3o", children: "p\u00E1g. 1" })] }));
|
|
141
|
+
const { rerender } = render(_jsx(Tela, { vazia: false }));
|
|
142
|
+
const busca = screen.getByLabelText("Buscar");
|
|
143
|
+
rerender(_jsx(Tela, { vazia: true }));
|
|
144
|
+
expect(screen.getByLabelText("Buscar")).toBe(busca);
|
|
145
|
+
expect(screen.getByRole("heading", { name: "Documentos" })).toBeInTheDocument();
|
|
146
|
+
expect(screen.getByRole("navigation", { name: "Paginação" })).toBeInTheDocument();
|
|
147
|
+
});
|
|
148
|
+
it("slot empty (prop empty no React) substitui o EmptyState", () => {
|
|
149
|
+
render(_jsx(List, { ariaLabel: "Lista", isEmpty: true, empty: _jsx("p", { "data-testid": "vazio", children: "Nada por aqui" }) }));
|
|
150
|
+
expect(screen.getByTestId("vazio")).toBeInTheDocument();
|
|
151
|
+
expect(document.querySelector(".umds-empty-state")).toBeNull();
|
|
152
|
+
});
|
|
153
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { act, fireEvent, render, screen } from "@testing-library/react";
|
|
4
|
+
import Modal from "../components/Modal/Modal";
|
|
5
|
+
import Select from "../components/Select/Select";
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
vi.unstubAllGlobals();
|
|
8
|
+
});
|
|
9
|
+
/** Troca `window.matchMedia` por um que responde `matches` e deixa disparar `change`. */
|
|
10
|
+
function simularTela(matches) {
|
|
11
|
+
const ouvintes = new Set();
|
|
12
|
+
vi.stubGlobal("matchMedia", (consulta) => ({
|
|
13
|
+
matches,
|
|
14
|
+
media: consulta,
|
|
15
|
+
addEventListener: (_, fn) => ouvintes.add(fn),
|
|
16
|
+
removeEventListener: (_, fn) => ouvintes.delete(fn),
|
|
17
|
+
}));
|
|
18
|
+
return (novo) => ouvintes.forEach((fn) => fn({ matches: novo }));
|
|
19
|
+
}
|
|
20
|
+
describe("Modal · variant primary", () => {
|
|
21
|
+
it("fica no modal base (três faixas), com a classe do cabeçalho cheio", () => {
|
|
22
|
+
render(_jsx(Modal, { open: true, inline: true, variant: "primary", title: "Resolver tarefa" }));
|
|
23
|
+
const dialogo = screen.getByRole("dialog", { name: "Resolver tarefa" });
|
|
24
|
+
expect(dialogo).toHaveClass("umds-modal--primary", "umds-modal--base");
|
|
25
|
+
expect(dialogo).not.toHaveClass("umds-modal--feedback");
|
|
26
|
+
expect(dialogo.querySelector(".umds-modal__header")).not.toBeNull();
|
|
27
|
+
expect(dialogo.querySelector(".umds-modal__icon")).toBeNull();
|
|
28
|
+
});
|
|
29
|
+
it("positive continua sendo diálogo de feedback", () => {
|
|
30
|
+
render(_jsx(Modal, { open: true, inline: true, variant: "positive", title: "Pronto" }));
|
|
31
|
+
expect(screen.getByRole("dialog")).toHaveClass("umds-modal--feedback");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe("Modal · width e maxWidth", () => {
|
|
35
|
+
it("escreve a largura e, sem maxWidth, solta o teto do size", () => {
|
|
36
|
+
render(_jsx(Modal, { open: true, inline: true, title: "x", width: "600px" }));
|
|
37
|
+
const dialogo = screen.getByRole("dialog");
|
|
38
|
+
expect(dialogo.style.width).toBe("600px");
|
|
39
|
+
expect(dialogo.style.maxWidth).toBe("100%");
|
|
40
|
+
});
|
|
41
|
+
it("maxWidth explícito vale", () => {
|
|
42
|
+
render(_jsx(Modal, { open: true, inline: true, title: "x", width: "90%", maxWidth: "720px" }));
|
|
43
|
+
expect(screen.getByRole("dialog").style.maxWidth).toBe("720px");
|
|
44
|
+
});
|
|
45
|
+
it("sem width nem maxWidth não escreve style", () => {
|
|
46
|
+
render(_jsx(Modal, { open: true, inline: true, title: "x" }));
|
|
47
|
+
expect(screen.getByRole("dialog").getAttribute("style")).toBeNull();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe("Modal · Escape", () => {
|
|
51
|
+
it("fecha com Escape no borbulho", () => {
|
|
52
|
+
const aoFechar = vi.fn();
|
|
53
|
+
render(_jsx(Modal, { open: true, inline: true, title: "x", onClose: aoFechar }));
|
|
54
|
+
fireEvent.keyDown(document.body, { key: "Escape" });
|
|
55
|
+
expect(aoFechar).toHaveBeenCalledTimes(1);
|
|
56
|
+
});
|
|
57
|
+
it("não fecha quando outro componente já consumiu a tecla (defaultPrevented)", () => {
|
|
58
|
+
const aoFechar = vi.fn();
|
|
59
|
+
render(_jsx(Modal, { open: true, inline: true, title: "x", onClose: aoFechar }));
|
|
60
|
+
const consumidor = document.createElement("div");
|
|
61
|
+
consumidor.addEventListener("keydown", (e) => e.preventDefault());
|
|
62
|
+
document.body.appendChild(consumidor);
|
|
63
|
+
fireEvent.keyDown(consumidor, { key: "Escape" });
|
|
64
|
+
expect(aoFechar).not.toHaveBeenCalled();
|
|
65
|
+
consumidor.remove();
|
|
66
|
+
});
|
|
67
|
+
it("Select aberto dentro do modal fecha só a lista, e o modal fica", () => {
|
|
68
|
+
const aoFechar = vi.fn();
|
|
69
|
+
render(_jsx(Modal, { open: true, inline: true, title: "x", onClose: aoFechar, children: _jsx(Select, { label: "Categoria", options: [{ label: "Administrativa", value: "adm" }] }) }));
|
|
70
|
+
const gatilho = screen.getByRole("combobox");
|
|
71
|
+
fireEvent.click(gatilho);
|
|
72
|
+
expect(gatilho).toHaveAttribute("aria-expanded", "true");
|
|
73
|
+
fireEvent.keyDown(gatilho, { key: "Escape" });
|
|
74
|
+
expect(gatilho).toHaveAttribute("aria-expanded", "false");
|
|
75
|
+
expect(aoFechar).not.toHaveBeenCalled();
|
|
76
|
+
// Com o Select fechado, o Esc seguinte é do modal.
|
|
77
|
+
fireEvent.keyDown(gatilho, { key: "Escape" });
|
|
78
|
+
expect(aoFechar).toHaveBeenCalledTimes(1);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
describe("Modal · controles de janela escolhidos um a um", () => {
|
|
82
|
+
it("true liga os quatro, como antes", () => {
|
|
83
|
+
render(_jsx(Modal, { open: true, inline: true, title: "x", windowControls: true }));
|
|
84
|
+
for (const nome of ["Modo normal", "Expandir", "Tela cheia", "Minimizar"]) {
|
|
85
|
+
expect(screen.getByRole("button", { name: nome })).toBeInTheDocument();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
it("lista escolhe só os pedidos, sem minimizar", () => {
|
|
89
|
+
render(_jsx(Modal, { open: true, inline: true, title: "x", windowControls: ["expand", "fullscreen"] }));
|
|
90
|
+
expect(screen.getByRole("button", { name: "Expandir" })).toBeInTheDocument();
|
|
91
|
+
expect(screen.getByRole("button", { name: "Tela cheia" })).toBeInTheDocument();
|
|
92
|
+
expect(screen.queryByRole("button", { name: "Minimizar" })).toBeNull();
|
|
93
|
+
expect(screen.queryByRole("button", { name: "Modo normal" })).toBeNull();
|
|
94
|
+
expect(screen.getByRole("button", { name: "Fechar" })).toBeInTheDocument();
|
|
95
|
+
});
|
|
96
|
+
it("sem windowControls só há o ×", () => {
|
|
97
|
+
render(_jsx(Modal, { open: true, inline: true, title: "x" }));
|
|
98
|
+
expect(screen.queryByRole("button", { name: "Expandir" })).toBeNull();
|
|
99
|
+
expect(screen.getByRole("button", { name: "Fechar" })).toBeInTheDocument();
|
|
100
|
+
});
|
|
101
|
+
it("sem o botão normal, clicar de novo no modo ativo volta ao normal; e o normal usa o tamanho do modal", () => {
|
|
102
|
+
render(_jsx(Modal, { open: true, inline: true, title: "x", width: "600px", windowControls: ["expand"] }));
|
|
103
|
+
const dialogo = screen.getByRole("dialog");
|
|
104
|
+
const expandir = screen.getByRole("button", { name: "Expandir" });
|
|
105
|
+
expect(dialogo).not.toHaveClass("umds-modal--janela-normal");
|
|
106
|
+
expect(dialogo.style.width).toBe("600px");
|
|
107
|
+
fireEvent.click(expandir);
|
|
108
|
+
expect(dialogo).toHaveClass("umds-modal--janela-expanded");
|
|
109
|
+
expect(dialogo.style.width).toBe("");
|
|
110
|
+
fireEvent.click(expandir);
|
|
111
|
+
expect(dialogo).not.toHaveClass("umds-modal--janela-expanded");
|
|
112
|
+
expect(dialogo.style.width).toBe("600px");
|
|
113
|
+
});
|
|
114
|
+
it("minimizar chama onMinimize", () => {
|
|
115
|
+
const aoMinimizar = vi.fn();
|
|
116
|
+
render(_jsx(Modal, { open: true, inline: true, title: "x", windowControls: ["minimize"], onMinimize: aoMinimizar }));
|
|
117
|
+
fireEvent.click(screen.getByRole("button", { name: "Minimizar" }));
|
|
118
|
+
expect(aoMinimizar).toHaveBeenCalledTimes(1);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
describe("Modal · responsive", () => {
|
|
122
|
+
it("em tela larga continua centralizado", () => {
|
|
123
|
+
simularTela(false);
|
|
124
|
+
render(_jsx(Modal, { open: true, inline: true, title: "x", responsive: true }));
|
|
125
|
+
expect(screen.getByRole("dialog")).not.toHaveClass("umds-modal--sheet");
|
|
126
|
+
});
|
|
127
|
+
it("em tela estreita vira painel de baixo, sem controles de janela e sem a largura livre", () => {
|
|
128
|
+
simularTela(true);
|
|
129
|
+
render(_jsx(Modal, { open: true, inline: true, title: "x", responsive: true, width: "600px", windowControls: true }));
|
|
130
|
+
const dialogo = screen.getByRole("dialog");
|
|
131
|
+
expect(dialogo).toHaveClass("umds-modal--sheet");
|
|
132
|
+
expect(dialogo.style.width).toBe("");
|
|
133
|
+
expect(screen.queryByRole("button", { name: "Expandir" })).toBeNull();
|
|
134
|
+
expect(dialogo.querySelector(".umds-modal__grabber")).not.toBeNull();
|
|
135
|
+
});
|
|
136
|
+
it("acompanha a mudança de tamanho da tela", () => {
|
|
137
|
+
const mudar = simularTela(false);
|
|
138
|
+
render(_jsx(Modal, { open: true, inline: true, title: "x", responsive: true }));
|
|
139
|
+
const dialogo = screen.getByRole("dialog");
|
|
140
|
+
expect(dialogo).not.toHaveClass("umds-modal--sheet");
|
|
141
|
+
act(() => mudar(true));
|
|
142
|
+
expect(screen.getByRole("dialog")).toHaveClass("umds-modal--sheet");
|
|
143
|
+
act(() => mudar(false));
|
|
144
|
+
expect(screen.getByRole("dialog")).not.toHaveClass("umds-modal--sheet");
|
|
145
|
+
});
|
|
146
|
+
it("sem responsive nunca vira painel", () => {
|
|
147
|
+
simularTela(true);
|
|
148
|
+
render(_jsx(Modal, { open: true, inline: true, title: "x" }));
|
|
149
|
+
expect(screen.getByRole("dialog")).not.toHaveClass("umds-modal--sheet");
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { act, screen } from "@testing-library/react";
|
|
4
|
+
import { renderToString } from "react-dom/server";
|
|
5
|
+
import { hydrateRoot } from "react-dom/client";
|
|
6
|
+
import Select from "../components/Select/Select";
|
|
7
|
+
import MultiSelect from "../components/MultiSelect/MultiSelect";
|
|
8
|
+
import SegmentedControl from "../components/SegmentedControl/SegmentedControl";
|
|
9
|
+
/**
|
|
10
|
+
* Select, MultiSelect e SegmentedControl nascem SEM id automático (utils/id):
|
|
11
|
+
* o id só é gerado no onMount, para o servidor e a primeira renderização do
|
|
12
|
+
* cliente saírem idênticos. Só que o nome acessível deles vinha de
|
|
13
|
+
* `aria-labelledby` apontando para esse id — no HTML do servidor, e até a
|
|
14
|
+
* hidratação, o combobox ficava sem nome (axe `aria-input-field-name`, visto
|
|
15
|
+
* no portal Astro com `client:visible`).
|
|
16
|
+
*
|
|
17
|
+
* Aqui o HTML sai do `renderToString`, que não dispara o onMount, e vai para
|
|
18
|
+
* o DOM do jsdom como o navegador receberia. O nome é conferido pelo cálculo
|
|
19
|
+
* do Testing Library, o mesmo algoritmo de nome acessível que o axe usa.
|
|
20
|
+
*/
|
|
21
|
+
const OPCOES = [
|
|
22
|
+
{ label: "Recursos Humanos", value: "rh" },
|
|
23
|
+
{ label: "Financeiro", value: "fin" },
|
|
24
|
+
];
|
|
25
|
+
const POR_PAGINA = [
|
|
26
|
+
{ label: "10", value: "10" },
|
|
27
|
+
{ label: "25", value: "25" },
|
|
28
|
+
];
|
|
29
|
+
function comoDoServidor(html) {
|
|
30
|
+
const raiz = document.createElement("div");
|
|
31
|
+
raiz.innerHTML = html;
|
|
32
|
+
document.body.appendChild(raiz);
|
|
33
|
+
return raiz;
|
|
34
|
+
}
|
|
35
|
+
describe("Nome acessível no HTML do servidor", () => {
|
|
36
|
+
// As raízes são montadas à mão (fora do render), e o cleanup global não as vê.
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
document.body.innerHTML = "";
|
|
39
|
+
});
|
|
40
|
+
it("Select: o combobox tem o nome do label antes do mount", () => {
|
|
41
|
+
comoDoServidor(renderToString(_jsx(Select, { label: "Setor", options: OPCOES })));
|
|
42
|
+
const campo = screen.getByRole("combobox", { name: "Setor" });
|
|
43
|
+
expect(campo).not.toHaveAttribute("aria-labelledby");
|
|
44
|
+
expect(campo).toHaveAttribute("aria-label", "Setor");
|
|
45
|
+
});
|
|
46
|
+
it("Select: `ariaLabel` continua valendo sobre o label", () => {
|
|
47
|
+
comoDoServidor(renderToString(_jsx(Select, { label: "Setor", ariaLabel: "Setor do servidor", options: OPCOES })));
|
|
48
|
+
expect(screen.getByRole("combobox", { name: "Setor do servidor" })).toBeInTheDocument();
|
|
49
|
+
});
|
|
50
|
+
it("Select com `id`: o servidor já liga pelo aria-labelledby", () => {
|
|
51
|
+
const html = renderToString(_jsx(Select, { id: "setor", label: "Setor", options: OPCOES }));
|
|
52
|
+
expect(html).toContain('aria-labelledby="setor-label"');
|
|
53
|
+
expect(html).not.toContain('aria-label="Setor"');
|
|
54
|
+
});
|
|
55
|
+
it("MultiSelect: o combobox tem o nome do label antes do mount", () => {
|
|
56
|
+
comoDoServidor(renderToString(_jsx(MultiSelect, { label: "Setores", options: OPCOES })));
|
|
57
|
+
expect(screen.getByRole("combobox", { name: "Setores" })).toBeInTheDocument();
|
|
58
|
+
});
|
|
59
|
+
it("SegmentedControl: grupo e segmentos têm nome antes do mount", () => {
|
|
60
|
+
comoDoServidor(renderToString(_jsx(SegmentedControl, { label: "Itens", options: POR_PAGINA })));
|
|
61
|
+
expect(screen.getByRole("radiogroup", { name: "Itens" })).toBeInTheDocument();
|
|
62
|
+
expect(screen.getByRole("radio", { name: "10" })).toBeInTheDocument();
|
|
63
|
+
expect(screen.getByRole("radio", { name: "25" })).toBeInTheDocument();
|
|
64
|
+
});
|
|
65
|
+
it("hidrata sem divergência e, montado, volta ao aria-labelledby", async () => {
|
|
66
|
+
const arvore = (_jsxs("div", { children: [_jsx(Select, { label: "Setor", options: OPCOES }), _jsx(MultiSelect, { label: "Setores", options: OPCOES }), _jsx(SegmentedControl, { label: "Itens", options: POR_PAGINA })] }));
|
|
67
|
+
const raiz = comoDoServidor(renderToString(arvore));
|
|
68
|
+
const erro = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
69
|
+
const avisosDeHidratacao = [];
|
|
70
|
+
await act(async () => {
|
|
71
|
+
hydrateRoot(raiz, arvore, {
|
|
72
|
+
onRecoverableError: (e) => avisosDeHidratacao.push(e),
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
const chamadas = erro.mock.calls.map((c) => String(c[0]));
|
|
76
|
+
erro.mockRestore();
|
|
77
|
+
expect(avisosDeHidratacao).toEqual([]);
|
|
78
|
+
expect(chamadas.filter((c) => /hydrat|did not match/i.test(c))).toEqual([]);
|
|
79
|
+
const select = screen.getByRole("combobox", { name: "Setor" });
|
|
80
|
+
expect(select).toHaveAttribute("aria-labelledby");
|
|
81
|
+
expect(select).not.toHaveAttribute("aria-label");
|
|
82
|
+
const multi = screen.getByRole("combobox", { name: "Setores" });
|
|
83
|
+
expect(multi).toHaveAttribute("aria-labelledby");
|
|
84
|
+
const grupo = screen.getByRole("radiogroup", { name: "Itens" });
|
|
85
|
+
expect(grupo).toHaveAttribute("aria-labelledby");
|
|
86
|
+
expect(screen.getByRole("radio", { name: "10" })).not.toHaveAttribute("aria-label");
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,162 @@
|
|
|
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 { useState } from "react";
|
|
4
|
+
import { fireEvent, render, screen, within } from "@testing-library/react";
|
|
5
|
+
import userEvent from "@testing-library/user-event";
|
|
6
|
+
import OrgChart from "../components/OrgChart/OrgChart";
|
|
7
|
+
const ARVORE = [
|
|
8
|
+
{
|
|
9
|
+
id: "copirn",
|
|
10
|
+
title: "COPIRN",
|
|
11
|
+
subtitle: "Companhia Potiguar de Infraestrutura",
|
|
12
|
+
children: [
|
|
13
|
+
{ id: "aj", title: "AJ", subtitle: "Assessoria Jurídica" },
|
|
14
|
+
{
|
|
15
|
+
id: "sadm",
|
|
16
|
+
title: "SADM",
|
|
17
|
+
subtitle: "Secretaria de Administração",
|
|
18
|
+
children: [
|
|
19
|
+
{ id: "al", title: "AL", subtitle: "Almoxarifado" },
|
|
20
|
+
{ id: "ssa", title: "SSA", subtitle: "Subsetor do Almoxarifado" },
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
{ id: "sfin", title: "SFIN", subtitle: "Secretaria de Finanças" },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
const item = (id) => document.querySelector(`[data-node-id="${id}"]`);
|
|
28
|
+
const esquerda = (id) => parseFloat(item(id).style.left);
|
|
29
|
+
describe("OrgChart", () => {
|
|
30
|
+
it("é uma tree nomeada, com um treeitem por nó visível e níveis ARIA", () => {
|
|
31
|
+
render(_jsx(OrgChart, { nodes: ARVORE, ariaLabel: "Setores" }));
|
|
32
|
+
expect(screen.getByRole("tree", { name: "Setores" })).toBeInTheDocument();
|
|
33
|
+
expect(screen.getAllByRole("treeitem")).toHaveLength(6);
|
|
34
|
+
expect(item("copirn")).toHaveAttribute("aria-level", "1");
|
|
35
|
+
expect(item("al")).toHaveAttribute("aria-level", "3");
|
|
36
|
+
expect(item("sadm")).toHaveAttribute("aria-posinset", "2");
|
|
37
|
+
expect(item("sadm")).toHaveAttribute("aria-setsize", "3");
|
|
38
|
+
expect(item("sadm")).toHaveAttribute("aria-expanded", "true");
|
|
39
|
+
expect(item("aj")).not.toHaveAttribute("aria-expanded");
|
|
40
|
+
});
|
|
41
|
+
it("o nome padrão é Organograma; título e subtítulo aparecem", () => {
|
|
42
|
+
render(_jsx(OrgChart, { nodes: ARVORE }));
|
|
43
|
+
expect(screen.getByRole("tree", { name: "Organograma" })).toBeInTheDocument();
|
|
44
|
+
expect(within(item("aj")).getByText("AJ")).toBeInTheDocument();
|
|
45
|
+
expect(within(item("aj")).getByText("Assessoria Jurídica")).toBeInTheDocument();
|
|
46
|
+
});
|
|
47
|
+
it("o pai se centra sobre os filhos e os irmãos andam lado a lado", () => {
|
|
48
|
+
render(_jsx(OrgChart, { nodes: ARVORE, nodeWidth: 100, gapX: 20 }));
|
|
49
|
+
expect(esquerda("aj")).toBe(0);
|
|
50
|
+
expect(esquerda("al")).toBe(120);
|
|
51
|
+
expect(esquerda("ssa")).toBe(240);
|
|
52
|
+
expect(esquerda("sadm")).toBe(180);
|
|
53
|
+
expect(esquerda("sfin")).toBe(360);
|
|
54
|
+
expect(esquerda("copirn")).toBe((0 + 360) / 2);
|
|
55
|
+
expect(parseFloat(item("sadm").style.top)).toBeGreaterThan(parseFloat(item("copirn").style.top));
|
|
56
|
+
});
|
|
57
|
+
it("desenha um conector por pai expandido", () => {
|
|
58
|
+
const { container } = render(_jsx(OrgChart, { nodes: ARVORE }));
|
|
59
|
+
expect(container.querySelectorAll(".umds-org-chart__line")).toHaveLength(2);
|
|
60
|
+
});
|
|
61
|
+
it("a raiz é cheia e o nó marcado leva aria-selected", () => {
|
|
62
|
+
render(_jsx(OrgChart, { nodes: ARVORE, selectedId: "aj" }));
|
|
63
|
+
expect(item("copirn")).toHaveClass("umds-org-chart__node--root");
|
|
64
|
+
expect(item("aj")).toHaveAttribute("aria-selected", "true");
|
|
65
|
+
expect(item("sfin")).not.toHaveAttribute("aria-selected");
|
|
66
|
+
});
|
|
67
|
+
it("recolher esconde os filhos e avisa; expandir volta", async () => {
|
|
68
|
+
const usuario = userEvent.setup();
|
|
69
|
+
const aoMudar = vi.fn();
|
|
70
|
+
render(_jsx(OrgChart, { nodes: ARVORE, onCollapsedChange: aoMudar }));
|
|
71
|
+
item("sadm").focus();
|
|
72
|
+
await usuario.keyboard(" ");
|
|
73
|
+
expect(aoMudar).toHaveBeenLastCalledWith(["sadm"]);
|
|
74
|
+
expect(screen.getAllByRole("treeitem")).toHaveLength(4);
|
|
75
|
+
expect(item("sadm")).toHaveAttribute("aria-expanded", "false");
|
|
76
|
+
await usuario.keyboard(" ");
|
|
77
|
+
expect(aoMudar).toHaveBeenLastCalledWith([]);
|
|
78
|
+
expect(screen.getAllByRole("treeitem")).toHaveLength(6);
|
|
79
|
+
});
|
|
80
|
+
it("o botão de expandir do mouse faz o mesmo e não aciona o nó", async () => {
|
|
81
|
+
const usuario = userEvent.setup();
|
|
82
|
+
const aoClicar = vi.fn();
|
|
83
|
+
const { container } = render(_jsx(OrgChart, { nodes: ARVORE, onNodeClick: aoClicar }));
|
|
84
|
+
const botoes = container.querySelectorAll(".umds-org-chart__toggle");
|
|
85
|
+
expect(botoes).toHaveLength(2);
|
|
86
|
+
await usuario.click(botoes[1]);
|
|
87
|
+
expect(screen.getAllByRole("treeitem")).toHaveLength(4);
|
|
88
|
+
expect(aoClicar).not.toHaveBeenCalled();
|
|
89
|
+
});
|
|
90
|
+
it("defaultCollapsedIds começa recolhido; collapsedIds controla", () => {
|
|
91
|
+
const { rerender } = render(_jsx(OrgChart, { nodes: ARVORE, defaultCollapsedIds: ["sadm"] }));
|
|
92
|
+
expect(screen.getAllByRole("treeitem")).toHaveLength(4);
|
|
93
|
+
rerender(_jsx(OrgChart, { nodes: ARVORE, collapsedIds: ["copirn"] }));
|
|
94
|
+
expect(screen.getAllByRole("treeitem")).toHaveLength(1);
|
|
95
|
+
});
|
|
96
|
+
it("clicar e Enter acionam o nó com o nó", async () => {
|
|
97
|
+
const usuario = userEvent.setup();
|
|
98
|
+
const aoClicar = vi.fn();
|
|
99
|
+
render(_jsx(OrgChart, { nodes: ARVORE, onNodeClick: aoClicar }));
|
|
100
|
+
await usuario.click(item("aj"));
|
|
101
|
+
expect(aoClicar).toHaveBeenLastCalledWith(expect.objectContaining({ id: "aj" }));
|
|
102
|
+
item("sfin").focus();
|
|
103
|
+
await usuario.keyboard("{Enter}");
|
|
104
|
+
expect(aoClicar).toHaveBeenLastCalledWith(expect.objectContaining({ id: "sfin" }));
|
|
105
|
+
});
|
|
106
|
+
it("só um nó entra na ordem de Tab (o primeiro)", () => {
|
|
107
|
+
render(_jsx(OrgChart, { nodes: ARVORE }));
|
|
108
|
+
const comTab = screen.getAllByRole("treeitem").filter((n) => n.getAttribute("tabindex") === "0");
|
|
109
|
+
expect(comTab).toHaveLength(1);
|
|
110
|
+
expect(comTab[0]).toHaveAttribute("data-node-id", "copirn");
|
|
111
|
+
});
|
|
112
|
+
it("setas: baixo desce ao primeiro filho, cima sobe ao pai, lados andam entre irmãos, Home e End", async () => {
|
|
113
|
+
const usuario = userEvent.setup();
|
|
114
|
+
render(_jsx(OrgChart, { nodes: ARVORE }));
|
|
115
|
+
item("copirn").focus();
|
|
116
|
+
await usuario.keyboard("{ArrowDown}");
|
|
117
|
+
expect(item("aj")).toHaveFocus();
|
|
118
|
+
await usuario.keyboard("{ArrowRight}");
|
|
119
|
+
expect(item("sadm")).toHaveFocus();
|
|
120
|
+
await usuario.keyboard("{ArrowDown}");
|
|
121
|
+
expect(item("al")).toHaveFocus();
|
|
122
|
+
await usuario.keyboard("{ArrowRight}");
|
|
123
|
+
expect(item("ssa")).toHaveFocus();
|
|
124
|
+
await usuario.keyboard("{Home}");
|
|
125
|
+
expect(item("al")).toHaveFocus();
|
|
126
|
+
await usuario.keyboard("{ArrowUp}");
|
|
127
|
+
expect(item("sadm")).toHaveFocus();
|
|
128
|
+
await usuario.keyboard("{End}");
|
|
129
|
+
expect(item("sfin")).toHaveFocus();
|
|
130
|
+
await usuario.keyboard("{ArrowLeft}{ArrowLeft}");
|
|
131
|
+
expect(item("aj")).toHaveFocus();
|
|
132
|
+
});
|
|
133
|
+
it("baixo num nó recolhido abre; o foco desce no seguinte", async () => {
|
|
134
|
+
const usuario = userEvent.setup();
|
|
135
|
+
render(_jsx(OrgChart, { nodes: ARVORE, defaultCollapsedIds: ["sadm"] }));
|
|
136
|
+
item("sadm").focus();
|
|
137
|
+
await usuario.keyboard("{ArrowDown}");
|
|
138
|
+
expect(item("sadm")).toHaveAttribute("aria-expanded", "true");
|
|
139
|
+
await usuario.keyboard("{ArrowDown}");
|
|
140
|
+
expect(item("al")).toHaveFocus();
|
|
141
|
+
});
|
|
142
|
+
it("renderNode troca o conteúdo do nó", () => {
|
|
143
|
+
render(_jsx(OrgChart, { nodes: ARVORE, renderNode: (no, meta) => _jsx("b", { "data-testid": "c-" + no.id, children: no.title + ":" + meta.level }) }));
|
|
144
|
+
expect(screen.getByTestId("c-ssa")).toHaveTextContent("SSA:2");
|
|
145
|
+
expect(item("ssa").querySelector(".umds-org-chart__title")).toBeNull();
|
|
146
|
+
});
|
|
147
|
+
it("textos do botão por prop; sem nós, não desenha a árvore", () => {
|
|
148
|
+
const { container, rerender } = render(_jsx(OrgChart, { nodes: ARVORE, expandLabel: "Expand", collapseLabel: "Collapse" }));
|
|
149
|
+
expect(container.querySelector(".umds-org-chart__toggle")).toHaveAttribute("title", "Collapse");
|
|
150
|
+
rerender(_jsx(OrgChart, { nodes: [] }));
|
|
151
|
+
expect(screen.queryByRole("tree")).toBeNull();
|
|
152
|
+
});
|
|
153
|
+
it("controlado por pai", () => {
|
|
154
|
+
function Pai() {
|
|
155
|
+
const [fechados, setFechados] = useState([]);
|
|
156
|
+
return (_jsxs(_Fragment, { children: [_jsx(OrgChart, { nodes: ARVORE, collapsedIds: fechados, onCollapsedChange: setFechados }), _jsx("button", { onClick: () => setFechados(["sadm"]), children: "fechar" })] }));
|
|
157
|
+
}
|
|
158
|
+
render(_jsx(Pai, {}));
|
|
159
|
+
fireEvent.click(screen.getByText("fechar"));
|
|
160
|
+
expect(screen.getAllByRole("treeitem")).toHaveLength(4);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|