@1doc/1ds-react 2.8.0 → 2.10.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 +1540 -0
- package/dist/components/AILabel/AILabel.js +1 -1
- package/dist/components/Accordion/Accordion.css +13 -0
- package/dist/components/Accordion/Accordion.d.ts +9 -2
- package/dist/components/Accordion/Accordion.js +1 -0
- package/dist/components/AppSubheader/AppSubheader.css +10 -1
- package/dist/components/AttachmentCard/AttachmentCard.css +26 -8
- package/dist/components/AttachmentCard/AttachmentCard.d.ts +22 -2
- package/dist/components/AttachmentCard/AttachmentCard.js +47 -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 +67 -2
- package/dist/components/Badge/Badge.d.ts +23 -4
- package/dist/components/Badge/Badge.js +4 -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 +126 -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 +128 -0
- package/dist/components/Coachmark/Coachmark.d.ts +87 -0
- package/dist/components/Coachmark/Coachmark.js +362 -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 +140 -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 +254 -0
- package/dist/components/DataTable/DataTable.d.ts +81 -5
- package/dist/components/DataTable/DataTable.js +335 -19
- package/dist/components/DatePicker/DatePicker.css +8 -0
- package/dist/components/DatePicker/DatePicker.js +10 -1
- package/dist/components/DescriptionList/DescriptionList.css +127 -0
- package/dist/components/DescriptionList/DescriptionList.d.ts +62 -0
- package/dist/components/DescriptionList/DescriptionList.js +52 -0
- package/dist/components/EmptyState/EmptyState.d.ts +4 -2
- package/dist/components/EmptyState/EmptyState.js +2 -2
- 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 +59 -0
- package/dist/components/Input/Input.d.ts +17 -1
- package/dist/components/Input/Input.js +67 -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 +97 -0
- package/dist/components/List/ListItem.js +82 -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 +250 -0
- package/dist/components/RichTextEditor/RichTextEditor.d.ts +97 -0
- package/dist/components/RichTextEditor/RichTextEditor.js +951 -0
- package/dist/components/SegmentedControl/SegmentedControl.css +24 -0
- package/dist/components/SegmentedControl/SegmentedControl.d.ts +2 -0
- package/dist/components/SegmentedControl/SegmentedControl.js +10 -2
- package/dist/components/Select/Select.css +164 -0
- package/dist/components/Select/Select.d.ts +34 -0
- package/dist/components/Select/Select.js +258 -28
- 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 +138 -0
- package/dist/components/StatCard/StatCard.d.ts +26 -0
- package/dist/components/StatCard/StatCard.js +13 -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/Toast/Toast.css +32 -0
- package/dist/components/Toast/Toast.d.ts +18 -0
- package/dist/components/Toast/Toast.js +14 -1
- package/dist/components/Toast/ToastStack.js +8 -1
- 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/AccordionTituloNeutro.test.d.ts +1 -0
- package/dist/teste/AccordionTituloNeutro.test.js +23 -0
- package/dist/teste/AppSubheaderRegiao.test.d.ts +1 -0
- package/dist/teste/AppSubheaderRegiao.test.js +13 -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/AttachmentCardLista.test.d.ts +1 -0
- package/dist/teste/AttachmentCardLista.test.js +49 -0
- package/dist/teste/AttachmentCardSm.test.d.ts +1 -0
- package/dist/teste/AttachmentCardSm.test.js +26 -0
- package/dist/teste/AvatarGroupOverlap.test.d.ts +1 -0
- package/dist/teste/AvatarGroupOverlap.test.js +56 -0
- package/dist/teste/BadgeNeutroColado.test.d.ts +1 -0
- package/dist/teste/BadgeNeutroColado.test.js +11 -0
- package/dist/teste/BadgeTag.test.d.ts +1 -0
- package/dist/teste/BadgeTag.test.js +55 -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/CalendarCabecalho.test.d.ts +1 -0
- package/dist/teste/CalendarCabecalho.test.js +11 -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/CoachmarkSemVeu.test.d.ts +1 -0
- package/dist/teste/CoachmarkSemVeu.test.js +85 -0
- package/dist/teste/ColorPicker.test.d.ts +1 -0
- package/dist/teste/ColorPicker.test.js +132 -0
- package/dist/teste/ColorPickerXs.test.d.ts +1 -0
- package/dist/teste/ColorPickerXs.test.js +21 -0
- package/dist/teste/DataTableArvoreEVariantes.test.d.ts +1 -0
- package/dist/teste/DataTableArvoreEVariantes.test.js +190 -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/DescriptionListCompacta.test.d.ts +1 -0
- package/dist/teste/DescriptionListCompacta.test.js +22 -0
- package/dist/teste/EmptyStateTituloRico.test.d.ts +1 -0
- package/dist/teste/EmptyStateTituloRico.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/InputAgrupado.test.d.ts +1 -0
- package/dist/teste/InputAgrupado.test.js +24 -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/ListItemTooltipStandalone.test.d.ts +1 -0
- package/dist/teste/ListItemTooltipStandalone.test.js +39 -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/RichTextEditorAlinhaImagemTabela.test.d.ts +1 -0
- package/dist/teste/RichTextEditorAlinhaImagemTabela.test.js +212 -0
- package/dist/teste/RichTextEditorEstiloDeTexto.test.d.ts +1 -0
- package/dist/teste/RichTextEditorEstiloDeTexto.test.js +120 -0
- package/dist/teste/SegmentedControlContador.test.d.ts +1 -0
- package/dist/teste/SegmentedControlContador.test.js +39 -0
- package/dist/teste/SelectAbertoControladoEFlutuante.test.d.ts +1 -0
- package/dist/teste/SelectAbertoControladoEFlutuante.test.js +145 -0
- package/dist/teste/SelectFiltroCorETamanho.test.d.ts +1 -0
- package/dist/teste/SelectFiltroCorETamanho.test.js +77 -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/StatCardSecundariaETom.test.d.ts +1 -0
- package/dist/teste/StatCardSecundariaETom.test.js +45 -0
- package/dist/teste/TabsContadorFechavel.test.d.ts +1 -0
- package/dist/teste/TabsContadorFechavel.test.js +79 -0
- package/dist/teste/ToastAcaoEDetalhes.test.d.ts +1 -0
- package/dist/teste/ToastAcaoEDetalhes.test.js +94 -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 +45 -0
- package/dist/utils/html-seguro.js +208 -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/dist/utils/toast.d.ts +10 -1
- package/dist/utils/toast.js +2 -1
- 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/AccordionTituloNeutro.test.tsx +25 -0
- package/src/teste/AppSubheaderRegiao.test.tsx +17 -0
- package/src/teste/AtributosRepassados.test.tsx +73 -0
- package/src/teste/AttachmentCardIA.test.tsx +126 -0
- package/src/teste/AttachmentCardLista.test.tsx +53 -0
- package/src/teste/AttachmentCardSm.test.tsx +29 -0
- package/src/teste/AvatarGroupOverlap.test.tsx +74 -0
- package/src/teste/BadgeNeutroColado.test.tsx +15 -0
- package/src/teste/BadgeTag.test.tsx +85 -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/CalendarCabecalho.test.tsx +11 -0
- package/src/teste/ChipsXs.test.tsx +41 -0
- package/src/teste/Coachmark.test.tsx +198 -0
- package/src/teste/CoachmarkSemVeu.test.tsx +101 -0
- package/src/teste/ColorPicker.test.tsx +153 -0
- package/src/teste/ColorPickerXs.test.tsx +22 -0
- package/src/teste/DataTableArvoreEVariantes.test.tsx +240 -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/DescriptionListCompacta.test.tsx +24 -0
- package/src/teste/EmptyStateTituloRico.test.tsx +34 -0
- package/src/teste/FileInputTrigger.test.tsx +89 -0
- package/src/teste/IconPicker.test.tsx +145 -0
- package/src/teste/InputAgrupado.test.tsx +26 -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/ListItemTooltipStandalone.test.tsx +64 -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/RichTextEditorAlinhaImagemTabela.test.tsx +238 -0
- package/src/teste/RichTextEditorEstiloDeTexto.test.tsx +137 -0
- package/src/teste/SegmentedControlContador.test.tsx +43 -0
- package/src/teste/SelectAbertoControladoEFlutuante.test.tsx +180 -0
- package/src/teste/SelectFiltroCorETamanho.test.tsx +90 -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/StatCardSecundariaETom.test.tsx +51 -0
- package/src/teste/TabsContadorFechavel.test.tsx +144 -0
- package/src/teste/ToastAcaoEDetalhes.test.tsx +109 -0
- package/src/teste/TooltipDark.test.tsx +51 -0
- package/src/utils/calendario.ts +78 -0
- package/src/utils/html-seguro.ts +197 -0
- package/src/utils/paleta-de-cores.ts +34 -0
- package/src/utils/texto.ts +5 -0
- package/src/utils/toast.ts +12 -2
|
@@ -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 {};
|
|
@@ -0,0 +1,143 @@
|
|
|
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 { render, screen } from "@testing-library/react";
|
|
5
|
+
import userEvent from "@testing-library/user-event";
|
|
6
|
+
import OtpInput from "../components/OtpInput/OtpInput";
|
|
7
|
+
const caixas = () => screen.getAllByRole("textbox");
|
|
8
|
+
describe("OtpInput", () => {
|
|
9
|
+
it("tem 6 caixas por padrão, cada uma com nome próprio, num grupo nomeado", () => {
|
|
10
|
+
render(_jsx(OtpInput, { ariaLabel: "C\u00F3digo de verifica\u00E7\u00E3o" }));
|
|
11
|
+
expect(screen.getByRole("group", { name: "Código de verificação" })).toBeInTheDocument();
|
|
12
|
+
expect(caixas()).toHaveLength(6);
|
|
13
|
+
expect(screen.getByRole("textbox", { name: "Dígito 3 de 6" })).toBeInTheDocument();
|
|
14
|
+
});
|
|
15
|
+
it("length muda a quantidade e digitLabel traduz o nome", () => {
|
|
16
|
+
render(_jsx(OtpInput, { ariaLabel: "Code", length: 4, digitLabel: "Digit {n} of {total}" }));
|
|
17
|
+
expect(caixas()).toHaveLength(4);
|
|
18
|
+
expect(screen.getByRole("textbox", { name: "Digit 2 of 4" })).toBeInTheDocument();
|
|
19
|
+
});
|
|
20
|
+
it("a primeira caixa leva autocomplete one-time-code e teclado numérico", () => {
|
|
21
|
+
render(_jsx(OtpInput, { ariaLabel: "C\u00F3digo" }));
|
|
22
|
+
expect(caixas()[0]).toHaveAttribute("autocomplete", "one-time-code");
|
|
23
|
+
expect(caixas()[1]).toHaveAttribute("autocomplete", "off");
|
|
24
|
+
expect(caixas()[0]).toHaveAttribute("inputmode", "numeric");
|
|
25
|
+
});
|
|
26
|
+
it("digitar avança o foco e entrega o código em onChange", async () => {
|
|
27
|
+
const usuario = userEvent.setup();
|
|
28
|
+
const aoMudar = vi.fn();
|
|
29
|
+
render(_jsx(OtpInput, { ariaLabel: "C\u00F3digo", onChange: aoMudar }));
|
|
30
|
+
caixas()[0].focus();
|
|
31
|
+
await usuario.keyboard("482");
|
|
32
|
+
expect(aoMudar).toHaveBeenLastCalledWith("482");
|
|
33
|
+
expect(caixas().map((c) => c.value).join("")).toBe("482");
|
|
34
|
+
expect(caixas()[3]).toHaveFocus();
|
|
35
|
+
});
|
|
36
|
+
it("rejeita o que não é dígito (numeric)", async () => {
|
|
37
|
+
const usuario = userEvent.setup();
|
|
38
|
+
const aoMudar = vi.fn();
|
|
39
|
+
render(_jsx(OtpInput, { ariaLabel: "C\u00F3digo", onChange: aoMudar }));
|
|
40
|
+
caixas()[0].focus();
|
|
41
|
+
await usuario.keyboard("a-b");
|
|
42
|
+
expect(aoMudar).not.toHaveBeenCalled();
|
|
43
|
+
expect(caixas()[0].value).toBe("");
|
|
44
|
+
expect(caixas()[0]).toHaveFocus();
|
|
45
|
+
});
|
|
46
|
+
it("alphanumeric aceita letras e sai em maiúsculas", async () => {
|
|
47
|
+
const usuario = userEvent.setup();
|
|
48
|
+
const aoMudar = vi.fn();
|
|
49
|
+
render(_jsx(OtpInput, { ariaLabel: "C\u00F3digo", type: "alphanumeric", length: 4, onChange: aoMudar }));
|
|
50
|
+
caixas()[0].focus();
|
|
51
|
+
await usuario.keyboard("a9z");
|
|
52
|
+
expect(aoMudar).toHaveBeenLastCalledWith("A9Z");
|
|
53
|
+
expect(caixas()[0]).toHaveAttribute("inputmode", "text");
|
|
54
|
+
});
|
|
55
|
+
it("colar o código inteiro em qualquer caixa distribui e chama onComplete uma vez", async () => {
|
|
56
|
+
const usuario = userEvent.setup();
|
|
57
|
+
const aoCompletar = vi.fn();
|
|
58
|
+
render(_jsx(OtpInput, { ariaLabel: "C\u00F3digo", onComplete: aoCompletar }));
|
|
59
|
+
caixas()[2].focus();
|
|
60
|
+
await usuario.paste("482 913");
|
|
61
|
+
expect(caixas().map((c) => c.value).join("")).toBe("482913");
|
|
62
|
+
expect(aoCompletar).toHaveBeenCalledTimes(1);
|
|
63
|
+
expect(aoCompletar).toHaveBeenCalledWith("482913");
|
|
64
|
+
expect(caixas()[5]).toHaveFocus();
|
|
65
|
+
});
|
|
66
|
+
it("colar um pedaço entra a partir da caixa atual", async () => {
|
|
67
|
+
const usuario = userEvent.setup();
|
|
68
|
+
render(_jsx(OtpInput, { ariaLabel: "C\u00F3digo", defaultValue: "12" }));
|
|
69
|
+
caixas()[2].focus();
|
|
70
|
+
await usuario.paste("345");
|
|
71
|
+
expect(caixas().map((c) => c.value).join("")).toBe("12345");
|
|
72
|
+
});
|
|
73
|
+
it("onComplete só dispara quando as caixas enchem", async () => {
|
|
74
|
+
const usuario = userEvent.setup();
|
|
75
|
+
const aoCompletar = vi.fn();
|
|
76
|
+
render(_jsx(OtpInput, { ariaLabel: "C\u00F3digo", length: 3, onComplete: aoCompletar }));
|
|
77
|
+
caixas()[0].focus();
|
|
78
|
+
await usuario.keyboard("12");
|
|
79
|
+
expect(aoCompletar).not.toHaveBeenCalled();
|
|
80
|
+
await usuario.keyboard("3");
|
|
81
|
+
expect(aoCompletar).toHaveBeenCalledWith("123");
|
|
82
|
+
});
|
|
83
|
+
it("Backspace apaga o caractere e, em caixa vazia, volta e apaga o anterior", async () => {
|
|
84
|
+
const usuario = userEvent.setup();
|
|
85
|
+
const aoMudar = vi.fn();
|
|
86
|
+
render(_jsx(OtpInput, { ariaLabel: "C\u00F3digo", defaultValue: "123", onChange: aoMudar }));
|
|
87
|
+
caixas()[3].focus();
|
|
88
|
+
await usuario.keyboard("{Backspace}");
|
|
89
|
+
expect(aoMudar).toHaveBeenLastCalledWith("12");
|
|
90
|
+
expect(caixas()[2]).toHaveFocus();
|
|
91
|
+
await usuario.keyboard("{Backspace}");
|
|
92
|
+
expect(aoMudar).toHaveBeenLastCalledWith("1");
|
|
93
|
+
});
|
|
94
|
+
it("setas, Home e End movem o foco", async () => {
|
|
95
|
+
const usuario = userEvent.setup();
|
|
96
|
+
render(_jsx(OtpInput, { ariaLabel: "C\u00F3digo", defaultValue: "1234" }));
|
|
97
|
+
caixas()[4].focus();
|
|
98
|
+
await usuario.keyboard("{ArrowLeft}");
|
|
99
|
+
expect(caixas()[3]).toHaveFocus();
|
|
100
|
+
await usuario.keyboard("{Home}");
|
|
101
|
+
expect(caixas()[0]).toHaveFocus();
|
|
102
|
+
await usuario.keyboard("{ArrowRight}");
|
|
103
|
+
expect(caixas()[1]).toHaveFocus();
|
|
104
|
+
await usuario.keyboard("{End}");
|
|
105
|
+
expect(caixas()[4]).toHaveFocus();
|
|
106
|
+
});
|
|
107
|
+
it("focar uma caixa à frente da primeira vazia volta para ela (sem buraco)", async () => {
|
|
108
|
+
const usuario = userEvent.setup();
|
|
109
|
+
render(_jsx(OtpInput, { ariaLabel: "C\u00F3digo", defaultValue: "12" }));
|
|
110
|
+
await usuario.click(caixas()[5]);
|
|
111
|
+
expect(caixas()[2]).toHaveFocus();
|
|
112
|
+
});
|
|
113
|
+
it("controlado: reflete o value e escreve no pai", async () => {
|
|
114
|
+
const usuario = userEvent.setup();
|
|
115
|
+
function Pai() {
|
|
116
|
+
const [codigo, setCodigo] = useState("");
|
|
117
|
+
return (_jsxs(_Fragment, { children: [_jsx(OtpInput, { ariaLabel: "C\u00F3digo", value: codigo, onChange: setCodigo }), _jsx("output", { "data-testid": "codigo", children: codigo })] }));
|
|
118
|
+
}
|
|
119
|
+
render(_jsx(Pai, {}));
|
|
120
|
+
caixas()[0].focus();
|
|
121
|
+
await usuario.keyboard("90");
|
|
122
|
+
expect(screen.getByTestId("codigo")).toHaveTextContent("90");
|
|
123
|
+
expect(caixas()[1].value).toBe("0");
|
|
124
|
+
});
|
|
125
|
+
it("errorMessage liga o estado de erro e descreve as caixas", () => {
|
|
126
|
+
render(_jsx(OtpInput, { ariaLabel: "C\u00F3digo", errorMessage: "C\u00F3digo inv\u00E1lido", id: "otp" }));
|
|
127
|
+
expect(screen.getByText("Código inválido")).toBeInTheDocument();
|
|
128
|
+
for (const c of caixas()) {
|
|
129
|
+
expect(c).toHaveAttribute("aria-invalid", "true");
|
|
130
|
+
expect(c).toHaveAttribute("aria-describedby", "otp-error");
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
it("disabled desabilita todas as caixas; label visível nomeia o grupo", () => {
|
|
134
|
+
render(_jsx(OtpInput, { label: "C\u00F3digo do app", disabled: true, id: "o" }));
|
|
135
|
+
for (const c of caixas())
|
|
136
|
+
expect(c).toBeDisabled();
|
|
137
|
+
expect(screen.getByRole("group", { name: "Código do app" })).toBeInTheDocument();
|
|
138
|
+
});
|
|
139
|
+
it("autoFocus foca a primeira caixa vazia", () => {
|
|
140
|
+
render(_jsx(OtpInput, { ariaLabel: "C\u00F3digo", autoFocus: true, defaultValue: "12" }));
|
|
141
|
+
expect(caixas()[2]).toHaveFocus();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { beforeAll, describe, expect, it } from "vitest";
|
|
3
|
+
import { render, screen } from "@testing-library/react";
|
|
4
|
+
import userEvent from "@testing-library/user-event";
|
|
5
|
+
import Popover from "../components/Popover/Popover";
|
|
6
|
+
import Button from "../components/Button/Button";
|
|
7
|
+
beforeAll(() => {
|
|
8
|
+
if (!globalThis.ResizeObserver) {
|
|
9
|
+
globalThis.ResizeObserver = class {
|
|
10
|
+
observe() { }
|
|
11
|
+
unobserve() { }
|
|
12
|
+
disconnect() { }
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
async function abrirCom(maxHeight) {
|
|
17
|
+
const usuario = userEvent.setup();
|
|
18
|
+
render(_jsx(Popover, { title: "Colunas", maxHeight: maxHeight, content: _jsx("p", { children: "Corpo do painel" }), children: _jsx(Button, { variant: "secondary", children: "Abrir" }) }));
|
|
19
|
+
await usuario.click(screen.getByRole("button", { name: "Abrir" }));
|
|
20
|
+
const corpo = document.querySelector(".umds-popover__body");
|
|
21
|
+
expect(corpo).not.toBeNull();
|
|
22
|
+
return corpo;
|
|
23
|
+
}
|
|
24
|
+
describe("Popover · maxHeight", () => {
|
|
25
|
+
it("sem a prop não escreve style no corpo (o teto de 300px do CSS segue valendo)", async () => {
|
|
26
|
+
const corpo = await abrirCom();
|
|
27
|
+
expect(corpo.getAttribute("style")).toBeNull();
|
|
28
|
+
});
|
|
29
|
+
it("número vira px", async () => {
|
|
30
|
+
expect((await abrirCom(180)).style.maxHeight).toBe("180px");
|
|
31
|
+
});
|
|
32
|
+
it("texto vai como CSS", async () => {
|
|
33
|
+
expect((await abrirCom("60vh")).style.maxHeight).toBe("60vh");
|
|
34
|
+
});
|
|
35
|
+
it("none deixa o corpo crescer", async () => {
|
|
36
|
+
expect((await abrirCom("none")).style.maxHeight).toBe("none");
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|