@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,94 @@
|
|
|
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 Toast from "../components/Toast/Toast";
|
|
5
|
+
import ToastStack from "../components/Toast/ToastStack";
|
|
6
|
+
import { dismissAllToasts, toast } from "../utils/toast";
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
act(() => dismissAllToasts());
|
|
9
|
+
});
|
|
10
|
+
describe("Toast · ação e detalhes", () => {
|
|
11
|
+
it("actionLabel com onAction desenha o botão; acionar chama onAction e fecha", () => {
|
|
12
|
+
const aoAgir = vi.fn();
|
|
13
|
+
const aoFechar = vi.fn();
|
|
14
|
+
render(_jsx(Toast, { open: true, message: "Despacho enviado", actionLabel: "Desfazer", onAction: aoAgir, onClose: aoFechar }));
|
|
15
|
+
fireEvent.click(screen.getByRole("button", { name: "Desfazer" }));
|
|
16
|
+
expect(aoAgir).toHaveBeenCalledTimes(1);
|
|
17
|
+
expect(aoFechar).toHaveBeenCalledTimes(1);
|
|
18
|
+
});
|
|
19
|
+
it("a ação roda antes de fechar", () => {
|
|
20
|
+
const ordem = [];
|
|
21
|
+
render(_jsx(Toast, { open: true, message: "x", actionLabel: "Ver processo", onAction: () => ordem.push("acao"), onClose: () => ordem.push("fechar") }));
|
|
22
|
+
fireEvent.click(screen.getByRole("button", { name: "Ver processo" }));
|
|
23
|
+
expect(ordem).toEqual(["acao", "fechar"]);
|
|
24
|
+
});
|
|
25
|
+
it("actionLabel sem onAction, ou onAction sem actionLabel, não desenha botão de ação", () => {
|
|
26
|
+
const { rerender } = render(_jsx(Toast, { open: true, message: "x", actionLabel: "Desfazer" }));
|
|
27
|
+
expect(screen.queryByRole("button", { name: "Desfazer" })).toBeNull();
|
|
28
|
+
rerender(_jsx(Toast, { open: true, message: "x", onAction: () => { } }));
|
|
29
|
+
expect(document.querySelector(".umds-toast__action")).toBeNull();
|
|
30
|
+
});
|
|
31
|
+
it("details vira uma lista de linhas abaixo da descrição", () => {
|
|
32
|
+
render(_jsx(Toast, { open: true, message: "3 anexos n\u00E3o enviados", description: "Corrija e envie de novo.", details: ["Anexo 1 acima de 10 MB", "Anexo 2 sem assunto"] }));
|
|
33
|
+
const itens = screen.getAllByRole("listitem");
|
|
34
|
+
expect(itens.map((i) => i.textContent)).toEqual(["Anexo 1 acima de 10 MB", "Anexo 2 sem assunto"]);
|
|
35
|
+
});
|
|
36
|
+
it("sem details não há lista", () => {
|
|
37
|
+
render(_jsx(Toast, { open: true, message: "Salvo" }));
|
|
38
|
+
expect(document.querySelector(".umds-toast__details")).toBeNull();
|
|
39
|
+
});
|
|
40
|
+
it("o botão de fechar continua ao lado da ação", () => {
|
|
41
|
+
render(_jsx(Toast, { open: true, message: "x", actionLabel: "Desfazer", onAction: () => { } }));
|
|
42
|
+
expect(screen.getByRole("button", { name: "Fechar notificação" })).toBeInTheDocument();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe("toast() imperativo com ação e detalhes", () => {
|
|
46
|
+
it("passa ação e detalhes ao ToastStack, e acionar a ação fecha o toast", () => {
|
|
47
|
+
const aoAgir = vi.fn();
|
|
48
|
+
render(_jsx(ToastStack, {}));
|
|
49
|
+
act(() => {
|
|
50
|
+
toast({ message: "Despacho enviado", details: ["SADM", "SOBR"], actionLabel: "Desfazer", onAction: aoAgir });
|
|
51
|
+
});
|
|
52
|
+
expect(screen.getByText("Despacho enviado")).toBeInTheDocument();
|
|
53
|
+
expect(screen.getAllByRole("listitem")).toHaveLength(2);
|
|
54
|
+
fireEvent.click(screen.getByRole("button", { name: "Desfazer" }));
|
|
55
|
+
expect(aoAgir).toHaveBeenCalledTimes(1);
|
|
56
|
+
expect(screen.queryByText("Despacho enviado")).toBeNull();
|
|
57
|
+
});
|
|
58
|
+
it("toast com ação não some sozinho (duration 0); sem ação continua em 5000", () => {
|
|
59
|
+
vi.useFakeTimers();
|
|
60
|
+
try {
|
|
61
|
+
render(_jsx(ToastStack, {}));
|
|
62
|
+
act(() => {
|
|
63
|
+
toast({ message: "Com ação", actionLabel: "Desfazer", onAction: () => { } });
|
|
64
|
+
toast({ message: "Sem ação" });
|
|
65
|
+
});
|
|
66
|
+
expect(screen.getByText("Com ação")).toBeInTheDocument();
|
|
67
|
+
expect(screen.getByText("Sem ação")).toBeInTheDocument();
|
|
68
|
+
act(() => {
|
|
69
|
+
vi.advanceTimersByTime(5100);
|
|
70
|
+
});
|
|
71
|
+
expect(screen.queryByText("Sem ação")).toBeNull();
|
|
72
|
+
expect(screen.getByText("Com ação")).toBeInTheDocument();
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
vi.useRealTimers();
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
it("um duration explícito vale também com ação", () => {
|
|
79
|
+
vi.useFakeTimers();
|
|
80
|
+
try {
|
|
81
|
+
render(_jsx(ToastStack, {}));
|
|
82
|
+
act(() => {
|
|
83
|
+
toast({ message: "Curto", actionLabel: "Desfazer", onAction: () => { }, duration: 1000 });
|
|
84
|
+
});
|
|
85
|
+
act(() => {
|
|
86
|
+
vi.advanceTimersByTime(1100);
|
|
87
|
+
});
|
|
88
|
+
expect(screen.queryByText("Curto")).toBeNull();
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
vi.useRealTimers();
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { render, screen, waitFor } from "@testing-library/react";
|
|
4
|
+
import Tooltip from "../components/Tooltip/Tooltip";
|
|
5
|
+
async function abrir(ui) {
|
|
6
|
+
render(ui);
|
|
7
|
+
screen.getByRole("button").focus();
|
|
8
|
+
await waitFor(() => expect(screen.getByRole("tooltip")).toHaveClass("umds-tooltip__bubble--open"));
|
|
9
|
+
return screen.getByRole("tooltip");
|
|
10
|
+
}
|
|
11
|
+
describe("Tooltip · variant dark e maxWidth", () => {
|
|
12
|
+
it("dark escreve a classe no balão e no wrapper flutuante", async () => {
|
|
13
|
+
const balao = await abrir(_jsx(Tooltip, { content: "Atualizar", variant: "dark", children: _jsx("button", { type: "button", children: "Atualizar" }) }));
|
|
14
|
+
expect(balao).toHaveClass("umds-tooltip__bubble--dark");
|
|
15
|
+
expect(balao.parentElement).toHaveClass("umds-tooltip__floating--dark");
|
|
16
|
+
});
|
|
17
|
+
it("sem variant o balão é o padrão", async () => {
|
|
18
|
+
const balao = await abrir(_jsx(Tooltip, { content: "Atualizar", children: _jsx("button", { type: "button", children: "Atualizar" }) }));
|
|
19
|
+
expect(balao).not.toHaveClass("umds-tooltip__bubble--dark");
|
|
20
|
+
expect(balao.parentElement).not.toHaveClass("umds-tooltip__floating--dark");
|
|
21
|
+
expect(balao.parentElement?.style.maxWidth).toBe("");
|
|
22
|
+
});
|
|
23
|
+
it("maxWidth vai ao wrapper flutuante, em qualquer variante", async () => {
|
|
24
|
+
const balao = await abrir(_jsx(Tooltip, { content: "Texto longo", variant: "dark", maxWidth: "140px", children: _jsx("button", { type: "button", children: "x" }) }));
|
|
25
|
+
expect(balao.parentElement?.style.maxWidth).toBe("140px");
|
|
26
|
+
});
|
|
27
|
+
it("a descrição acessível não muda com a variante", () => {
|
|
28
|
+
render(_jsx(Tooltip, { content: "Atualizar", variant: "dark", children: _jsx("button", { type: "button", children: "Recarregar" }) }));
|
|
29
|
+
expect(screen.getByRole("button")).toHaveAccessibleDescription("Atualizar");
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Datas do `Calendar`, em ISO local (`YYYY-MM-DD`) e sem biblioteca.
|
|
3
|
+
*
|
|
4
|
+
* Tudo aqui trabalha com o DIA como texto: `"2026-05-15"`. O `Date` só aparece
|
|
5
|
+
* por dentro, sempre à meia-noite LOCAL (`new Date(ano, mes, dia)`), nunca
|
|
6
|
+
* `new Date("2026-05-15")`, que o JavaScript lê como UTC e, no fuso do Brasil,
|
|
7
|
+
* devolve o dia anterior.
|
|
8
|
+
*/
|
|
9
|
+
/** `Date` local → `YYYY-MM-DD`. */
|
|
10
|
+
export declare const toIso: (data: Date) => string;
|
|
11
|
+
/** `YYYY-MM-DD` → `Date` à meia-noite local. */
|
|
12
|
+
export declare const parseIso: (iso: string) => Date;
|
|
13
|
+
/** Aceita `Date` ou texto (`2026-05-15` e `2026-05-15T10:00`) e devolve `YYYY-MM-DD`; "" se não der. */
|
|
14
|
+
export declare const normalizeIso: (valor: any) => string;
|
|
15
|
+
export declare const addDays: (iso: string, dias: number) => string;
|
|
16
|
+
/** Soma meses mantendo o dia, ou o último dia do mês de chegada (31/01 + 1 mês = 28/02). */
|
|
17
|
+
export declare const addMonths: (iso: string, meses: number) => string;
|
|
18
|
+
/** Dia da semana: 0 = domingo. */
|
|
19
|
+
export declare const weekdayOf: (iso: string) => number;
|
|
20
|
+
/** Primeiro dia da semana que contém `iso`, dado o dia em que a semana começa (0 = domingo, 1 = segunda). */
|
|
21
|
+
export declare const startOfWeek: (iso: string, comecaEm: number) => string;
|
|
22
|
+
export declare const startOfMonth: (iso: string) => string;
|
|
23
|
+
export declare const endOfMonth: (iso: string) => string;
|
|
24
|
+
export declare const isSameMonth: (a: string, b: string) => boolean;
|
|
25
|
+
/** Os sete dias da semana que contém `iso`. */
|
|
26
|
+
export declare const weekDays: (iso: string, comecaEm: number) => string[];
|
|
27
|
+
/** O mês de `iso` em semanas inteiras (de 4 a 6 linhas), com os dias dos meses vizinhos que completam as pontas. */
|
|
28
|
+
export declare const monthMatrix: (iso: string, comecaEm: number) => string[][];
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Datas do `Calendar`, em ISO local (`YYYY-MM-DD`) e sem biblioteca.
|
|
3
|
+
*
|
|
4
|
+
* Tudo aqui trabalha com o DIA como texto: `"2026-05-15"`. O `Date` só aparece
|
|
5
|
+
* por dentro, sempre à meia-noite LOCAL (`new Date(ano, mes, dia)`), nunca
|
|
6
|
+
* `new Date("2026-05-15")`, que o JavaScript lê como UTC e, no fuso do Brasil,
|
|
7
|
+
* devolve o dia anterior.
|
|
8
|
+
*/
|
|
9
|
+
const doisDigitos = (n) => n < 10 ? "0" + n : String(n);
|
|
10
|
+
/** `Date` local → `YYYY-MM-DD`. */
|
|
11
|
+
export const toIso = (data) => data.getFullYear() + "-" + doisDigitos(data.getMonth() + 1) + "-" + doisDigitos(data.getDate());
|
|
12
|
+
/** `YYYY-MM-DD` → `Date` à meia-noite local. */
|
|
13
|
+
export const parseIso = (iso) => {
|
|
14
|
+
const partes = iso.slice(0, 10).split("-");
|
|
15
|
+
return new Date(Number(partes[0]), Number(partes[1]) - 1, Number(partes[2]));
|
|
16
|
+
};
|
|
17
|
+
/** Aceita `Date` ou texto (`2026-05-15` e `2026-05-15T10:00`) e devolve `YYYY-MM-DD`; "" se não der. */
|
|
18
|
+
export const normalizeIso = (valor) => {
|
|
19
|
+
if (!valor)
|
|
20
|
+
return "";
|
|
21
|
+
if (typeof valor === "string")
|
|
22
|
+
return /^\d{4}-\d{2}-\d{2}/.test(valor) ? valor.slice(0, 10) : "";
|
|
23
|
+
if (valor instanceof Date && !isNaN(valor.getTime()))
|
|
24
|
+
return toIso(valor);
|
|
25
|
+
return "";
|
|
26
|
+
};
|
|
27
|
+
export const addDays = (iso, dias) => {
|
|
28
|
+
const data = parseIso(iso);
|
|
29
|
+
data.setDate(data.getDate() + dias);
|
|
30
|
+
return toIso(data);
|
|
31
|
+
};
|
|
32
|
+
/** Soma meses mantendo o dia, ou o último dia do mês de chegada (31/01 + 1 mês = 28/02). */
|
|
33
|
+
export const addMonths = (iso, meses) => {
|
|
34
|
+
const data = parseIso(iso);
|
|
35
|
+
const dia = data.getDate();
|
|
36
|
+
data.setDate(1);
|
|
37
|
+
data.setMonth(data.getMonth() + meses);
|
|
38
|
+
const ultimo = new Date(data.getFullYear(), data.getMonth() + 1, 0).getDate();
|
|
39
|
+
data.setDate(Math.min(dia, ultimo));
|
|
40
|
+
return toIso(data);
|
|
41
|
+
};
|
|
42
|
+
/** Dia da semana: 0 = domingo. */
|
|
43
|
+
export const weekdayOf = (iso) => parseIso(iso).getDay();
|
|
44
|
+
/** Primeiro dia da semana que contém `iso`, dado o dia em que a semana começa (0 = domingo, 1 = segunda). */
|
|
45
|
+
export const startOfWeek = (iso, comecaEm) => {
|
|
46
|
+
const recuo = (weekdayOf(iso) - comecaEm + 7) % 7;
|
|
47
|
+
return addDays(iso, -recuo);
|
|
48
|
+
};
|
|
49
|
+
export const startOfMonth = (iso) => iso.slice(0, 8) + "01";
|
|
50
|
+
export const endOfMonth = (iso) => {
|
|
51
|
+
const data = parseIso(iso);
|
|
52
|
+
return toIso(new Date(data.getFullYear(), data.getMonth() + 1, 0));
|
|
53
|
+
};
|
|
54
|
+
export const isSameMonth = (a, b) => a.slice(0, 7) === b.slice(0, 7);
|
|
55
|
+
/** Os sete dias da semana que contém `iso`. */
|
|
56
|
+
export const weekDays = (iso, comecaEm) => {
|
|
57
|
+
const inicio = startOfWeek(iso, comecaEm);
|
|
58
|
+
const dias = [];
|
|
59
|
+
for (let i = 0; i < 7; i++)
|
|
60
|
+
dias.push(addDays(inicio, i));
|
|
61
|
+
return dias;
|
|
62
|
+
};
|
|
63
|
+
/** O mês de `iso` em semanas inteiras (de 4 a 6 linhas), com os dias dos meses vizinhos que completam as pontas. */
|
|
64
|
+
export const monthMatrix = (iso, comecaEm) => {
|
|
65
|
+
const ultimo = endOfMonth(iso);
|
|
66
|
+
let cursor = startOfWeek(startOfMonth(iso), comecaEm);
|
|
67
|
+
const semanas = [];
|
|
68
|
+
while (cursor <= ultimo) {
|
|
69
|
+
semanas.push(weekDays(cursor, comecaEm));
|
|
70
|
+
cursor = addDays(cursor, 7);
|
|
71
|
+
}
|
|
72
|
+
return semanas;
|
|
73
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML seguro para o `RichTextEditor`.
|
|
3
|
+
*
|
|
4
|
+
* O editor devolve HTML (`onChange`) e recebe HTML (`value`). HTML que vem de
|
|
5
|
+
* fora (um valor guardado, um texto colado) pode trazer `<img onerror>`,
|
|
6
|
+
* `<a href="javascript:...">`, `<script>`, `style` e classes de outro site. Este
|
|
7
|
+
* módulo reduz o HTML ao que o editor sabe escrever: negrito, itálico,
|
|
8
|
+
* sublinhado, tachado, listas, parágrafos, quebras, títulos, citação, link,
|
|
9
|
+
* estilo de texto por token (`color`, `font-size`, `font-family` em `<span>`),
|
|
10
|
+
* alinhamento de bloco (`text-align`), imagem (`http(s)`, caminho ou `data:image`
|
|
11
|
+
* em PNG, JPEG, GIF e WebP) e tabela simples.
|
|
12
|
+
*
|
|
13
|
+
* Roda no navegador (precisa de DOM). Dentro de um `<template>` o HTML é inerte:
|
|
14
|
+
* nada executa e nenhuma imagem carrega enquanto se higieniza. Sem DOM
|
|
15
|
+
* (servidor), devolve o texto escapado.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Endereço de link que o editor aceita: `http:`, `https:`, `mailto:`, `tel:`,
|
|
19
|
+
* caminho (`/x`, `./x`, `../x`) ou âncora (`#x`). Devolve o endereço limpo, ou ""
|
|
20
|
+
* se for perigoso (`javascript:`, `data:`, `vbscript:`...).
|
|
21
|
+
*/
|
|
22
|
+
export declare const enderecoSeguro: (bruto: string) => string;
|
|
23
|
+
/** Devolve o endereço pronto para `href`: o seguro como veio, ou `https://` + o texto quando não tem esquema. "" se perigoso ou vazio. */
|
|
24
|
+
export declare const normalizarEndereco: (bruto: string) => string;
|
|
25
|
+
/**
|
|
26
|
+
* Declaração de estilo de texto que o editor aceita: `color`, `font-size` e `font-family`,
|
|
27
|
+
* cada uma só com um token do DS. Devolve a declaração limpa ("color: var(--x);") ou "".
|
|
28
|
+
*/
|
|
29
|
+
export declare const estiloDeTexto: (propriedade: string, valor: string) => string;
|
|
30
|
+
/**
|
|
31
|
+
* Endereço de imagem que o editor aceita: `http(s)`, caminho (`/x`, `./x`, `../x`) ou
|
|
32
|
+
* `data:image` em base64 de PNG, JPEG, GIF ou WebP (SVG fica de fora: carrega script).
|
|
33
|
+
* Devolve o endereço limpo, ou "" se for perigoso ou vazio.
|
|
34
|
+
*/
|
|
35
|
+
export declare const imagemSegura: (bruto: string) => string;
|
|
36
|
+
/** Reduz `html` ao que o editor escreve. Sem DOM, devolve o texto escapado. */
|
|
37
|
+
export declare const sanitizarHtml: (html: string) => string;
|
|
38
|
+
/** A tabela vazia que o editor insere: `linhas` × `colunas` células com uma quebra, para o cursor entrar. */
|
|
39
|
+
export declare const htmlDaTabela: (linhas: number, colunas: number) => string;
|
|
40
|
+
/** O `<img>` que o editor insere (endereço já seguro). */
|
|
41
|
+
export declare const htmlDaImagem: (endereco: string, alt: string) => string;
|
|
42
|
+
/** O editor está "vazio": sem texto, sem lista e sem quebra solta (o `<br>` que o navegador deixa). */
|
|
43
|
+
export declare const htmlVazio: (html: string) => boolean;
|
|
44
|
+
/** O `<a>` que o editor escreve quando o link não tem texto escolhido: o endereço (já seguro) é o href e o texto. */
|
|
45
|
+
export declare const htmlDoLink: (endereco: string) => string;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML seguro para o `RichTextEditor`.
|
|
3
|
+
*
|
|
4
|
+
* O editor devolve HTML (`onChange`) e recebe HTML (`value`). HTML que vem de
|
|
5
|
+
* fora (um valor guardado, um texto colado) pode trazer `<img onerror>`,
|
|
6
|
+
* `<a href="javascript:...">`, `<script>`, `style` e classes de outro site. Este
|
|
7
|
+
* módulo reduz o HTML ao que o editor sabe escrever: negrito, itálico,
|
|
8
|
+
* sublinhado, tachado, listas, parágrafos, quebras, títulos, citação, link,
|
|
9
|
+
* estilo de texto por token (`color`, `font-size`, `font-family` em `<span>`),
|
|
10
|
+
* alinhamento de bloco (`text-align`), imagem (`http(s)`, caminho ou `data:image`
|
|
11
|
+
* em PNG, JPEG, GIF e WebP) e tabela simples.
|
|
12
|
+
*
|
|
13
|
+
* Roda no navegador (precisa de DOM). Dentro de um `<template>` o HTML é inerte:
|
|
14
|
+
* nada executa e nenhuma imagem carrega enquanto se higieniza. Sem DOM
|
|
15
|
+
* (servidor), devolve o texto escapado.
|
|
16
|
+
*/
|
|
17
|
+
const PERMITIDAS = ["A", "B", "STRONG", "I", "EM", "U", "S", "STRIKE", "DEL", "UL", "OL", "LI", "P", "BR", "DIV", "SPAN", "H1", "H2", "H3", "H4", "BLOCKQUOTE", "IMG", "TABLE", "THEAD", "TBODY", "TR", "TH", "TD"];
|
|
18
|
+
/** Blocos que aceitam `text-align`. */
|
|
19
|
+
const ALINHAVEIS = ["P", "DIV", "H1", "H2", "H3", "H4", "LI", "BLOCKQUOTE", "TH", "TD"];
|
|
20
|
+
const ALINHAMENTOS = ["left", "center", "right", "justify"];
|
|
21
|
+
const DESCARTADAS = ["SCRIPT", "STYLE", "IFRAME", "OBJECT", "EMBED", "LINK", "META", "TEMPLATE", "NOSCRIPT", "SVG", "MATH", "FORM", "INPUT", "BUTTON", "TEXTAREA", "SELECT"];
|
|
22
|
+
const escapar = (texto) => texto.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
23
|
+
/**
|
|
24
|
+
* Endereço de link que o editor aceita: `http:`, `https:`, `mailto:`, `tel:`,
|
|
25
|
+
* caminho (`/x`, `./x`, `../x`) ou âncora (`#x`). Devolve o endereço limpo, ou ""
|
|
26
|
+
* se for perigoso (`javascript:`, `data:`, `vbscript:`...).
|
|
27
|
+
*/
|
|
28
|
+
export const enderecoSeguro = (bruto) => {
|
|
29
|
+
const texto = (bruto || "").trim();
|
|
30
|
+
if (!texto)
|
|
31
|
+
return "";
|
|
32
|
+
// Caracteres de controle e espaços no meio do esquema são o truque clássico (`java\nscript:`).
|
|
33
|
+
const semControle = texto.replace(/[\u0000-\u001f\u007f\s]+/g, "");
|
|
34
|
+
if (/^(https?:\/\/|mailto:|tel:|\/|\.\.?\/|#)/i.test(semControle))
|
|
35
|
+
return texto;
|
|
36
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(semControle))
|
|
37
|
+
return "";
|
|
38
|
+
return "";
|
|
39
|
+
};
|
|
40
|
+
/** Devolve o endereço pronto para `href`: o seguro como veio, ou `https://` + o texto quando não tem esquema. "" se perigoso ou vazio. */
|
|
41
|
+
export const normalizarEndereco = (bruto) => {
|
|
42
|
+
const texto = (bruto || "").trim();
|
|
43
|
+
if (!texto)
|
|
44
|
+
return "";
|
|
45
|
+
const seguro = enderecoSeguro(texto);
|
|
46
|
+
if (seguro)
|
|
47
|
+
return seguro;
|
|
48
|
+
const semControle = texto.replace(/[\u0000-\u001f\u007f\s]+/g, "");
|
|
49
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(semControle))
|
|
50
|
+
return "";
|
|
51
|
+
return "https://" + texto;
|
|
52
|
+
};
|
|
53
|
+
/** Cor, tamanho e família de fonte só valem como TOKEN do DS: `var(--umds-...)`. Cor em hex, rgb ou nome sai. */
|
|
54
|
+
const COR_TOKEN = /^var\(--umds-(?:primitive|semantic)-[a-z0-9-]+\)$/;
|
|
55
|
+
const TAMANHO_TOKEN = /^var\(--umds-primitive-font-size-[a-z0-9]+\)$/;
|
|
56
|
+
const FAMILIA_TOKEN = /^var\(--umds-(?:primitive|semantic)-font-family-(?:base|mono)\)$/;
|
|
57
|
+
/**
|
|
58
|
+
* Declaração de estilo de texto que o editor aceita: `color`, `font-size` e `font-family`,
|
|
59
|
+
* cada uma só com um token do DS. Devolve a declaração limpa ("color: var(--x);") ou "".
|
|
60
|
+
*/
|
|
61
|
+
export const estiloDeTexto = (propriedade, valor) => {
|
|
62
|
+
const prop = String(propriedade || "").trim().toLowerCase();
|
|
63
|
+
const val = String(valor || "").trim();
|
|
64
|
+
if (prop === "color" && COR_TOKEN.test(val))
|
|
65
|
+
return "color: " + val + ";";
|
|
66
|
+
if (prop === "font-size" && TAMANHO_TOKEN.test(val))
|
|
67
|
+
return "font-size: " + val + ";";
|
|
68
|
+
if (prop === "font-family" && FAMILIA_TOKEN.test(val))
|
|
69
|
+
return "font-family: " + val + ";";
|
|
70
|
+
return "";
|
|
71
|
+
};
|
|
72
|
+
/** As declarações válidas de um atributo `style` (só color, font-size e font-family por token). */
|
|
73
|
+
const estiloDoSpan = (estilo) => {
|
|
74
|
+
const partes = String(estilo || "").split(";");
|
|
75
|
+
let saida = "";
|
|
76
|
+
for (let i = 0; i < partes.length; i++) {
|
|
77
|
+
const corte = partes[i].indexOf(":");
|
|
78
|
+
if (corte === -1)
|
|
79
|
+
continue;
|
|
80
|
+
const decl = estiloDeTexto(partes[i].slice(0, corte), partes[i].slice(corte + 1));
|
|
81
|
+
if (decl)
|
|
82
|
+
saida += (saida ? " " : "") + decl;
|
|
83
|
+
}
|
|
84
|
+
return saida;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Endereço de imagem que o editor aceita: `http(s)`, caminho (`/x`, `./x`, `../x`) ou
|
|
88
|
+
* `data:image` em base64 de PNG, JPEG, GIF ou WebP (SVG fica de fora: carrega script).
|
|
89
|
+
* Devolve o endereço limpo, ou "" se for perigoso ou vazio.
|
|
90
|
+
*/
|
|
91
|
+
export const imagemSegura = (bruto) => {
|
|
92
|
+
const texto = (bruto || "").trim();
|
|
93
|
+
if (!texto)
|
|
94
|
+
return "";
|
|
95
|
+
const semControle = texto.replace(/[\u0000-\u001f\u007f\s]+/g, "");
|
|
96
|
+
if (/^data:image\/(png|jpe?g|gif|webp);base64,[a-z0-9+/]+=*$/i.test(semControle))
|
|
97
|
+
return semControle;
|
|
98
|
+
// `//host/x` (sem esquema) carregaria de outro site sem o autor ter pedido: fica de fora.
|
|
99
|
+
if (/^(https?:\/\/|\/(?!\/)|\.\.?\/)/i.test(semControle))
|
|
100
|
+
return texto;
|
|
101
|
+
return "";
|
|
102
|
+
};
|
|
103
|
+
/** O `text-align` de um elemento, pelo `style` ou pelo atributo `align`: left, center, right, justify ou "". */
|
|
104
|
+
const alinhamentoDe = (elemento) => {
|
|
105
|
+
const estilo = String(elemento.getAttribute("style") || "");
|
|
106
|
+
const achado = /(?:^|;)\s*text-align\s*:\s*([a-z]+)\s*(?:;|$)/i.exec(estilo);
|
|
107
|
+
const valor = (achado ? achado[1] : String(elemento.getAttribute("align") || "")).toLowerCase();
|
|
108
|
+
return ALINHAMENTOS.indexOf(valor) !== -1 ? valor : "";
|
|
109
|
+
};
|
|
110
|
+
/** Número inteiro de 2 a 99 em `colspan`/`rowspan`; o resto some. */
|
|
111
|
+
const vao = (valor) => {
|
|
112
|
+
const n = parseInt(String(valor || ""), 10);
|
|
113
|
+
return n >= 2 && n <= 99 ? String(n) : "";
|
|
114
|
+
};
|
|
115
|
+
const limparNo = (no, saida, documento) => {
|
|
116
|
+
const filhos = Array.prototype.slice.call(no.childNodes);
|
|
117
|
+
for (let i = 0; i < filhos.length; i++) {
|
|
118
|
+
const filho = filhos[i];
|
|
119
|
+
if (filho.nodeType === 3) {
|
|
120
|
+
saida.appendChild(documento.createTextNode(filho.nodeValue));
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (filho.nodeType !== 1)
|
|
124
|
+
continue;
|
|
125
|
+
const tag = String(filho.tagName).toUpperCase();
|
|
126
|
+
if (DESCARTADAS.indexOf(tag) !== -1)
|
|
127
|
+
continue;
|
|
128
|
+
if (PERMITIDAS.indexOf(tag) === -1) {
|
|
129
|
+
// Tag desconhecida: some a tag, fica o conteúdo.
|
|
130
|
+
limparNo(filho, saida, documento);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (tag === "IMG") {
|
|
134
|
+
// Imagem sem endereço seguro some inteira (não deixa um <img> quebrado nem carregando de fora).
|
|
135
|
+
const origem = imagemSegura(filho.getAttribute("src") || "");
|
|
136
|
+
if (!origem)
|
|
137
|
+
continue;
|
|
138
|
+
const imagem = documento.createElement("img");
|
|
139
|
+
imagem.setAttribute("src", origem);
|
|
140
|
+
imagem.setAttribute("alt", String(filho.getAttribute("alt") || ""));
|
|
141
|
+
saida.appendChild(imagem);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const novo = documento.createElement(tag.toLowerCase());
|
|
145
|
+
if (ALINHAVEIS.indexOf(tag) !== -1) {
|
|
146
|
+
const alinhamento = alinhamentoDe(filho);
|
|
147
|
+
if (alinhamento)
|
|
148
|
+
novo.setAttribute("style", "text-align: " + alinhamento + ";");
|
|
149
|
+
}
|
|
150
|
+
if (tag === "SPAN") {
|
|
151
|
+
const estiloValido = estiloDoSpan(filho.getAttribute("style") || "");
|
|
152
|
+
if (estiloValido)
|
|
153
|
+
novo.setAttribute("style", estiloValido);
|
|
154
|
+
}
|
|
155
|
+
if (tag === "TD" || tag === "TH") {
|
|
156
|
+
const colunas = vao(filho.getAttribute("colspan"));
|
|
157
|
+
const linhas = vao(filho.getAttribute("rowspan"));
|
|
158
|
+
if (colunas)
|
|
159
|
+
novo.setAttribute("colspan", colunas);
|
|
160
|
+
if (linhas)
|
|
161
|
+
novo.setAttribute("rowspan", linhas);
|
|
162
|
+
}
|
|
163
|
+
if (tag === "A") {
|
|
164
|
+
const href = enderecoSeguro(filho.getAttribute("href") || "");
|
|
165
|
+
if (href) {
|
|
166
|
+
novo.setAttribute("href", href);
|
|
167
|
+
novo.setAttribute("rel", "noopener noreferrer");
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
limparNo(filho, novo, documento);
|
|
171
|
+
saida.appendChild(novo);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
/** Reduz `html` ao que o editor escreve. Sem DOM, devolve o texto escapado. */
|
|
175
|
+
export const sanitizarHtml = (html) => {
|
|
176
|
+
const bruto = html || "";
|
|
177
|
+
if (typeof document === "undefined")
|
|
178
|
+
return escapar(bruto);
|
|
179
|
+
const molde = document.createElement("template");
|
|
180
|
+
molde.innerHTML = bruto;
|
|
181
|
+
const saida = document.createElement("div");
|
|
182
|
+
limparNo(molde.content, saida, document);
|
|
183
|
+
return saida.innerHTML;
|
|
184
|
+
};
|
|
185
|
+
/** A tabela vazia que o editor insere: `linhas` × `colunas` células com uma quebra, para o cursor entrar. */
|
|
186
|
+
export const htmlDaTabela = (linhas, colunas) => {
|
|
187
|
+
let celulas = "";
|
|
188
|
+
for (let c = 0; c < colunas; c++)
|
|
189
|
+
celulas += "<td><br></td>";
|
|
190
|
+
let corpo = "";
|
|
191
|
+
for (let l = 0; l < linhas; l++)
|
|
192
|
+
corpo += "<tr>" + celulas + "</tr>";
|
|
193
|
+
return "<table><tbody>" + corpo + "</tbody></table><p><br></p>";
|
|
194
|
+
};
|
|
195
|
+
/** O `<img>` que o editor insere (endereço já seguro). */
|
|
196
|
+
export const htmlDaImagem = (endereco, alt) => '<img src="' + escapar(endereco) + '" alt="' + escapar(alt || "") + '">';
|
|
197
|
+
/** O editor está "vazio": sem texto, sem lista e sem quebra solta (o `<br>` que o navegador deixa). */
|
|
198
|
+
export const htmlVazio = (html) => {
|
|
199
|
+
if (/<(img|table)[\s>]/i.test(html || ""))
|
|
200
|
+
return false;
|
|
201
|
+
const texto = (html || "").replace(/<br\s*\/?>/gi, "").replace(/<\/?(div|p|span)[^>]*>/gi, "").trim();
|
|
202
|
+
return texto === "";
|
|
203
|
+
};
|
|
204
|
+
/** O `<a>` que o editor escreve quando o link não tem texto escolhido: o endereço (já seguro) é o href e o texto. */
|
|
205
|
+
export const htmlDoLink = (endereco) => {
|
|
206
|
+
const seguro = escapar(endereco);
|
|
207
|
+
return '<a href="' + seguro + '">' + seguro + "</a>";
|
|
208
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paleta de cor categórica do 1DS: os primitivos `--umds-primitive-<família>-<tom>`.
|
|
3
|
+
*
|
|
4
|
+
* É a lista que o `ColorPicker` mostra por padrão. Ela existe como constante
|
|
5
|
+
* exportada para o app mapear o valor guardado (o nome do token) de volta para
|
|
6
|
+
* um rótulo, ou montar a própria lista a partir dela (`COLOR_PALETTE.filter(...)`).
|
|
7
|
+
*
|
|
8
|
+
* Primitivo não muda com o tema: é o certo para cor de DADO (marcador, tipo de
|
|
9
|
+
* documento, setor), que precisa ser a mesma no claro e no escuro. Para cor com
|
|
10
|
+
* PAPEL (primário, erro, sucesso) use os semânticos `--umds-semantic-color-*`,
|
|
11
|
+
* que não entram aqui.
|
|
12
|
+
*/
|
|
13
|
+
export type PaletteColor = {
|
|
14
|
+
/** Nome da variável CSS, ex.: `--umds-primitive-blue-pure`. Use como `var(${value})`. */
|
|
15
|
+
value: string;
|
|
16
|
+
/** Nome em português, ex.: "Azul escuro". */
|
|
17
|
+
label: string;
|
|
18
|
+
/** Família: blue, red, green, orange, yellow, purple ou gray. */
|
|
19
|
+
family: string;
|
|
20
|
+
/** Tom claro o bastante para o ícone de marcação sair escuro (contraste mínimo de 3:1). */
|
|
21
|
+
onLight: boolean;
|
|
22
|
+
};
|
|
23
|
+
/** Os 31 primitivos de cor (6 famílias x 4 tons + 7 cinzas), do mais escuro ao mais claro. */
|
|
24
|
+
export declare const COLOR_PALETTE: PaletteColor[];
|
|
25
|
+
/** `#RRGGBB` completo (o `#` é opcional). Hexadecimal curto (`#FFF`) não vale. */
|
|
26
|
+
export declare const isHexColor: (texto: string) => boolean;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const cor = (family, tone, label, onLight) => ({
|
|
2
|
+
value: "--umds-primitive-" + family + "-" + tone,
|
|
3
|
+
label,
|
|
4
|
+
family,
|
|
5
|
+
onLight
|
|
6
|
+
});
|
|
7
|
+
/** Os 31 primitivos de cor (6 famílias x 4 tons + 7 cinzas), do mais escuro ao mais claro. */
|
|
8
|
+
export const COLOR_PALETTE = [cor("blue", "dark", "Azul escuro", false), cor("blue", "pure", "Azul", false), cor("blue", "medium", "Azul médio", false), cor("blue", "light", "Azul claro", true), cor("red", "dark", "Vermelho escuro", false), cor("red", "pure", "Vermelho", false), cor("red", "medium", "Vermelho médio", false), cor("red", "light", "Vermelho claro", true), cor("green", "dark", "Verde escuro", false), cor("green", "pure", "Verde", false), cor("green", "medium", "Verde médio", false), cor("green", "light", "Verde claro", true), cor("orange", "dark", "Laranja escuro", false), cor("orange", "pure", "Laranja", false), cor("orange", "medium", "Laranja médio", true), cor("orange", "light", "Laranja claro", true), cor("yellow", "dark", "Amarelo escuro", false), cor("yellow", "pure", "Amarelo", true), cor("yellow", "medium", "Amarelo médio", true), cor("yellow", "light", "Amarelo claro", true), cor("purple", "dark", "Roxo escuro", false), cor("purple", "pure", "Roxo", false), cor("purple", "medium", "Roxo médio", false), cor("purple", "light", "Roxo claro", true), cor("gray", "darkest", "Cinza muito escuro", false), cor("gray", "darker", "Cinza escuro", false), cor("gray", "dark", "Cinza", false), cor("gray", "medium", "Cinza médio", true), cor("gray", "light", "Cinza claro", true), cor("gray", "lighter", "Cinza mais claro", true), cor("gray", "lightest", "Cinza muito claro", true)];
|
|
9
|
+
/** `#RRGGBB` completo (o `#` é opcional). Hexadecimal curto (`#FFF`) não vale. */
|
|
10
|
+
export const isHexColor = (texto) => /^#?[0-9a-fA-F]{6}$/.test((texto || "").trim());
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Texto para comparar: minúsculo, sem acento e sem espaço nas pontas. É o que a
|
|
3
|
+
* busca do `IconPicker` usa para achar "usuario" em "Usuário".
|
|
4
|
+
*/
|
|
5
|
+
export const semAcento = (texto) => String(texto || "").normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().trim();
|
package/dist/utils/toast.d.ts
CHANGED
|
@@ -21,7 +21,13 @@ export interface ToastOptions {
|
|
|
21
21
|
description?: string;
|
|
22
22
|
/** Variante semântica @default 'info' */
|
|
23
23
|
variant?: ToastVariant;
|
|
24
|
-
/**
|
|
24
|
+
/** Linhas de detalhe abaixo da descrição */
|
|
25
|
+
details?: string[];
|
|
26
|
+
/** Rótulo do botão de ação ("Desfazer"); só aparece com `onAction` */
|
|
27
|
+
actionLabel?: string;
|
|
28
|
+
/** Chamado ao acionar o botão de ação; depois dele o toast fecha */
|
|
29
|
+
onAction?: (event?: any) => void;
|
|
30
|
+
/** Auto-dismiss em ms; 0 = não fecha sozinho @default 5000 (0 quando há ação: dá tempo de agir, WCAG 2.2.1) */
|
|
25
31
|
duration?: number;
|
|
26
32
|
/** Exibe o botão de fechar @default true */
|
|
27
33
|
closable?: boolean;
|
|
@@ -30,6 +36,9 @@ export interface ToastData {
|
|
|
30
36
|
id: number;
|
|
31
37
|
message: string;
|
|
32
38
|
description?: string;
|
|
39
|
+
details?: string[];
|
|
40
|
+
actionLabel?: string;
|
|
41
|
+
onAction?: (event?: any) => void;
|
|
33
42
|
variant: ToastVariant;
|
|
34
43
|
duration: number;
|
|
35
44
|
closable: boolean;
|
package/dist/utils/toast.js
CHANGED
|
@@ -26,7 +26,8 @@ export const toast = (options) => {
|
|
|
26
26
|
const id = sequence;
|
|
27
27
|
items = [...items, {
|
|
28
28
|
variant: "info",
|
|
29
|
-
|
|
29
|
+
// Toast com ação não some antes de dar tempo de agir.
|
|
30
|
+
duration: options.actionLabel && options.onAction ? 0 : 5000,
|
|
30
31
|
closable: true,
|
|
31
32
|
...options,
|
|
32
33
|
id
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1doc/1ds-react",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"sideEffects": [
|
|
6
6
|
"**/*.css"
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
"react-dom": "^18.0.0",
|
|
46
46
|
"typescript": "^5.0.0",
|
|
47
47
|
"vitest": "^3.2.7",
|
|
48
|
-
"@1doc/1ds-core": "2.
|
|
49
|
-
"@1doc/1ds-tokens": "0.
|
|
48
|
+
"@1doc/1ds-core": "2.10.0",
|
|
49
|
+
"@1doc/1ds-tokens": "0.5.0"
|
|
50
50
|
},
|
|
51
51
|
"publishConfig": {
|
|
52
52
|
"access": "public"
|