@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,53 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
4
|
+
import FileInput from "../components/FileInput/FileInput";
|
|
5
|
+
const arquivo = (nome) => new File(["conteudo"], nome, { type: "application/pdf" });
|
|
6
|
+
describe("FileInput · variant trigger", () => {
|
|
7
|
+
it("renderiza o conteúdo como gatilho, sem a caixa de arrastar", () => {
|
|
8
|
+
const { container } = render(_jsx(FileInput, { variant: "trigger", children: _jsx("button", { type: "button", children: "Anexar arquivo" }) }));
|
|
9
|
+
expect(screen.getByRole("button", { name: "Anexar arquivo" })).toBeInTheDocument();
|
|
10
|
+
expect(container.querySelector(".umds-file-input__drop-zone")).toBeNull();
|
|
11
|
+
expect(container.querySelector(".umds-file-input--trigger")).not.toBeNull();
|
|
12
|
+
});
|
|
13
|
+
it("clicar no conteúdo abre o seletor (clique no input nativo)", () => {
|
|
14
|
+
const { container } = render(_jsx(FileInput, { variant: "trigger", children: _jsx("button", { type: "button", children: "Anexar arquivo" }) }));
|
|
15
|
+
const nativo = container.querySelector('input[type="file"]');
|
|
16
|
+
const clique = vi.spyOn(nativo, "click");
|
|
17
|
+
fireEvent.click(screen.getByRole("button", { name: "Anexar arquivo" }));
|
|
18
|
+
expect(clique).toHaveBeenCalledTimes(1);
|
|
19
|
+
});
|
|
20
|
+
it("disabled não abre o seletor", () => {
|
|
21
|
+
const { container } = render(_jsx(FileInput, { variant: "trigger", disabled: true, children: _jsx("span", { children: "Alterar foto" }) }));
|
|
22
|
+
const nativo = container.querySelector('input[type="file"]');
|
|
23
|
+
const clique = vi.spyOn(nativo, "click");
|
|
24
|
+
fireEvent.click(screen.getByText("Alterar foto"));
|
|
25
|
+
expect(clique).not.toHaveBeenCalled();
|
|
26
|
+
});
|
|
27
|
+
it("escolher arquivos chama onChange, e accept e multiple chegam ao input", () => {
|
|
28
|
+
const aoMudar = vi.fn();
|
|
29
|
+
const { container } = render(_jsx(FileInput, { variant: "trigger", accept: ".pdf", multiple: true, onChange: aoMudar, children: _jsx("button", { type: "button", children: "Anexar" }) }));
|
|
30
|
+
const nativo = container.querySelector('input[type="file"]');
|
|
31
|
+
expect(nativo).toHaveAttribute("accept", ".pdf");
|
|
32
|
+
expect(nativo).toHaveAttribute("multiple");
|
|
33
|
+
fireEvent.change(nativo, { target: { files: [arquivo("a.pdf"), arquivo("b.pdf")] } });
|
|
34
|
+
expect(aoMudar).toHaveBeenCalledTimes(1);
|
|
35
|
+
expect(aoMudar.mock.calls[0][0].map((f) => f.name)).toEqual(["a.pdf", "b.pdf"]);
|
|
36
|
+
});
|
|
37
|
+
it("o input nativo não entra no Tab e tem nome acessível", () => {
|
|
38
|
+
const { container } = render(_jsx(FileInput, { variant: "trigger", label: "Anexar arquivo", children: _jsx("button", { type: "button", children: "Anexar" }) }));
|
|
39
|
+
const nativo = container.querySelector('input[type="file"]');
|
|
40
|
+
expect(nativo).toHaveAttribute("tabindex", "-1");
|
|
41
|
+
expect(nativo).toHaveAttribute("aria-label", "Anexar arquivo");
|
|
42
|
+
// o rótulo visível não aparece: o gatilho é o conteúdo
|
|
43
|
+
expect(container.querySelector(".umds-file-input__label")).toBeNull();
|
|
44
|
+
});
|
|
45
|
+
it("a lista de arquivos continua aparecendo no modo trigger", () => {
|
|
46
|
+
render(_jsx(FileInput, { variant: "trigger", files: [{ name: "oficio.pdf", size: 2048 }], children: _jsx("button", { type: "button", children: "Anexar" }) }));
|
|
47
|
+
expect(screen.getByText("oficio.pdf")).toBeInTheDocument();
|
|
48
|
+
});
|
|
49
|
+
it("as variantes base e card seguem com a caixa", () => {
|
|
50
|
+
const { container } = render(_jsxs(_Fragment, { children: [_jsx(FileInput, { variant: "base" }), _jsx(FileInput, { variant: "card" })] }));
|
|
51
|
+
expect(container.querySelectorAll(".umds-file-input__drop-zone")).toHaveLength(2);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { render, screen } from "@testing-library/react";
|
|
5
|
+
import userEvent from "@testing-library/user-event";
|
|
6
|
+
import { faBuilding, faFileLines, faFolder, faGavel, faUser } from "@fortawesome/free-solid-svg-icons";
|
|
7
|
+
import IconPicker from "../components/IconPicker/IconPicker";
|
|
8
|
+
const ICONES = [
|
|
9
|
+
faUser,
|
|
10
|
+
faFolder,
|
|
11
|
+
faFileLines,
|
|
12
|
+
{ icon: faBuilding, label: "Prédio", keywords: ["prefeitura", "órgão"] },
|
|
13
|
+
{ icon: faGavel, label: "Martelo", keywords: ["jurídico", "decisão"] },
|
|
14
|
+
];
|
|
15
|
+
const radios = () => screen.getAllByRole("radio");
|
|
16
|
+
describe("IconPicker", () => {
|
|
17
|
+
it("é um radiogroup com um radio por ícone, nomeado pelo iconName sem hífen", () => {
|
|
18
|
+
render(_jsx(IconPicker, { ariaLabel: "\u00CDcone da categoria", icons: ICONES }));
|
|
19
|
+
expect(screen.getByRole("radiogroup", { name: "Ícone da categoria" })).toBeInTheDocument();
|
|
20
|
+
expect(radios()).toHaveLength(5);
|
|
21
|
+
expect(screen.getByRole("radio", { name: "user" })).toBeInTheDocument();
|
|
22
|
+
expect(screen.getByRole("radio", { name: "file lines" })).toBeInTheDocument();
|
|
23
|
+
expect(screen.getByRole("radio", { name: "Prédio" })).toBeInTheDocument();
|
|
24
|
+
});
|
|
25
|
+
it("iconLabel traduz os nomes que não vêm com label", () => {
|
|
26
|
+
render(_jsx(IconPicker, { ariaLabel: "x", icons: [faUser, faFolder], iconLabel: (n) => ({ user: "Pessoa", folder: "Pasta" }[n]) }));
|
|
27
|
+
expect(screen.getByRole("radio", { name: "Pessoa" })).toBeInTheDocument();
|
|
28
|
+
expect(screen.getByRole("radio", { name: "Pasta" })).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
it("clicar escolhe e entrega o NOME do ícone e o item em onChange", async () => {
|
|
31
|
+
const usuario = userEvent.setup();
|
|
32
|
+
const aoMudar = vi.fn();
|
|
33
|
+
render(_jsx(IconPicker, { ariaLabel: "x", icons: ICONES, onChange: aoMudar }));
|
|
34
|
+
await usuario.click(screen.getByRole("radio", { name: "folder" }));
|
|
35
|
+
expect(aoMudar).toHaveBeenCalledWith("folder", faFolder);
|
|
36
|
+
expect(screen.getByRole("radio", { name: "folder" })).toHaveAttribute("aria-checked", "true");
|
|
37
|
+
await usuario.click(screen.getByRole("radio", { name: "Prédio" }));
|
|
38
|
+
expect(aoMudar).toHaveBeenLastCalledWith("building", ICONES[3]);
|
|
39
|
+
});
|
|
40
|
+
it("defaultValue marca o escolhido; só ele entra na ordem de Tab", () => {
|
|
41
|
+
render(_jsx(IconPicker, { ariaLabel: "x", icons: ICONES, defaultValue: "file-lines" }));
|
|
42
|
+
expect(screen.getByRole("radio", { name: "file lines" })).toHaveAttribute("aria-checked", "true");
|
|
43
|
+
const comTab = radios().filter((r) => r.getAttribute("tabindex") === "0");
|
|
44
|
+
expect(comTab).toHaveLength(1);
|
|
45
|
+
expect(comTab[0]).toHaveAccessibleName("file lines");
|
|
46
|
+
});
|
|
47
|
+
it("sem valor, Tab entra no primeiro", () => {
|
|
48
|
+
render(_jsx(IconPicker, { ariaLabel: "x", icons: ICONES }));
|
|
49
|
+
expect(radios()[0]).toHaveAttribute("tabindex", "0");
|
|
50
|
+
});
|
|
51
|
+
it("a busca filtra por nome, rótulo e palavras-chave, sem acento nem caixa", async () => {
|
|
52
|
+
const usuario = userEvent.setup();
|
|
53
|
+
render(_jsx(IconPicker, { ariaLabel: "x", icons: ICONES }));
|
|
54
|
+
const campo = screen.getByRole("searchbox", { name: "Buscar ícone" });
|
|
55
|
+
await usuario.type(campo, "PREFEITURA");
|
|
56
|
+
expect(radios()).toHaveLength(1);
|
|
57
|
+
expect(radios()[0]).toHaveAccessibleName("Prédio");
|
|
58
|
+
await usuario.clear(campo);
|
|
59
|
+
await usuario.type(campo, "juridico");
|
|
60
|
+
expect(radios().map((r) => r.getAttribute("aria-label"))).toEqual(["Martelo"]);
|
|
61
|
+
await usuario.clear(campo);
|
|
62
|
+
await usuario.type(campo, "fol");
|
|
63
|
+
expect(radios().map((r) => r.getAttribute("aria-label"))).toEqual(["folder"]);
|
|
64
|
+
});
|
|
65
|
+
it("a quantidade de resultados é anunciada e resultsLabel traduz", async () => {
|
|
66
|
+
const usuario = userEvent.setup();
|
|
67
|
+
render(_jsx(IconPicker, { ariaLabel: "x", icons: ICONES, resultsLabel: "{count} icons" }));
|
|
68
|
+
expect(screen.getByRole("status")).toHaveTextContent("5 icons");
|
|
69
|
+
await usuario.type(screen.getByRole("searchbox"), "user");
|
|
70
|
+
expect(screen.getByRole("status")).toHaveTextContent("1 icons");
|
|
71
|
+
});
|
|
72
|
+
it("sem resultado mostra o texto e some a grade", async () => {
|
|
73
|
+
const usuario = userEvent.setup();
|
|
74
|
+
render(_jsx(IconPicker, { ariaLabel: "x", icons: ICONES, emptyText: "Nada por aqui" }));
|
|
75
|
+
await usuario.type(screen.getByRole("searchbox"), "zzz");
|
|
76
|
+
expect(screen.getByText("Nada por aqui")).toBeInTheDocument();
|
|
77
|
+
expect(screen.queryByRole("radiogroup")).toBeNull();
|
|
78
|
+
});
|
|
79
|
+
it("setas movem a escolha e o foco; cima e baixo andam uma linha (columns)", async () => {
|
|
80
|
+
const usuario = userEvent.setup();
|
|
81
|
+
const aoMudar = vi.fn();
|
|
82
|
+
render(_jsx(IconPicker, { ariaLabel: "x", icons: ICONES, columns: 2, defaultValue: "user", onChange: aoMudar }));
|
|
83
|
+
radios()[0].focus();
|
|
84
|
+
await usuario.keyboard("{ArrowRight}");
|
|
85
|
+
expect(aoMudar).toHaveBeenLastCalledWith("folder", faFolder);
|
|
86
|
+
expect(radios()[1]).toHaveFocus();
|
|
87
|
+
await usuario.keyboard("{ArrowDown}");
|
|
88
|
+
expect(radios()[3]).toHaveFocus();
|
|
89
|
+
await usuario.keyboard("{ArrowUp}");
|
|
90
|
+
expect(radios()[1]).toHaveFocus();
|
|
91
|
+
await usuario.keyboard("{End}");
|
|
92
|
+
expect(radios()[4]).toHaveFocus();
|
|
93
|
+
await usuario.keyboard("{ArrowRight}");
|
|
94
|
+
expect(radios()[0]).toHaveFocus();
|
|
95
|
+
await usuario.keyboard("{ArrowLeft}");
|
|
96
|
+
expect(radios()[4]).toHaveFocus();
|
|
97
|
+
await usuario.keyboard("{Home}");
|
|
98
|
+
expect(radios()[0]).toHaveFocus();
|
|
99
|
+
});
|
|
100
|
+
it("seta para baixo no campo de busca leva à grade", async () => {
|
|
101
|
+
const usuario = userEvent.setup();
|
|
102
|
+
render(_jsx(IconPicker, { ariaLabel: "x", icons: ICONES, defaultValue: "folder" }));
|
|
103
|
+
await usuario.click(screen.getByRole("searchbox"));
|
|
104
|
+
await usuario.keyboard("{ArrowDown}");
|
|
105
|
+
expect(screen.getByRole("radio", { name: "folder" })).toHaveFocus();
|
|
106
|
+
});
|
|
107
|
+
it("hideSearch tira o campo; disabled desabilita tudo", () => {
|
|
108
|
+
const { rerender } = render(_jsx(IconPicker, { ariaLabel: "x", icons: ICONES, hideSearch: true }));
|
|
109
|
+
expect(screen.queryByRole("searchbox")).toBeNull();
|
|
110
|
+
rerender(_jsx(IconPicker, { ariaLabel: "x", icons: ICONES, disabled: true }));
|
|
111
|
+
for (const r of radios())
|
|
112
|
+
expect(r).toBeDisabled();
|
|
113
|
+
});
|
|
114
|
+
it("textos da busca por prop; label visível nomeia o grupo; columns vira o grid", () => {
|
|
115
|
+
const { container } = render(_jsx(IconPicker, { label: "\u00CDcone", id: "ip", icons: ICONES, searchLabel: "Search icon", searchPlaceholder: "Search...", columns: 4, maxHeight: 120 }));
|
|
116
|
+
expect(screen.getByRole("radiogroup", { name: "Ícone" })).toBeInTheDocument();
|
|
117
|
+
expect(screen.getByRole("searchbox", { name: "Search icon" })).toHaveAttribute("placeholder", "Search...");
|
|
118
|
+
const grade = container.querySelector(".umds-icon-picker__grid");
|
|
119
|
+
expect(grade.style.gridTemplateColumns).toBe("repeat(4, var(--umds-icon-picker-tile))");
|
|
120
|
+
expect(grade.style.maxHeight).toBe("120px");
|
|
121
|
+
});
|
|
122
|
+
it("controlado: reflete o value", () => {
|
|
123
|
+
function Pai() {
|
|
124
|
+
const [valor, setValor] = useState("gavel");
|
|
125
|
+
return _jsx(IconPicker, { ariaLabel: "x", icons: ICONES, value: valor, onChange: setValor });
|
|
126
|
+
}
|
|
127
|
+
render(_jsx(Pai, {}));
|
|
128
|
+
expect(screen.getByRole("radio", { name: "Martelo" })).toHaveAttribute("aria-checked", "true");
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { render } from "@testing-library/react";
|
|
4
|
+
import Input from "../components/Input/Input";
|
|
5
|
+
describe("Input · grouped", () => {
|
|
6
|
+
it("grouped liga a classe própria e a do borderless; sem a prop, nenhuma das duas", () => {
|
|
7
|
+
const { container, rerender } = render(_jsx(Input, { ariaLabel: "Adicionar setor", grouped: true }));
|
|
8
|
+
const raiz = () => container.firstElementChild;
|
|
9
|
+
expect(raiz()).toHaveClass("umds-input--grouped");
|
|
10
|
+
expect(raiz()).toHaveClass("umds-input--borderless");
|
|
11
|
+
rerender(_jsx(Input, { ariaLabel: "Adicionar setor" }));
|
|
12
|
+
expect(raiz()).not.toHaveClass("umds-input--grouped");
|
|
13
|
+
expect(raiz()).not.toHaveClass("umds-input--borderless");
|
|
14
|
+
});
|
|
15
|
+
it("borderless sozinho não ganha a classe do grupo (segue com altura de campo e foco próprio)", () => {
|
|
16
|
+
const { container } = render(_jsx(Input, { ariaLabel: "x", borderless: true }));
|
|
17
|
+
expect(container.firstElementChild).toHaveClass("umds-input--borderless");
|
|
18
|
+
expect(container.firstElementChild).not.toHaveClass("umds-input--grouped");
|
|
19
|
+
});
|
|
20
|
+
it("o campo continua digitável dentro do grupo", () => {
|
|
21
|
+
const { container } = render(_jsx(Input, { ariaLabel: "Adicionar setor", grouped: true, placeholder: "SADM, SOBR..." }));
|
|
22
|
+
expect(container.querySelector("input")).toHaveAttribute("placeholder", "SADM, SOBR...");
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
4
|
+
import userEvent from "@testing-library/user-event";
|
|
5
|
+
import { faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons";
|
|
6
|
+
import Input from "../components/Input/Input";
|
|
7
|
+
describe("Input · onChange a cada tecla, uma vez só", () => {
|
|
8
|
+
it("digitar chama onChange uma vez por tecla, com o valor novo", async () => {
|
|
9
|
+
const aoMudar = vi.fn();
|
|
10
|
+
render(_jsx(Input, { label: "Nome", onChange: aoMudar }));
|
|
11
|
+
await userEvent.type(screen.getByLabelText("Nome"), "ab");
|
|
12
|
+
expect(aoMudar.mock.calls.map((c) => c[0])).toEqual(["a", "ab"]);
|
|
13
|
+
});
|
|
14
|
+
it("change disparado à mão também chega (testes de quem consome)", () => {
|
|
15
|
+
const aoMudar = vi.fn();
|
|
16
|
+
render(_jsx(Input, { label: "Nome", onChange: aoMudar }));
|
|
17
|
+
fireEvent.change(screen.getByLabelText("Nome"), { target: { value: "xyz" } });
|
|
18
|
+
expect(aoMudar).toHaveBeenCalledTimes(1);
|
|
19
|
+
expect(aoMudar).toHaveBeenCalledWith("xyz");
|
|
20
|
+
});
|
|
21
|
+
it("onBlur continua sendo o evento de saída do campo", async () => {
|
|
22
|
+
const aoSair = vi.fn();
|
|
23
|
+
const aoMudar = vi.fn();
|
|
24
|
+
render(_jsx(Input, { label: "Nome", onBlur: aoSair, onChange: aoMudar }));
|
|
25
|
+
const campo = screen.getByLabelText("Nome");
|
|
26
|
+
await userEvent.type(campo, "a");
|
|
27
|
+
expect(aoSair).not.toHaveBeenCalled();
|
|
28
|
+
await userEvent.tab();
|
|
29
|
+
expect(aoSair).toHaveBeenCalledTimes(1);
|
|
30
|
+
expect(aoMudar).toHaveBeenCalledTimes(1);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe("Input · mask", () => {
|
|
34
|
+
async function digitar(mask, texto) {
|
|
35
|
+
const aoMudar = vi.fn();
|
|
36
|
+
render(_jsx(Input, { label: "Campo", mask: mask, onChange: aoMudar }));
|
|
37
|
+
const campo = screen.getByLabelText("Campo");
|
|
38
|
+
await userEvent.type(campo, texto);
|
|
39
|
+
return { campo, aoMudar };
|
|
40
|
+
}
|
|
41
|
+
it("cpf", async () => {
|
|
42
|
+
const { campo, aoMudar } = await digitar("cpf", "12345678901");
|
|
43
|
+
expect(campo.value).toBe("123.456.789-01");
|
|
44
|
+
expect(aoMudar).toHaveBeenLastCalledWith("123.456.789-01");
|
|
45
|
+
});
|
|
46
|
+
it("cnpj", async () => {
|
|
47
|
+
const { campo } = await digitar("cnpj", "12345678000190");
|
|
48
|
+
expect(campo.value).toBe("12.345.678/0001-90");
|
|
49
|
+
});
|
|
50
|
+
it("cep", async () => {
|
|
51
|
+
const { campo } = await digitar("cep", "80010000");
|
|
52
|
+
expect(campo.value).toBe("80010-000");
|
|
53
|
+
});
|
|
54
|
+
it("phone troca de formato com 10 ou 11 dígitos", async () => {
|
|
55
|
+
expect((await digitar("phone", "4133334444")).campo.value).toBe("(41) 3333-4444");
|
|
56
|
+
});
|
|
57
|
+
it("phone de 11 dígitos", async () => {
|
|
58
|
+
expect((await digitar("phone", "41999998888")).campo.value).toBe("(41) 99999-8888");
|
|
59
|
+
});
|
|
60
|
+
it("ignora letras e passa do tamanho, sem estourar", async () => {
|
|
61
|
+
const { campo } = await digitar("cpf", "12a3.4b5-678901999");
|
|
62
|
+
expect(campo.value).toBe("123.456.789-01");
|
|
63
|
+
});
|
|
64
|
+
it("valor controlado com dígitos aparece formatado", () => {
|
|
65
|
+
render(_jsx(Input, { label: "CPF", mask: "cpf", value: "12345678901", onChange: () => { } }));
|
|
66
|
+
expect(screen.getByLabelText("CPF").value).toBe("123.456.789-01");
|
|
67
|
+
});
|
|
68
|
+
it("abre o teclado numérico e limita o tamanho; sem máscara, nenhum dos dois", () => {
|
|
69
|
+
render(_jsxs(_Fragment, { children: [_jsx(Input, { label: "Com", mask: "cep" }), _jsx(Input, { label: "Sem" })] }));
|
|
70
|
+
expect(screen.getByLabelText("Com")).toHaveAttribute("inputmode", "numeric");
|
|
71
|
+
expect(screen.getByLabelText("Com")).toHaveAttribute("maxlength", "9");
|
|
72
|
+
expect(screen.getByLabelText("Sem")).not.toHaveAttribute("inputmode");
|
|
73
|
+
expect(screen.getByLabelText("Sem")).not.toHaveAttribute("maxlength");
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe("Input · iconRight e borderless", () => {
|
|
77
|
+
it("iconRight é decorativo: aparece, não é botão e fica fora da árvore de acessibilidade", () => {
|
|
78
|
+
const { container } = render(_jsx(Input, { ariaLabel: "N\u00FAmero", iconRight: faMagnifyingGlass }));
|
|
79
|
+
const icone = container.querySelector(".umds-input__icon-right");
|
|
80
|
+
expect(icone).not.toBeNull();
|
|
81
|
+
expect(icone).toHaveAttribute("aria-hidden", "true");
|
|
82
|
+
expect(icone.querySelector("svg")).not.toBeNull();
|
|
83
|
+
expect(screen.queryByRole("button")).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
it("sem iconRight não há ícone à direita", () => {
|
|
86
|
+
const { container } = render(_jsx(Input, { ariaLabel: "x" }));
|
|
87
|
+
expect(container.querySelector(".umds-input__icon-right")).toBeNull();
|
|
88
|
+
});
|
|
89
|
+
it("borderless escreve a classe; sem a prop não", () => {
|
|
90
|
+
const { container } = render(_jsxs(_Fragment, { children: [_jsx(Input, { ariaLabel: "a", borderless: true }), _jsx(Input, { ariaLabel: "b" })] }));
|
|
91
|
+
expect(container.children[0]).toHaveClass("umds-input--borderless");
|
|
92
|
+
expect(container.children[1]).not.toHaveClass("umds-input--borderless");
|
|
93
|
+
});
|
|
94
|
+
it("borderless com erro continua marcando o erro", () => {
|
|
95
|
+
const { container } = render(_jsx(Input, { ariaLabel: "a", borderless: true, errorMessage: "Obrigat\u00F3rio" }));
|
|
96
|
+
expect(container.firstElementChild).toHaveClass("umds-input--borderless", "umds-input--error");
|
|
97
|
+
expect(screen.getByLabelText("a")).toHaveAttribute("aria-invalid", "true");
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { act, render, screen } from "@testing-library/react";
|
|
4
|
+
import userEvent from "@testing-library/user-event";
|
|
5
|
+
import Kbd from "../components/Kbd/Kbd";
|
|
6
|
+
import CodeBlock from "../components/CodeBlock/CodeBlock";
|
|
7
|
+
describe("Kbd", () => {
|
|
8
|
+
it("uma tecla é um kbd com o texto", () => {
|
|
9
|
+
const { container } = render(_jsx(Kbd, { children: "Esc" }));
|
|
10
|
+
const tecla = container.querySelector("kbd");
|
|
11
|
+
expect(tecla).toHaveTextContent("Esc");
|
|
12
|
+
expect(tecla).toHaveClass("umds-kbd", "umds-kbd--md");
|
|
13
|
+
expect(container.querySelectorAll("kbd")).toHaveLength(1);
|
|
14
|
+
});
|
|
15
|
+
it("keys desenha uma tecla por item, com o separador entre elas", () => {
|
|
16
|
+
const { container } = render(_jsx(Kbd, { keys: ["Ctrl", "Shift", "P"] }));
|
|
17
|
+
const conjunto = container.querySelector("kbd.umds-kbd-combo");
|
|
18
|
+
expect(conjunto).not.toBeNull();
|
|
19
|
+
expect(conjunto.querySelectorAll("kbd.umds-kbd")).toHaveLength(3);
|
|
20
|
+
expect(conjunto.textContent).toBe("Ctrl+Shift+P");
|
|
21
|
+
});
|
|
22
|
+
it("separator troca o texto entre as teclas; size vira classe", () => {
|
|
23
|
+
const { container } = render(_jsx(Kbd, { keys: ["Ctrl", "K"], separator: " + ", size: "sm" }));
|
|
24
|
+
expect(container.querySelector("kbd")).toHaveClass("umds-kbd--sm");
|
|
25
|
+
expect(container.textContent).toBe("Ctrl + K");
|
|
26
|
+
});
|
|
27
|
+
it("atributos HTML vão para o kbd", () => {
|
|
28
|
+
const { container } = render(_jsx(Kbd, { title: "Atalho", "data-x": "1", children: "Tab" }));
|
|
29
|
+
const tecla = container.querySelector("kbd");
|
|
30
|
+
expect(tecla).toHaveAttribute("title", "Atalho");
|
|
31
|
+
expect(tecla).toHaveAttribute("data-x", "1");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe("CodeBlock", () => {
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
vi.useRealTimers();
|
|
37
|
+
vi.restoreAllMocks();
|
|
38
|
+
});
|
|
39
|
+
it("mostra o código em pre/code, mantendo as quebras, numa região focável e nomeada", () => {
|
|
40
|
+
const { container } = render(_jsx(CodeBlock, { code: '{\n "ok": true\n}', ariaLabel: "Resposta" }));
|
|
41
|
+
const regiao = screen.getByRole("region", { name: "Resposta" });
|
|
42
|
+
expect(regiao.tagName).toBe("PRE");
|
|
43
|
+
expect(regiao).toHaveAttribute("tabindex", "0");
|
|
44
|
+
expect(container.querySelector("code")?.textContent).toBe('{\n "ok": true\n}');
|
|
45
|
+
});
|
|
46
|
+
it("o texto também vem como conteúdo; o nome padrão é a linguagem, ou Código", () => {
|
|
47
|
+
const { rerender } = render(_jsx(CodeBlock, { language: "JSON", children: "{}" }));
|
|
48
|
+
expect(screen.getByRole("region", { name: "JSON" })).toHaveTextContent("{}");
|
|
49
|
+
expect(screen.getByText("JSON", { selector: ".umds-code-block__language" })).toBeInTheDocument();
|
|
50
|
+
rerender(_jsx(CodeBlock, { code: "x" }));
|
|
51
|
+
expect(screen.getByRole("region", { name: "Código" })).toBeInTheDocument();
|
|
52
|
+
});
|
|
53
|
+
it("wrap e maxHeight", () => {
|
|
54
|
+
const { container } = render(_jsx(CodeBlock, { code: "linha longa", wrap: true, maxHeight: 120 }));
|
|
55
|
+
expect(container.querySelector(".umds-code-block--wrap")).not.toBeNull();
|
|
56
|
+
expect(container.querySelector("pre").style.maxHeight).toBe("120px");
|
|
57
|
+
});
|
|
58
|
+
it("sem copyable e sem linguagem, não há cabeçalho nem botão", () => {
|
|
59
|
+
render(_jsx(CodeBlock, { code: "x" }));
|
|
60
|
+
expect(screen.queryByRole("button")).toBeNull();
|
|
61
|
+
});
|
|
62
|
+
it("copiar manda o texto inteiro à área de transferência, confirma e volta depois de 2 s", async () => {
|
|
63
|
+
vi.useFakeTimers({ shouldAdvanceTime: true });
|
|
64
|
+
const escrever = vi.fn(() => Promise.resolve());
|
|
65
|
+
const aoCopiar = vi.fn();
|
|
66
|
+
const usuario = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
67
|
+
// O userEvent.setup instala a própria área de transferência: o espião entra depois.
|
|
68
|
+
Object.defineProperty(navigator, "clipboard", { value: { writeText: escrever }, configurable: true });
|
|
69
|
+
render(_jsx(CodeBlock, { code: '{"a":1}', copyable: true, onCopy: aoCopiar }));
|
|
70
|
+
await usuario.click(screen.getByRole("button", { name: "Copiar" }));
|
|
71
|
+
expect(escrever).toHaveBeenCalledWith('{"a":1}');
|
|
72
|
+
expect(await screen.findByRole("button", { name: "Copiado" })).toBeInTheDocument();
|
|
73
|
+
expect(aoCopiar).toHaveBeenCalledWith('{"a":1}');
|
|
74
|
+
expect(screen.getByRole("status")).toHaveTextContent("Copiado");
|
|
75
|
+
await act(async () => {
|
|
76
|
+
vi.advanceTimersByTime(2100);
|
|
77
|
+
});
|
|
78
|
+
expect(screen.getByRole("button", { name: "Copiar" })).toBeInTheDocument();
|
|
79
|
+
});
|
|
80
|
+
it("copiar com o texto vindo como conteúdo lê o texto do bloco", async () => {
|
|
81
|
+
const escrever = vi.fn(() => Promise.resolve());
|
|
82
|
+
const usuario = userEvent.setup();
|
|
83
|
+
Object.defineProperty(navigator, "clipboard", { value: { writeText: escrever }, configurable: true });
|
|
84
|
+
render(_jsx(CodeBlock, { copyable: true, children: "npm run build" }));
|
|
85
|
+
await usuario.click(screen.getByRole("button", { name: "Copiar" }));
|
|
86
|
+
expect(escrever).toHaveBeenCalledWith("npm run build");
|
|
87
|
+
});
|
|
88
|
+
it("copyLabel e copiedLabel por prop", async () => {
|
|
89
|
+
const usuario = userEvent.setup();
|
|
90
|
+
Object.defineProperty(navigator, "clipboard", { value: { writeText: () => Promise.resolve() }, configurable: true });
|
|
91
|
+
render(_jsx(CodeBlock, { code: "x", copyable: true, copyLabel: "Copy", copiedLabel: "Copied" }));
|
|
92
|
+
await usuario.click(screen.getByRole("button", { name: "Copy" }));
|
|
93
|
+
expect(await screen.findByRole("button", { name: "Copied" })).toBeInTheDocument();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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,39 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { render, screen, fireEvent } from "@testing-library/react";
|
|
4
|
+
import ListItem from "../components/List/ListItem";
|
|
5
|
+
describe("ListItem: tooltip, standalone e atributos só no elemento interativo", () => {
|
|
6
|
+
it("tooltip vira o atributo title do elemento interativo, e o li fica limpo", () => {
|
|
7
|
+
const { container } = render(_jsx("ul", { children: _jsx(ListItem, { title: "Memorando 114/2025", tooltip: "Abrir o memorando", "aria-label": "Memorando", role: "menuitem", "data-origem": "home" }) }));
|
|
8
|
+
const li = container.querySelector("li");
|
|
9
|
+
const botao = screen.getByRole("menuitem");
|
|
10
|
+
expect(botao).toHaveAttribute("title", "Abrir o memorando");
|
|
11
|
+
expect(botao).toHaveAttribute("aria-label", "Memorando");
|
|
12
|
+
expect(botao).toHaveAttribute("data-origem", "home");
|
|
13
|
+
for (const atributo of ["title", "aria-label", "role", "data-origem"])
|
|
14
|
+
expect(li).not.toHaveAttribute(atributo);
|
|
15
|
+
});
|
|
16
|
+
it("tooltip funciona no link e na linha div; sem tooltip não há title", () => {
|
|
17
|
+
const { container } = render(_jsxs("ul", { children: [_jsx(ListItem, { title: "Link", href: "/x", tooltip: "Dica do link" }), _jsx(ListItem, { as: "div", title: "Div", tooltip: "Dica da div" }), _jsx(ListItem, { title: "Sem dica" })] }));
|
|
18
|
+
const titulos = [...container.querySelectorAll(".umds-list-item")].map((e) => e.getAttribute("title"));
|
|
19
|
+
expect(titulos).toEqual(["Dica do link", "Dica da div", null]);
|
|
20
|
+
});
|
|
21
|
+
it("o título da linha continua sendo texto, não o atributo", () => {
|
|
22
|
+
const { container } = render(_jsx("ul", { children: _jsx(ListItem, { title: "S\u00F3 o t\u00EDtulo" }) }));
|
|
23
|
+
expect(container.querySelector("[title]")).toBeNull();
|
|
24
|
+
});
|
|
25
|
+
it("standalone renderiza sem li, só o elemento interativo", () => {
|
|
26
|
+
const aoClicar = vi.fn();
|
|
27
|
+
const { container } = render(_jsx("div", { children: _jsx(ListItem, { standalone: true, title: "12", tooltip: "P\u00E1gina 12", onClick: aoClicar }) }));
|
|
28
|
+
expect(container.querySelector("li")).toBeNull();
|
|
29
|
+
const botao = screen.getByRole("button", { name: /12/ });
|
|
30
|
+
expect(botao.parentElement?.tagName).toBe("DIV");
|
|
31
|
+
expect(botao).toHaveAttribute("title", "Página 12");
|
|
32
|
+
fireEvent.click(botao);
|
|
33
|
+
expect(aoClicar).toHaveBeenCalledTimes(1);
|
|
34
|
+
});
|
|
35
|
+
it("sem standalone o li continua lá", () => {
|
|
36
|
+
const { container } = render(_jsx("ul", { children: _jsx(ListItem, { title: "Normal" }) }));
|
|
37
|
+
expect(container.querySelectorAll("li")).toHaveLength(1);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|