@1doc/1ds-react 2.10.0 → 2.11.1
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 +235 -0
- package/dist/components/Accordion/Accordion.d.ts +6 -0
- package/dist/components/Accordion/Accordion.js +17 -5
- package/dist/components/Badge/Badge.css +13 -4
- package/dist/components/Badge/Badge.d.ts +9 -2
- package/dist/components/Badge/Badge.js +3 -0
- package/dist/components/Button/Button.css +5 -0
- package/dist/components/Button/Button.d.ts +3 -0
- package/dist/components/Button/Button.js +1 -0
- package/dist/components/Calendar/Calendar.css +10 -0
- package/dist/components/Calendar/Calendar.d.ts +3 -0
- package/dist/components/Calendar/Calendar.js +6 -1
- package/dist/components/Coachmark/Coachmark.d.ts +2 -1
- package/dist/components/Coachmark/Coachmark.js +6 -1
- package/dist/components/CodeBlock/CodeBlock.css +3 -3
- package/dist/components/CodeBlock/CodeBlock.d.ts +2 -2
- package/dist/components/DataTable/DataTable.css +42 -0
- package/dist/components/DataTable/DataTable.d.ts +9 -2
- package/dist/components/DataTable/DataTable.js +15 -0
- package/dist/components/Input/Input.css +9 -0
- package/dist/components/Input/Input.d.ts +3 -0
- package/dist/components/Input/Input.js +1 -0
- package/dist/components/Kbd/Kbd.css +3 -3
- package/dist/components/Kbd/Kbd.d.ts +1 -1
- package/dist/components/Modal/Modal.css +19 -0
- package/dist/components/Modal/Modal.d.ts +7 -0
- package/dist/components/Modal/Modal.js +1 -0
- package/dist/components/RichTextEditor/RichTextEditor.d.ts +2 -2
- package/dist/components/RichTextEditor/RichTextEditor.js +0 -5
- package/dist/components/SegmentedControl/SegmentedControl.css +46 -0
- package/dist/components/SegmentedControl/SegmentedControl.d.ts +3 -0
- package/dist/components/SegmentedControl/SegmentedControl.js +1 -0
- package/dist/components/Select/Select.css +28 -2
- package/dist/components/Select/Select.d.ts +7 -0
- package/dist/components/Select/Select.js +55 -9
- package/dist/components/StatCard/StatCard.css +8 -0
- package/dist/components/StatCard/StatCard.d.ts +3 -0
- package/dist/components/StatCard/StatCard.js +1 -0
- package/dist/components/Tabs/Tabs.css +11 -0
- package/dist/components/Tabs/Tabs.d.ts +7 -0
- package/dist/components/Tabs/Tabs.js +7 -1
- package/dist/components/Tag/Tag.css +3 -1
- package/dist/teste/AccordionControlado.test.d.ts +1 -0
- package/dist/teste/AccordionControlado.test.js +73 -0
- package/dist/teste/BadgeAttachedTomEPadding.test.d.ts +1 -0
- package/dist/teste/BadgeAttachedTomEPadding.test.js +43 -0
- package/dist/teste/ButtonDensidade.test.d.ts +1 -0
- package/dist/teste/ButtonDensidade.test.js +28 -0
- package/dist/teste/CalendarCompacto.test.d.ts +1 -0
- package/dist/teste/CalendarCompacto.test.js +29 -0
- package/dist/teste/CoachmarkUmPasso.test.d.ts +1 -0
- package/dist/teste/CoachmarkUmPasso.test.js +34 -0
- package/dist/teste/DataTableCompactaETom.test.d.ts +1 -0
- package/dist/teste/DataTableCompactaETom.test.js +69 -0
- package/dist/teste/InputGhost.test.d.ts +1 -0
- package/dist/teste/InputGhost.test.js +32 -0
- package/dist/teste/ModalProporcoes1Doc.test.d.ts +1 -0
- package/dist/teste/ModalProporcoes1Doc.test.js +32 -0
- package/dist/teste/RichTextEditorEstiloDeTexto.test.js +7 -7
- package/dist/teste/SegmentedControlSuperficie.test.d.ts +1 -0
- package/dist/teste/SegmentedControlSuperficie.test.js +39 -0
- package/dist/teste/SelectItemComEscopo.test.d.ts +1 -0
- package/dist/teste/SelectItemComEscopo.test.js +54 -0
- package/dist/teste/SelectListaFiltro.test.d.ts +1 -0
- package/dist/teste/SelectListaFiltro.test.js +102 -0
- package/dist/teste/StatCardTamanhoSm.test.d.ts +1 -0
- package/dist/teste/StatCardTamanhoSm.test.js +32 -0
- package/dist/teste/TabsCompacta.test.d.ts +1 -0
- package/dist/teste/TabsCompacta.test.js +35 -0
- package/dist/teste/TagPositivoTingido.test.d.ts +1 -0
- package/dist/teste/TagPositivoTingido.test.js +54 -0
- package/dist/teste/lerArquivo.d.ts +1 -0
- package/dist/teste/lerArquivo.js +12 -0
- package/dist/utils/html-seguro.js +1 -1
- package/package.json +3 -3
- package/src/teste/AccordionControlado.test.tsx +87 -0
- package/src/teste/BadgeAttachedTomEPadding.test.tsx +60 -0
- package/src/teste/ButtonDensidade.test.tsx +40 -0
- package/src/teste/CalendarCompacto.test.tsx +37 -0
- package/src/teste/CoachmarkUmPasso.test.tsx +45 -0
- package/src/teste/DataTableCompactaETom.test.tsx +85 -0
- package/src/teste/InputGhost.test.tsx +42 -0
- package/src/teste/ModalProporcoes1Doc.test.tsx +36 -0
- package/src/teste/RichTextEditorEstiloDeTexto.test.tsx +8 -8
- package/src/teste/SegmentedControlSuperficie.test.tsx +52 -0
- package/src/teste/SelectItemComEscopo.test.tsx +69 -0
- package/src/teste/SelectListaFiltro.test.tsx +120 -0
- package/src/teste/StatCardTamanhoSm.test.tsx +42 -0
- package/src/teste/TabsCompacta.test.tsx +45 -0
- package/src/teste/TagPositivoTingido.test.tsx +60 -0
- package/src/teste/lerArquivo.ts +13 -0
- package/src/utils/html-seguro.ts +1 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
4
|
+
import Select from "../components/Select/Select";
|
|
5
|
+
import { lerArquivo } from "./lerArquivo";
|
|
6
|
+
const cssSelect = await lerArquivo("../core/src/components/Select/Select.css");
|
|
7
|
+
const OPCOES = [
|
|
8
|
+
{ label: "Urgente", value: "urgente" },
|
|
9
|
+
{ label: "Revisão", value: "revisao" },
|
|
10
|
+
];
|
|
11
|
+
afterEach(() => vi.restoreAllMocks());
|
|
12
|
+
const lista = (container) => container.querySelector(".umds-select__dropdown");
|
|
13
|
+
/** jsdom não tem layout: só a lista "mede" o que o teste mandar. */
|
|
14
|
+
function medirLista(direita, larguraDaJanela) {
|
|
15
|
+
vi.spyOn(HTMLElement.prototype, "getBoundingClientRect").mockImplementation(function () {
|
|
16
|
+
const eLista = this.classList.contains("umds-select__dropdown");
|
|
17
|
+
return { top: 0, bottom: 0, left: 0, right: eLista ? direita : 0, width: 0, height: 0, x: 0, y: 0, toJSON() { } };
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(window, "innerWidth", { value: larguraDaJanela, configurable: true });
|
|
20
|
+
}
|
|
21
|
+
describe("Select filtro · listMinWidth", () => {
|
|
22
|
+
it("um número vira px e a lista nunca fica mais estreita que o gatilho", () => {
|
|
23
|
+
const { container } = render(_jsx(Select, { ariaLabel: "x", options: OPCOES, variant: "filter", listMinWidth: 220 }));
|
|
24
|
+
fireEvent.click(screen.getByRole("combobox"));
|
|
25
|
+
expect(lista(container).style.minWidth).toBe("max(100%, 220px)");
|
|
26
|
+
});
|
|
27
|
+
it("uma medida CSS vale como veio", () => {
|
|
28
|
+
const { container } = render(_jsx(Select, { ariaLabel: "x", options: OPCOES, variant: "filter", listMinWidth: "18rem" }));
|
|
29
|
+
fireEvent.click(screen.getByRole("combobox"));
|
|
30
|
+
expect(lista(container).style.minWidth).toBe("max(100%, 18rem)");
|
|
31
|
+
});
|
|
32
|
+
it("0 é um valor (sem mínimo além do gatilho), não ausência", () => {
|
|
33
|
+
const { container } = render(_jsx(Select, { ariaLabel: "x", options: OPCOES, variant: "filter", listMinWidth: 0 }));
|
|
34
|
+
fireEvent.click(screen.getByRole("combobox"));
|
|
35
|
+
expect(lista(container).style.minWidth).toBe("max(100%, 0px)");
|
|
36
|
+
});
|
|
37
|
+
it("sem a prop o mínimo é o do CSS (240px), sem estilo inline", () => {
|
|
38
|
+
const { container } = render(_jsx(Select, { ariaLabel: "x", options: OPCOES, variant: "filter" }));
|
|
39
|
+
fireEvent.click(screen.getByRole("combobox"));
|
|
40
|
+
expect(lista(container).style.minWidth).toBe("");
|
|
41
|
+
});
|
|
42
|
+
it("o Select padrão ignora a prop (a lista é a largura do campo)", () => {
|
|
43
|
+
const { container } = render(_jsx(Select, { ariaLabel: "x", options: OPCOES, listMinWidth: 220 }));
|
|
44
|
+
fireEvent.click(screen.getByRole("combobox"));
|
|
45
|
+
expect(lista(container).style.minWidth).toBe("");
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe("Select filtro · nunca passa da borda da janela", () => {
|
|
49
|
+
it("a lista que estoura a borda direita se alinha ao fim do gatilho", async () => {
|
|
50
|
+
medirLista(1457, 1440);
|
|
51
|
+
const { container } = render(_jsx(Select, { ariaLabel: "x", options: OPCOES, variant: "filter" }));
|
|
52
|
+
fireEvent.click(screen.getByRole("combobox"));
|
|
53
|
+
await waitFor(() => expect(container.querySelector(".umds-select--list-end")).not.toBeNull());
|
|
54
|
+
});
|
|
55
|
+
it("a lista que cabe fica onde está", async () => {
|
|
56
|
+
medirLista(900, 1440);
|
|
57
|
+
const { container } = render(_jsx(Select, { ariaLabel: "x", options: OPCOES, variant: "filter" }));
|
|
58
|
+
fireEvent.click(screen.getByRole("combobox"));
|
|
59
|
+
await waitFor(() => expect(lista(container)).not.toBeNull());
|
|
60
|
+
expect(container.querySelector(".umds-select--list-end")).toBeNull();
|
|
61
|
+
});
|
|
62
|
+
it("deixa 8px de respiro: encostada na borda ainda alinha", async () => {
|
|
63
|
+
medirLista(1436, 1440);
|
|
64
|
+
const { container } = render(_jsx(Select, { ariaLabel: "x", options: OPCOES, variant: "filter" }));
|
|
65
|
+
fireEvent.click(screen.getByRole("combobox"));
|
|
66
|
+
await waitFor(() => expect(container.querySelector(".umds-select--list-end")).not.toBeNull());
|
|
67
|
+
});
|
|
68
|
+
it("fechar desliga o alinhamento", async () => {
|
|
69
|
+
medirLista(1457, 1440);
|
|
70
|
+
const { container } = render(_jsx(Select, { ariaLabel: "x", options: OPCOES, variant: "filter" }));
|
|
71
|
+
fireEvent.click(screen.getByRole("combobox"));
|
|
72
|
+
await waitFor(() => expect(container.querySelector(".umds-select--list-end")).not.toBeNull());
|
|
73
|
+
fireEvent.click(screen.getByRole("combobox"));
|
|
74
|
+
await waitFor(() => expect(container.querySelector(".umds-select--list-end")).toBeNull());
|
|
75
|
+
});
|
|
76
|
+
it("o Select padrão (não filtro) não é movido", async () => {
|
|
77
|
+
medirLista(1457, 1440);
|
|
78
|
+
const { container } = render(_jsx(Select, { ariaLabel: "x", options: OPCOES }));
|
|
79
|
+
fireEvent.click(screen.getByRole("combobox"));
|
|
80
|
+
await waitFor(() => expect(lista(container)).not.toBeNull());
|
|
81
|
+
expect(container.querySelector(".umds-select--list-end")).toBeNull();
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
describe("Select filtro · CSS", () => {
|
|
85
|
+
const css = cssSelect.replace(/\r\n/g, "\n");
|
|
86
|
+
const bloco = (seletor) => {
|
|
87
|
+
const inicio = css.indexOf(seletor + " {");
|
|
88
|
+
return css.slice(inicio, css.indexOf("}", inicio));
|
|
89
|
+
};
|
|
90
|
+
it("o wrapper do filtro é fit-content (o auto o esticava até o pai e a lista herdava essa largura)", () => {
|
|
91
|
+
expect(bloco(".umds-select--filter")).toMatch(/width:\s*fit-content/);
|
|
92
|
+
});
|
|
93
|
+
it("a busca da lista fica fora da conta da largura natural", () => {
|
|
94
|
+
expect(bloco(".umds-select--filter .umds-select__search-container")).toMatch(/contain:\s*inline-size/);
|
|
95
|
+
expect(bloco(".umds-select__search-input")).toMatch(/min-width:\s*0/);
|
|
96
|
+
});
|
|
97
|
+
it("o alinhamento ao fim ancora a lista pela direita", () => {
|
|
98
|
+
const b = bloco(".umds-select--filter.umds-select--list-end .umds-select__dropdown");
|
|
99
|
+
expect(b).toMatch(/left:\s*auto/);
|
|
100
|
+
expect(b).toMatch(/right:\s*0/);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { render } from "@testing-library/react";
|
|
4
|
+
import StatCard from "../components/StatCard/StatCard";
|
|
5
|
+
import { lerArquivo } from "./lerArquivo";
|
|
6
|
+
const cssStatCard = await lerArquivo("../core/src/components/StatCard/StatCard.css");
|
|
7
|
+
const css = cssStatCard.replace(/\r\n/g, "\n");
|
|
8
|
+
const bloco = (seletor) => {
|
|
9
|
+
const inicio = css.indexOf(seletor + " {");
|
|
10
|
+
return css.slice(inicio, css.indexOf("}", inicio));
|
|
11
|
+
};
|
|
12
|
+
describe("StatCard · size", () => {
|
|
13
|
+
it("sm escreve a classe; sem a prop e com md o cartão é o de antes", () => {
|
|
14
|
+
const { container } = render(_jsxs(_Fragment, { children: [_jsx(StatCard, { value: 44, label: "N\u00E3o lidos", size: "sm" }), _jsx(StatCard, { value: 44, label: "N\u00E3o lidos" }), _jsx(StatCard, { value: 44, label: "N\u00E3o lidos", size: "md" })] }));
|
|
15
|
+
expect(container.children[0]).toHaveClass("umds-stat-card--sm");
|
|
16
|
+
expect(container.children[1]).not.toHaveClass("umds-stat-card--sm");
|
|
17
|
+
expect(container.children[2]).not.toHaveClass("umds-stat-card--sm");
|
|
18
|
+
});
|
|
19
|
+
it("o número do sm é 18px/800, e a regra vence o 32/800 do rótulo no topo", () => {
|
|
20
|
+
const regra = bloco(".umds-stat-card.umds-stat-card--sm .umds-stat-card__valor");
|
|
21
|
+
expect(regra).toMatch(/font-size:\s*1\.125rem/);
|
|
22
|
+
expect(regra).toMatch(/font-weight:\s*var\(--umds-primitive-font-weight-extrabold\)/);
|
|
23
|
+
const { container } = render(_jsx(StatCard, { value: 44, label: "N\u00E3o lidos", size: "sm", labelPosition: "top" }));
|
|
24
|
+
expect(container.firstElementChild).toHaveClass("umds-stat-card--sm", "umds-stat-card--rotulo-topo");
|
|
25
|
+
});
|
|
26
|
+
it("o número continua antes do rótulo no HTML", () => {
|
|
27
|
+
const { container } = render(_jsx(StatCard, { value: 44, label: "N\u00E3o lidos", size: "sm" }));
|
|
28
|
+
const valor = container.querySelector(".umds-stat-card__valor");
|
|
29
|
+
const rotulo = container.querySelector(".umds-stat-card__rotulo");
|
|
30
|
+
expect(valor.compareDocumentPosition(rotulo) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import Tabs from "../components/Tabs/Tabs";
|
|
4
|
+
import Tab from "../components/Tabs/Tab";
|
|
5
|
+
import { render } from "@testing-library/react";
|
|
6
|
+
import { lerArquivo } from "./lerArquivo";
|
|
7
|
+
const cssTabs = await lerArquivo("../core/src/components/Tabs/Tabs.css");
|
|
8
|
+
const CSS = { Tabs: cssTabs };
|
|
9
|
+
const css = (componente) => CSS[componente].replace(/\r\n/g, "\n");
|
|
10
|
+
const bloco = (texto, seletor) => {
|
|
11
|
+
const inicio = texto.indexOf(seletor + " {");
|
|
12
|
+
expect(inicio, seletor).toBeGreaterThanOrEqual(0);
|
|
13
|
+
return texto.slice(inicio, texto.indexOf("}", inicio));
|
|
14
|
+
};
|
|
15
|
+
describe("Tabs · density", () => {
|
|
16
|
+
it("compact escreve a classe na raiz; sem a prop e com default, não", () => {
|
|
17
|
+
const { container } = render(_jsxs(_Fragment, { children: [_jsx(Tabs, { value: "0", density: "compact", children: _jsx(Tab, { label: "A", index: "0" }) }), _jsx(Tabs, { value: "0", children: _jsx(Tab, { label: "B", index: "0" }) }), _jsx(Tabs, { value: "0", density: "default", children: _jsx(Tab, { label: "C", index: "0" }) })] }));
|
|
18
|
+
expect(container.children[0]).toHaveClass("umds-tabs--compact");
|
|
19
|
+
expect(container.children[1]).not.toHaveClass("umds-tabs--compact");
|
|
20
|
+
expect(container.children[2]).not.toHaveClass("umds-tabs--compact");
|
|
21
|
+
});
|
|
22
|
+
it("CSS: a compacta tem altura mínima de 40px (degrau sm) e padding vertical de 8px; o padrão segue em 48px", () => {
|
|
23
|
+
const t = css("Tabs");
|
|
24
|
+
const compacta = bloco(t, ".umds-tabs--compact .umds-tabs__tab");
|
|
25
|
+
expect(compacta).toMatch(/min-height:\s*var\(--umds-primitive-spacing-sm\)/);
|
|
26
|
+
expect(compacta).toMatch(/padding-block:\s*var\(--umds-primitive-spacing-nano\)/);
|
|
27
|
+
expect(bloco(t, ".umds-tabs__tab")).toMatch(/min-height:\s*48px/);
|
|
28
|
+
});
|
|
29
|
+
it("a pílula do contador é cinza na aba inativa e azul só na ativa (nas duas densidades)", () => {
|
|
30
|
+
const t = css("Tabs");
|
|
31
|
+
expect(bloco(t, ".umds-tabs__count")).toMatch(/background-color:\s*var\(--umds-semantic-color-neutral-light-medium\)/);
|
|
32
|
+
expect(bloco(t, ".umds-tabs__tab--selected .umds-tabs__count")).toMatch(/background-color:\s*var\(--umds-semantic-color-primary-pure\)/);
|
|
33
|
+
expect(t).not.toMatch(/--compact[^{]*\.umds-tabs__count/);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
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 Tag from "../components/Tag/Tag";
|
|
5
|
+
import { lerArquivo } from "./lerArquivo";
|
|
6
|
+
const cssTag = await lerArquivo("../core/src/components/Tag/Tag.css");
|
|
7
|
+
const tema1doc = await lerArquivo("../tokens/dist/css/theme-1doc.css");
|
|
8
|
+
const temaEscuro = await lerArquivo("../tokens/dist/css/theme-1doc-dark.css");
|
|
9
|
+
const temaAltoContraste = await lerArquivo("../tokens/dist/css/theme-1doc-high-contrast.css");
|
|
10
|
+
const css = cssTag.replace(/\r\n/g, "\n");
|
|
11
|
+
const bloco = (seletor) => {
|
|
12
|
+
const inicio = css.indexOf(seletor + " {");
|
|
13
|
+
return css.slice(inicio, css.indexOf("}", inicio));
|
|
14
|
+
};
|
|
15
|
+
const TEMAS = { "theme-1doc.css": tema1doc, "theme-1doc-dark.css": temaEscuro, "theme-1doc-high-contrast.css": temaAltoContraste };
|
|
16
|
+
const tema = (arquivo) => {
|
|
17
|
+
const texto = TEMAS[arquivo];
|
|
18
|
+
const cor = (nome) => texto.match(new RegExp("--umds-theme-" + nome + ":\\s*(#[0-9A-Fa-f]{6})"))[1];
|
|
19
|
+
return cor;
|
|
20
|
+
};
|
|
21
|
+
const luz = (hex) => {
|
|
22
|
+
const c = [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255).map((v) => (v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4));
|
|
23
|
+
return 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2];
|
|
24
|
+
};
|
|
25
|
+
const contraste = (a, b) => {
|
|
26
|
+
const [x, y] = [luz(a), luz(b)].sort((m, n) => n - m);
|
|
27
|
+
return (x + 0.05) / (y + 0.05);
|
|
28
|
+
};
|
|
29
|
+
describe("Tag positive outlined sobre fundo tingido", () => {
|
|
30
|
+
it("escreve as classes que a regra de CSS escolhe", () => {
|
|
31
|
+
const { container } = render(_jsx(Tag, { label: "Entregue", variant: "positive", appearance: "outlined" }));
|
|
32
|
+
const tag = container.firstElementChild;
|
|
33
|
+
expect(tag).toHaveClass("umds-tag--outlined", "umds-tag--positive");
|
|
34
|
+
});
|
|
35
|
+
it("o texto é positive-dark e o contorno segue positive-pure", () => {
|
|
36
|
+
const regra = bloco(".umds-tag--outlined.umds-tag--positive");
|
|
37
|
+
expect(regra).toMatch(/color:\s*var\(--umds-semantic-color-feedback-positive-dark\)/);
|
|
38
|
+
expect(regra).toMatch(/border-color:\s*var\(--umds-semantic-color-feedback-positive-pure\)/);
|
|
39
|
+
});
|
|
40
|
+
it("no tema 1doc, sobre o azul-claro do card o texto passa de 4,5:1 (o pure dava 3,6:1)", () => {
|
|
41
|
+
const cor = tema("theme-1doc.css");
|
|
42
|
+
const fundo = cor("primary-light");
|
|
43
|
+
expect(contraste(cor("feedback-positive-pure"), fundo)).toBeLessThan(4.5);
|
|
44
|
+
expect(contraste(cor("feedback-positive-dark"), fundo)).toBeGreaterThanOrEqual(4.5);
|
|
45
|
+
});
|
|
46
|
+
it("nos temas escuro e de alto contraste o texto também passa de 4,5:1 sobre o fundo tingido e sobre a página", () => {
|
|
47
|
+
for (const arquivo of ["theme-1doc-dark.css", "theme-1doc-high-contrast.css"]) {
|
|
48
|
+
const cor = tema(arquivo);
|
|
49
|
+
for (const fundo of [cor("primary-light"), cor("background-white")]) {
|
|
50
|
+
expect(contraste(cor("feedback-positive-dark"), fundo)).toBeGreaterThanOrEqual(4.5);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function lerArquivo(relativo: string): Promise<string>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lê um arquivo do pacote (relativo à pasta `packages/react`) para os testes que conferem uma regra de CSS na
|
|
3
|
+
* fonte: o jsdom não tem cascata de arquivo, e o `css: false` do vitest esvazia o `?raw` do CSS.
|
|
4
|
+
* O `node:fs` entra por import dinâmico com nome calculado porque o `tsc` do pacote não traz os tipos do Node.
|
|
5
|
+
*/
|
|
6
|
+
const modulo = (nome) => import(/* @vite-ignore */ nome);
|
|
7
|
+
export async function lerArquivo(relativo) {
|
|
8
|
+
const fs = await modulo("node:fs");
|
|
9
|
+
const path = await modulo("node:path");
|
|
10
|
+
const raiz = globalThis.process.cwd();
|
|
11
|
+
return fs.readFileSync(path.resolve(raiz, relativo), "utf8");
|
|
12
|
+
}
|
|
@@ -53,7 +53,7 @@ export const normalizarEndereco = (bruto) => {
|
|
|
53
53
|
/** Cor, tamanho e família de fonte só valem como TOKEN do DS: `var(--umds-...)`. Cor em hex, rgb ou nome sai. */
|
|
54
54
|
const COR_TOKEN = /^var\(--umds-(?:primitive|semantic)-[a-z0-9-]+\)$/;
|
|
55
55
|
const TAMANHO_TOKEN = /^var\(--umds-primitive-font-size-[a-z0-9]+\)$/;
|
|
56
|
-
const FAMILIA_TOKEN = /^var\(--umds-(?:primitive|semantic)-font-family-
|
|
56
|
+
const FAMILIA_TOKEN = /^var\(--umds-(?:primitive|semantic)-font-family-base\)$/;
|
|
57
57
|
/**
|
|
58
58
|
* Declaração de estilo de texto que o editor aceita: `color`, `font-size` e `font-family`,
|
|
59
59
|
* cada uma só com um token do DS. Devolve a declaração limpa ("color: var(--x);") ou "".
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1doc/1ds-react",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.1",
|
|
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.11.1",
|
|
49
|
+
"@1doc/1ds-tokens": "0.7.0"
|
|
50
50
|
},
|
|
51
51
|
"publishConfig": {
|
|
52
52
|
"access": "public"
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
4
|
+
import Accordion from "../components/Accordion/Accordion";
|
|
5
|
+
|
|
6
|
+
const ITENS = [
|
|
7
|
+
{ id: "prazos", title: "Prazos", content: "Dez dias úteis.", defaultOpen: true },
|
|
8
|
+
{ id: "anexos", title: "Anexos", content: "Até 10 MB." },
|
|
9
|
+
{ id: "sigilo", title: "Sigilo", content: "Só o setor." },
|
|
10
|
+
];
|
|
11
|
+
const aberto = (nome: string) => screen.getByRole("button", { name: nome }).getAttribute("aria-expanded") === "true";
|
|
12
|
+
|
|
13
|
+
describe("Accordion · não controlado (como antes)", () => {
|
|
14
|
+
it("começa pelos defaultOpen e o clique abre e fecha; onOpenChange avisa a nova lista", () => {
|
|
15
|
+
const aoMudar = vi.fn();
|
|
16
|
+
render(<Accordion items={ITENS} multiple onOpenChange={aoMudar} />);
|
|
17
|
+
expect(aberto("Prazos")).toBe(true);
|
|
18
|
+
fireEvent.click(screen.getByRole("button", { name: "Anexos" }));
|
|
19
|
+
expect(aberto("Anexos")).toBe(true);
|
|
20
|
+
expect(aoMudar).toHaveBeenLastCalledWith(["prazos", "anexos"]);
|
|
21
|
+
fireEvent.click(screen.getByRole("button", { name: "Prazos" }));
|
|
22
|
+
expect(aberto("Prazos")).toBe(false);
|
|
23
|
+
expect(aoMudar).toHaveBeenLastCalledWith(["anexos"]);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("sem multiple, abrir um fecha o outro", () => {
|
|
27
|
+
render(<Accordion items={ITENS} />);
|
|
28
|
+
fireEvent.click(screen.getByRole("button", { name: "Anexos" }));
|
|
29
|
+
expect(aberto("Anexos")).toBe(true);
|
|
30
|
+
expect(aberto("Prazos")).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe("Accordion · controlado (open e onOpenChange)", () => {
|
|
35
|
+
it("open manda: o que está em open está aberto, ignorando defaultOpen", () => {
|
|
36
|
+
render(<Accordion items={ITENS} open={["sigilo"]} />);
|
|
37
|
+
expect(aberto("Sigilo")).toBe(true);
|
|
38
|
+
expect(aberto("Prazos")).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("o clique só pede (onOpenChange); sem o app mudar open, nada abre", () => {
|
|
42
|
+
const aoMudar = vi.fn();
|
|
43
|
+
render(<Accordion items={ITENS} multiple open={["prazos"]} onOpenChange={aoMudar} />);
|
|
44
|
+
fireEvent.click(screen.getByRole("button", { name: "Anexos" }));
|
|
45
|
+
expect(aoMudar).toHaveBeenCalledWith(["prazos", "anexos"]);
|
|
46
|
+
expect(aberto("Anexos")).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("o app guarda a escolha: com o estado dele, abre, fecha e a lista chega em ordem", () => {
|
|
50
|
+
let ultima: string[] = [];
|
|
51
|
+
function App() {
|
|
52
|
+
const [abertos, setAbertos] = useState<string[]>([]);
|
|
53
|
+
return (
|
|
54
|
+
<Accordion
|
|
55
|
+
items={ITENS}
|
|
56
|
+
multiple
|
|
57
|
+
open={abertos}
|
|
58
|
+
onOpenChange={(ids: string[]) => {
|
|
59
|
+
ultima = ids;
|
|
60
|
+
setAbertos(ids);
|
|
61
|
+
}}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
render(<App />);
|
|
66
|
+
expect(aberto("Prazos")).toBe(false);
|
|
67
|
+
fireEvent.click(screen.getByRole("button", { name: "Sigilo" }));
|
|
68
|
+
fireEvent.click(screen.getByRole("button", { name: "Anexos" }));
|
|
69
|
+
expect(ultima).toEqual(["sigilo", "anexos"]);
|
|
70
|
+
expect(aberto("Sigilo") && aberto("Anexos")).toBe(true);
|
|
71
|
+
fireEvent.click(screen.getByRole("button", { name: "Sigilo" }));
|
|
72
|
+
expect(ultima).toEqual(["anexos"]);
|
|
73
|
+
expect(aberto("Sigilo")).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("open vazio é controlado (tudo fechado), não ausência", () => {
|
|
77
|
+
render(<Accordion items={ITENS} open={[]} />);
|
|
78
|
+
expect(aberto("Prazos")).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("item desabilitado não pede nada", () => {
|
|
82
|
+
const aoMudar = vi.fn();
|
|
83
|
+
render(<Accordion items={[{ id: "x", title: "Bloqueado", content: "...", disabled: true }]} open={[]} onOpenChange={aoMudar} />);
|
|
84
|
+
fireEvent.click(screen.getByRole("button", { name: "Bloqueado" }));
|
|
85
|
+
expect(aoMudar).not.toHaveBeenCalled();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { render } from "@testing-library/react";
|
|
3
|
+
import Badge from "../components/Badge/Badge";
|
|
4
|
+
import { lerArquivo } from "./lerArquivo";
|
|
5
|
+
|
|
6
|
+
const cssBadge = await lerArquivo("../core/src/components/Badge/Badge.css");
|
|
7
|
+
|
|
8
|
+
const css = cssBadge.replace(/\r\n/g, "\n");
|
|
9
|
+
const bloco = (seletor: string) => {
|
|
10
|
+
const inicio = css.indexOf(seletor + " {");
|
|
11
|
+
return css.slice(inicio, css.indexOf("}", inicio));
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
describe("Badge 2xs · padding lateral de 8px", () => {
|
|
15
|
+
it("a regra do 2xs usa o degrau nano (8px) dos lados e o micro (2px) em cima e embaixo", () => {
|
|
16
|
+
expect(bloco(".umds-badge--2xs")).toMatch(
|
|
17
|
+
/padding:\s*var\(--umds-primitive-spacing-micro\)\s+var\(--umds-primitive-spacing-nano\)/,
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("os outros tamanhos não mudam de padding", () => {
|
|
22
|
+
expect(bloco(".umds-badge--xs")).toMatch(/padding:\s*var\(--umds-primitive-spacing-micro\)\s+var\(--umds-primitive-spacing-quarck\)/);
|
|
23
|
+
expect(bloco(".umds-badge--md")).toMatch(/padding:\s*var\(--umds-primitive-spacing-quarck\)\s+var\(--umds-primitive-spacing-nano\)/);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe("Badge attached · 22px e tom escuro", () => {
|
|
28
|
+
it("o rótulo colado tem altura mínima de 22px (24 - 2), de qualquer tamanho", () => {
|
|
29
|
+
expect(bloco(".umds-badge--attached")).toMatch(
|
|
30
|
+
/min-height:\s*calc\(var\(--umds-primitive-spacing-xxs\)\s*-\s*var\(--umds-primitive-spacing-micro\)\)/,
|
|
31
|
+
);
|
|
32
|
+
const { container } = render(<Badge attached size="xs" variant="neutral" appearance="solid">Tarefa manual</Badge>);
|
|
33
|
+
expect(container.firstElementChild).toHaveClass("umds-badge--attached", "umds-badge--xs");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("attachedTone dark escreve a classe; sem ela e com light, não", () => {
|
|
37
|
+
const { container } = render(
|
|
38
|
+
<>
|
|
39
|
+
<Badge attached attachedTone="dark" variant="neutral" appearance="solid">A</Badge>
|
|
40
|
+
<Badge attached variant="neutral" appearance="solid">B</Badge>
|
|
41
|
+
<Badge attached attachedTone="light" variant="neutral" appearance="solid">C</Badge>
|
|
42
|
+
</>,
|
|
43
|
+
);
|
|
44
|
+
expect(container.children[0]).toHaveClass("umds-badge--attached-dark");
|
|
45
|
+
expect(container.children[1]).not.toHaveClass("umds-badge--attached-dark");
|
|
46
|
+
expect(container.children[2]).not.toHaveClass("umds-badge--attached-dark");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("sem attached o tom é ignorado", () => {
|
|
50
|
+
const { container } = render(<Badge attachedTone="dark" variant="neutral" appearance="solid">A</Badge>);
|
|
51
|
+
expect(container.firstElementChild).not.toHaveClass("umds-badge--attached-dark");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("CSS: o escuro é neutral-dark-down com texto branco, só no neutro sólido colado", () => {
|
|
55
|
+
const regra = bloco(".umds-badge--attached.umds-badge--neutral.umds-badge--solid.umds-badge--attached-dark");
|
|
56
|
+
expect(regra).toMatch(/background-color:\s*var\(--umds-semantic-color-neutral-dark-down\)/);
|
|
57
|
+
expect(regra).toMatch(/color:\s*var\(--umds-semantic-color-neutral-light-pure\)/);
|
|
58
|
+
expect(bloco(".umds-badge--attached.umds-badge--neutral.umds-badge--solid")).toMatch(/neutral-light-medium/);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import Button from "../components/Button/Button";
|
|
4
|
+
import { lerArquivo } from "./lerArquivo";
|
|
5
|
+
|
|
6
|
+
const cssButton = await lerArquivo("../core/src/components/Button/Button.css");
|
|
7
|
+
|
|
8
|
+
const css = cssButton.replace(/\r\n/g, "\n");
|
|
9
|
+
const bloco = (seletor: string) => {
|
|
10
|
+
const inicio = css.indexOf(seletor + " {");
|
|
11
|
+
return css.slice(inicio, css.indexOf("}", inicio));
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
describe("Button · density", () => {
|
|
15
|
+
it("comfortable escreve a classe; sem a prop e com default o botão é o de antes", () => {
|
|
16
|
+
render(
|
|
17
|
+
<>
|
|
18
|
+
<Button size="sm" density="comfortable">A</Button>
|
|
19
|
+
<Button size="sm">B</Button>
|
|
20
|
+
<Button size="sm" density="default">C</Button>
|
|
21
|
+
</>,
|
|
22
|
+
);
|
|
23
|
+
expect(screen.getByRole("button", { name: "A" })).toHaveClass("umds-button--sm", "umds-button--comfortable");
|
|
24
|
+
expect(screen.getByRole("button", { name: "B" })).not.toHaveClass("umds-button--comfortable");
|
|
25
|
+
expect(screen.getByRole("button", { name: "C" })).not.toHaveClass("umds-button--comfortable");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("CSS: o sm confortável tem 12px de padding lateral; o sm de antes segue em 8px; o só-ícone fica de fora", () => {
|
|
29
|
+
expect(bloco(".umds-button--sm.umds-button--comfortable:not(.umds-button--icon)")).toMatch(
|
|
30
|
+
/padding:\s*0\s+var\(--umds-primitive-spacing-mini\)/,
|
|
31
|
+
);
|
|
32
|
+
expect(bloco(".umds-button--sm")).toMatch(/padding:\s*0\s+var\(--umds-primitive-spacing-nano\)/);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("os outros tamanhos mantêm o padding (a densidade só mexe no sm)", () => {
|
|
36
|
+
expect(bloco(".umds-button--xs")).toMatch(/padding:\s*0\s+var\(--umds-primitive-spacing-mini\)/);
|
|
37
|
+
expect(bloco(".umds-button--md")).toMatch(/padding:\s*0\s+var\(--umds-primitive-spacing-xxxs\)/);
|
|
38
|
+
expect(bloco(".umds-button--lg")).toMatch(/padding:\s*0\s+var\(--umds-primitive-spacing-xxs\)/);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import Calendar from "../components/Calendar/Calendar";
|
|
3
|
+
import { render } from "@testing-library/react";
|
|
4
|
+
import { lerArquivo } from "./lerArquivo";
|
|
5
|
+
|
|
6
|
+
const cssCalendar = await lerArquivo("../core/src/components/Calendar/Calendar.css");
|
|
7
|
+
|
|
8
|
+
const CSS: Record<string, string> = { Calendar: cssCalendar };
|
|
9
|
+
const css = (componente: string) => CSS[componente].replace(/\r\n/g, "\n");
|
|
10
|
+
const bloco = (texto: string, seletor: string) => {
|
|
11
|
+
const inicio = texto.indexOf(seletor + " {");
|
|
12
|
+
expect(inicio, seletor).toBeGreaterThanOrEqual(0);
|
|
13
|
+
return texto.slice(inicio, texto.indexOf("}", inicio));
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
describe("Calendar · density compact", () => {
|
|
17
|
+
it("compact escreve a classe na raiz; sem a prop, não", () => {
|
|
18
|
+
const { container } = render(
|
|
19
|
+
<>
|
|
20
|
+
<Calendar today="2026-05-15" defaultDate="2026-05-15" density="compact" />
|
|
21
|
+
<Calendar today="2026-05-15" defaultDate="2026-05-15" />
|
|
22
|
+
</>,
|
|
23
|
+
);
|
|
24
|
+
expect(container.children[0]).toHaveClass("umds-calendar--compact", "umds-calendar--month");
|
|
25
|
+
expect(container.children[1]).not.toHaveClass("umds-calendar--compact");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("CSS: evento de 20px (16 + 4), 4px de padding lateral e linha de 16px; o título só é cortado quando passa da largura", () => {
|
|
29
|
+
const t = css("Calendar");
|
|
30
|
+
const compacto = bloco(t, ".umds-calendar--compact .umds-calendar__event");
|
|
31
|
+
expect(compacto).toMatch(/min-height:\s*calc\(var\(--umds-primitive-spacing-xxxs\)\s*\+\s*var\(--umds-primitive-spacing-quarck\)\)/);
|
|
32
|
+
expect(compacto).toMatch(/padding:\s*var\(--umds-primitive-spacing-micro\)\s+var\(--umds-primitive-spacing-quarck\)/);
|
|
33
|
+
expect(compacto).toMatch(/line-height:\s*var\(--umds-primitive-spacing-xxxs\)/);
|
|
34
|
+
const titulo = bloco(t, ".umds-calendar__event-title");
|
|
35
|
+
expect(titulo).toMatch(/text-overflow:\s*ellipsis/);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import Coachmark from "../components/Coachmark/Coachmark";
|
|
4
|
+
|
|
5
|
+
const UM = [{ target: "#alvo-a", title: "Novo: arrastar", content: "Arraste a linha para trocar a ordem." }];
|
|
6
|
+
const DOIS = [...UM, { title: "Pronto", content: "Sem alvo." }];
|
|
7
|
+
|
|
8
|
+
function Pagina({ passos, ...resto }: any) {
|
|
9
|
+
return (
|
|
10
|
+
<div>
|
|
11
|
+
<button id="alvo-a">A</button>
|
|
12
|
+
<Coachmark steps={passos} open {...resto} />
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("Coachmark · contador", () => {
|
|
18
|
+
it("com um passo só não há contador, nem visível nem na região aria-live", () => {
|
|
19
|
+
const { baseElement } = render(<Pagina passos={UM} />);
|
|
20
|
+
expect(screen.queryByText("1 de 1")).toBeNull();
|
|
21
|
+
expect(baseElement.textContent).not.toContain("1 de 1");
|
|
22
|
+
expect(baseElement.querySelector(".umds-coachmark__sr")?.textContent).toBe("Novo: arrastar");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("com um passo e modal={false} também não (a dica pontual)", () => {
|
|
26
|
+
const { baseElement } = render(<Pagina passos={UM} modal={false} />);
|
|
27
|
+
expect(baseElement.textContent).not.toContain("1 de 1");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("com dois passos o contador aparece e o leitor de tela o ouve", () => {
|
|
31
|
+
const { baseElement } = render(<Pagina passos={DOIS} />);
|
|
32
|
+
expect(screen.getByText("1 de 2")).toBeInTheDocument();
|
|
33
|
+
expect(baseElement.querySelector(".umds-coachmark__sr")?.textContent).toBe("1 de 2: Novo: arrastar");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("counterLabel segue valendo com mais de um passo", () => {
|
|
37
|
+
render(<Pagina passos={DOIS} counterLabel="Step {current} of {total}" />);
|
|
38
|
+
expect(screen.getByText("Step 1 of 2")).toBeInTheDocument();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("o botão do último passo continua sendo Concluir", () => {
|
|
42
|
+
render(<Pagina passos={UM} />);
|
|
43
|
+
expect(screen.getByRole("button", { name: "Concluir" })).toBeInTheDocument();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { render } from "@testing-library/react";
|
|
3
|
+
import DataTable from "../components/DataTable/DataTable";
|
|
4
|
+
import { lerArquivo } from "./lerArquivo";
|
|
5
|
+
|
|
6
|
+
const cssDataTable = await lerArquivo("../core/src/components/DataTable/DataTable.css");
|
|
7
|
+
|
|
8
|
+
const COLUNAS = [
|
|
9
|
+
{ key: "nome", label: "Nome" },
|
|
10
|
+
{ key: "situacao", label: "Situação" },
|
|
11
|
+
];
|
|
12
|
+
const DADOS = [
|
|
13
|
+
{ nome: "Ana Souza", situacao: "Ativa", tom: "positive" },
|
|
14
|
+
{ nome: "Carlos Lima", situacao: "Bloqueado", tom: "negative" },
|
|
15
|
+
{ nome: "Marina Alves", situacao: "Licença", tom: "info" },
|
|
16
|
+
{ nome: "Rafael Pereira", situacao: "Desligado", tom: "muted" },
|
|
17
|
+
{ nome: "Beatriz Lima", situacao: "Ativa" },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const linhas = (c: HTMLElement) => [...c.querySelectorAll("tbody tr")] as HTMLElement[];
|
|
21
|
+
|
|
22
|
+
describe("DataTable · rowTone", () => {
|
|
23
|
+
it("o nome de um campo da linha traz o tom", () => {
|
|
24
|
+
const { container } = render(<DataTable columns={COLUNAS} data={DADOS} rowTone="tom" />);
|
|
25
|
+
const l = linhas(container);
|
|
26
|
+
expect(l[0]).toHaveClass("umds-datatable__row--positive");
|
|
27
|
+
expect(l[1]).toHaveClass("umds-datatable__row--negative");
|
|
28
|
+
expect(l[2]).toHaveClass("umds-datatable__row--info");
|
|
29
|
+
expect(l[3]).toHaveClass("umds-datatable__row--muted");
|
|
30
|
+
expect(l[4].className).toBe("umds-datatable__row");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("uma função (row, rowIndex) devolve o tom", () => {
|
|
34
|
+
const { container } = render(
|
|
35
|
+
<DataTable columns={COLUNAS} data={DADOS} rowTone={(row, i) => (row.situacao === "Bloqueado" || i === 4 ? "negative" : undefined)} />,
|
|
36
|
+
);
|
|
37
|
+
const l = linhas(container);
|
|
38
|
+
expect(l[1]).toHaveClass("umds-datatable__row--negative");
|
|
39
|
+
expect(l[4]).toHaveClass("umds-datatable__row--negative");
|
|
40
|
+
expect(l[0].className).toBe("umds-datatable__row");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("tom desconhecido ou vazio não vira classe", () => {
|
|
44
|
+
const { container } = render(
|
|
45
|
+
<DataTable columns={COLUNAS} data={[{ nome: "A", situacao: "x", tom: "rosa" }, { nome: "B", situacao: "y", tom: "" }]} rowTone="tom" />,
|
|
46
|
+
);
|
|
47
|
+
for (const l of linhas(container)) expect(l.className).toBe("umds-datatable__row");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("sem rowTone nada muda", () => {
|
|
51
|
+
const { container } = render(<DataTable columns={COLUNAS} data={DADOS} />);
|
|
52
|
+
for (const l of linhas(container)) expect(l.className).toBe("umds-datatable__row");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("o tom convive com a linha selecionada e a zebra (as classes ficam; o CSS faz a selecionada vencer)", () => {
|
|
56
|
+
const { container } = render(
|
|
57
|
+
<DataTable columns={COLUNAS} data={DADOS} rowTone="tom" striped rowKey="nome" selectedRowKeys={["Carlos Lima"]} />,
|
|
58
|
+
);
|
|
59
|
+
const l = linhas(container);
|
|
60
|
+
expect(l[1]).toHaveClass("umds-datatable__row--negative", "umds-datatable__row--selected", "umds-datatable__row--striped");
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe("DataTable · compacta, 44px e CSS do tom", () => {
|
|
65
|
+
const css = cssDataTable.replace(/\r\n/g, "\n");
|
|
66
|
+
const bloco = (seletor: string) => {
|
|
67
|
+
const inicio = css.indexOf(seletor + " {");
|
|
68
|
+
return css.slice(inicio, css.indexOf("}", inicio));
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
it("a densidade compacta fixa cabeçalho e linha na altura de campo (44px); o documento fica de fora", () => {
|
|
72
|
+
expect(bloco(".umds-datatable--compact:not(.umds-datatable--document) .umds-datatable__td")).toMatch(
|
|
73
|
+
/height:\s*var\(--umds-semantic-field-height\)/,
|
|
74
|
+
);
|
|
75
|
+
expect(css).toContain(".umds-datatable--compact:not(.umds-datatable--document) .umds-datatable__th,");
|
|
76
|
+
const { container } = render(<DataTable density="compact" columns={COLUNAS} data={DADOS} />);
|
|
77
|
+
expect(container.querySelector(".umds-datatable--compact")).not.toBeNull();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("a linha selecionada vence o tom (o :not) e o tom vence a zebra", () => {
|
|
81
|
+
expect(css).toMatch(/\.umds-datatable__row--negative:not\(\.umds-datatable__row--selected\)/);
|
|
82
|
+
expect(css).toMatch(/\.umds-datatable__row--negative:not\(\.umds-datatable__row--selected\)\.umds-datatable__row--striped/);
|
|
83
|
+
expect(bloco(".umds-datatable__row--muted .umds-datatable__td")).toMatch(/neutral-dark-down/);
|
|
84
|
+
});
|
|
85
|
+
});
|