@1doc/1ds-react 2.10.0 → 2.11.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.
Files changed (82) hide show
  1. package/CHANGELOG.md +219 -0
  2. package/dist/components/Accordion/Accordion.d.ts +6 -0
  3. package/dist/components/Accordion/Accordion.js +17 -5
  4. package/dist/components/Badge/Badge.css +13 -4
  5. package/dist/components/Badge/Badge.d.ts +9 -2
  6. package/dist/components/Badge/Badge.js +3 -0
  7. package/dist/components/Button/Button.css +5 -0
  8. package/dist/components/Button/Button.d.ts +3 -0
  9. package/dist/components/Button/Button.js +1 -0
  10. package/dist/components/Calendar/Calendar.css +10 -0
  11. package/dist/components/Calendar/Calendar.d.ts +3 -0
  12. package/dist/components/Calendar/Calendar.js +6 -1
  13. package/dist/components/Coachmark/Coachmark.d.ts +2 -1
  14. package/dist/components/Coachmark/Coachmark.js +6 -1
  15. package/dist/components/DataTable/DataTable.css +42 -0
  16. package/dist/components/DataTable/DataTable.d.ts +9 -2
  17. package/dist/components/DataTable/DataTable.js +15 -0
  18. package/dist/components/Input/Input.css +9 -0
  19. package/dist/components/Input/Input.d.ts +3 -0
  20. package/dist/components/Input/Input.js +1 -0
  21. package/dist/components/Modal/Modal.css +19 -0
  22. package/dist/components/Modal/Modal.d.ts +7 -0
  23. package/dist/components/Modal/Modal.js +1 -0
  24. package/dist/components/SegmentedControl/SegmentedControl.css +46 -0
  25. package/dist/components/SegmentedControl/SegmentedControl.d.ts +3 -0
  26. package/dist/components/SegmentedControl/SegmentedControl.js +1 -0
  27. package/dist/components/Select/Select.css +28 -2
  28. package/dist/components/Select/Select.d.ts +7 -0
  29. package/dist/components/Select/Select.js +55 -9
  30. package/dist/components/StatCard/StatCard.css +8 -0
  31. package/dist/components/StatCard/StatCard.d.ts +3 -0
  32. package/dist/components/StatCard/StatCard.js +1 -0
  33. package/dist/components/Tabs/Tabs.css +11 -0
  34. package/dist/components/Tabs/Tabs.d.ts +7 -0
  35. package/dist/components/Tabs/Tabs.js +7 -1
  36. package/dist/components/Tag/Tag.css +3 -1
  37. package/dist/teste/AccordionControlado.test.d.ts +1 -0
  38. package/dist/teste/AccordionControlado.test.js +73 -0
  39. package/dist/teste/BadgeAttachedTomEPadding.test.d.ts +1 -0
  40. package/dist/teste/BadgeAttachedTomEPadding.test.js +43 -0
  41. package/dist/teste/ButtonDensidade.test.d.ts +1 -0
  42. package/dist/teste/ButtonDensidade.test.js +28 -0
  43. package/dist/teste/CalendarCompacto.test.d.ts +1 -0
  44. package/dist/teste/CalendarCompacto.test.js +29 -0
  45. package/dist/teste/CoachmarkUmPasso.test.d.ts +1 -0
  46. package/dist/teste/CoachmarkUmPasso.test.js +34 -0
  47. package/dist/teste/DataTableCompactaETom.test.d.ts +1 -0
  48. package/dist/teste/DataTableCompactaETom.test.js +69 -0
  49. package/dist/teste/InputGhost.test.d.ts +1 -0
  50. package/dist/teste/InputGhost.test.js +32 -0
  51. package/dist/teste/ModalProporcoes1Doc.test.d.ts +1 -0
  52. package/dist/teste/ModalProporcoes1Doc.test.js +32 -0
  53. package/dist/teste/SegmentedControlSuperficie.test.d.ts +1 -0
  54. package/dist/teste/SegmentedControlSuperficie.test.js +39 -0
  55. package/dist/teste/SelectItemComEscopo.test.d.ts +1 -0
  56. package/dist/teste/SelectItemComEscopo.test.js +54 -0
  57. package/dist/teste/SelectListaFiltro.test.d.ts +1 -0
  58. package/dist/teste/SelectListaFiltro.test.js +102 -0
  59. package/dist/teste/StatCardTamanhoSm.test.d.ts +1 -0
  60. package/dist/teste/StatCardTamanhoSm.test.js +32 -0
  61. package/dist/teste/TabsCompacta.test.d.ts +1 -0
  62. package/dist/teste/TabsCompacta.test.js +35 -0
  63. package/dist/teste/TagPositivoTingido.test.d.ts +1 -0
  64. package/dist/teste/TagPositivoTingido.test.js +54 -0
  65. package/dist/teste/lerArquivo.d.ts +1 -0
  66. package/dist/teste/lerArquivo.js +12 -0
  67. package/package.json +3 -3
  68. package/src/teste/AccordionControlado.test.tsx +87 -0
  69. package/src/teste/BadgeAttachedTomEPadding.test.tsx +60 -0
  70. package/src/teste/ButtonDensidade.test.tsx +40 -0
  71. package/src/teste/CalendarCompacto.test.tsx +37 -0
  72. package/src/teste/CoachmarkUmPasso.test.tsx +45 -0
  73. package/src/teste/DataTableCompactaETom.test.tsx +85 -0
  74. package/src/teste/InputGhost.test.tsx +42 -0
  75. package/src/teste/ModalProporcoes1Doc.test.tsx +36 -0
  76. package/src/teste/SegmentedControlSuperficie.test.tsx +52 -0
  77. package/src/teste/SelectItemComEscopo.test.tsx +69 -0
  78. package/src/teste/SelectListaFiltro.test.tsx +120 -0
  79. package/src/teste/StatCardTamanhoSm.test.tsx +42 -0
  80. package/src/teste/TabsCompacta.test.tsx +45 -0
  81. package/src/teste/TagPositivoTingido.test.tsx +60 -0
  82. package/src/teste/lerArquivo.ts +13 -0
@@ -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
+ });
@@ -0,0 +1,42 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import Input from "../components/Input/Input";
3
+ import { render } from "@testing-library/react";
4
+ import { lerArquivo } from "./lerArquivo";
5
+
6
+ const cssInput = await lerArquivo("../core/src/components/Input/Input.css");
7
+
8
+ const CSS: Record<string, string> = { Input: cssInput };
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("Input · variant ghost", () => {
17
+ it("ghost escreve a classe; sem a prop e com default, não", () => {
18
+ const { container } = render(
19
+ <>
20
+ <Input ariaLabel="a" variant="ghost" />
21
+ <Input ariaLabel="b" />
22
+ <Input ariaLabel="c" variant="default" />
23
+ </>,
24
+ );
25
+ expect(container.children[0]).toHaveClass("umds-input--ghost");
26
+ expect(container.children[1]).not.toHaveClass("umds-input--ghost");
27
+ expect(container.children[2]).not.toHaveClass("umds-input--ghost");
28
+ });
29
+
30
+ it("CSS: só o repouso muda (fundo claro, borda clara) e com baixa especificidade, para o hover e o foco vencerem", () => {
31
+ const t = css("Input");
32
+ const ghost = bloco(t, ".umds-input--ghost:where(:not(.umds-input--error):not(.umds-input--disabled):not(.umds-input--readonly)) .umds-input__container");
33
+ expect(ghost).toMatch(/background-color:\s*var\(--umds-semantic-color-background-light\)/);
34
+ expect(ghost).toMatch(/border-color:\s*var\(--umds-semantic-color-neutral-light-medium\)/);
35
+ expect(t.indexOf(".umds-input__container:focus-within:not(")).toBeGreaterThanOrEqual(0);
36
+ });
37
+
38
+ it("o foco, o erro e o desabilitado do campo ghost seguem as classes do padrão", () => {
39
+ const { container } = render(<Input ariaLabel="d" variant="ghost" errorMessage="Obrigatório" />);
40
+ expect(container.firstElementChild).toHaveClass("umds-input--ghost", "umds-input--error");
41
+ });
42
+ });
@@ -0,0 +1,36 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import Modal from "../components/Modal/Modal";
3
+ import { render, screen } from "@testing-library/react";
4
+ import { lerArquivo } from "./lerArquivo";
5
+
6
+ const cssModal = await lerArquivo("../core/src/components/Modal/Modal.css");
7
+
8
+ const CSS: Record<string, string> = { Modal: cssModal };
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("Modal · density 1doc", () => {
17
+ it("escreve a classe e convive com variant primary; sem a prop, não", () => {
18
+ render(<Modal open inline density="1doc" variant="primary" title="Resolver tarefa" />);
19
+ expect(screen.getByRole("dialog", { name: "Resolver tarefa" })).toHaveClass("umds-modal--1doc", "umds-modal--primary", "umds-modal--base");
20
+ });
21
+
22
+ it("sem density o modal é o de antes", () => {
23
+ render(<Modal open inline title="Padrão" />);
24
+ expect(screen.getByRole("dialog", { name: "Padrão" })).not.toHaveClass("umds-modal--1doc");
25
+ });
26
+
27
+ it("CSS: cabeçalho de 67px, rodapé de 65px (padding 12px 24px) e raio de 12px; o padrão (8px) não muda", () => {
28
+ const t = css("Modal");
29
+ expect(bloco(t, ".umds-modal--1doc.umds-modal--base .umds-modal__header")).toMatch(/min-height:\s*67px/);
30
+ const rodape = bloco(t, ".umds-modal--1doc.umds-modal--base .umds-modal__footer");
31
+ expect(rodape).toMatch(/min-height:\s*65px/);
32
+ expect(rodape).toMatch(/padding:\s*var\(--umds-primitive-spacing-mini\)\s+var\(--umds-primitive-spacing-xxs\)/);
33
+ expect(t).toMatch(/\.umds-modal--1doc:not\(\.umds-modal--fullscreen\):not\(\.umds-modal--janela-fullscreen\):not\(\.umds-modal--sheet\) \{\n(?:\s*\/\*.*\*\/\n)?\s*border-radius:\s*12px/);
34
+ expect(bloco(t, ".umds-modal")).toMatch(/border-radius:\s*var\(--umds-primitive-border-radius-sm\)/);
35
+ });
36
+ });
@@ -0,0 +1,52 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import SegmentedControl from "../components/SegmentedControl/SegmentedControl";
3
+ import { render } from "@testing-library/react";
4
+ import { lerArquivo } from "./lerArquivo";
5
+
6
+ const cssSegmentedControl = await lerArquivo("../core/src/components/SegmentedControl/SegmentedControl.css");
7
+
8
+ const CSS: Record<string, string> = { SegmentedControl: cssSegmentedControl };
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("SegmentedControl · variant surface", () => {
17
+ const OPCOES = [
18
+ { label: "Individual", value: "individual", count: 12 },
19
+ { label: "Em lote", value: "lote" },
20
+ ];
21
+
22
+ it("surface escreve a classe; sem a prop e com default, não", () => {
23
+ const { container } = render(
24
+ <>
25
+ <SegmentedControl options={OPCOES} variant="surface" defaultValue="individual" />
26
+ <SegmentedControl options={OPCOES} defaultValue="individual" />
27
+ <SegmentedControl options={OPCOES} variant="default" defaultValue="individual" />
28
+ </>,
29
+ );
30
+ expect(container.children[0]).toHaveClass("umds-segmented--surface");
31
+ expect(container.children[1]).not.toHaveClass("umds-segmented--surface");
32
+ expect(container.children[2]).not.toHaveClass("umds-segmented--surface");
33
+ });
34
+
35
+ it("CSS: o escolhido é branco, em negrito e com sombra; o trilho é cinza claro; o azul cheio do padrão segue", () => {
36
+ const t = css("SegmentedControl");
37
+ const escolhido = bloco(t, ".umds-segmented--surface .umds-segmented__input:checked + .umds-segmented__item,\n.umds-segmented--surface .umds-segmented__input:checked + .umds-segmented__item:hover");
38
+ expect(escolhido).toMatch(/background-color:\s*var\(--umds-semantic-color-background-white\)/);
39
+ expect(escolhido).toMatch(/font-weight:\s*var\(--umds-primitive-font-weight-bold\)/);
40
+ expect(escolhido).toMatch(/box-shadow:\s*var\(--umds-semantic-shadow-md\)/);
41
+ expect(bloco(t, ".umds-segmented--surface .umds-segmented__track")).toMatch(/background-color:\s*var\(--umds-semantic-color-neutral-light-medium\)/);
42
+ expect(t).toMatch(/\.umds-segmented__input:checked \+ \.umds-segmented__item,\n\.umds-segmented__input:checked \+ \.umds-segmented__item:hover \{\n\s*background-color:\s*var\(--umds-semantic-color-primary-pure\)/);
43
+ });
44
+
45
+ it("o contador do escolhido fica cinza sobre o branco e o dos outros, branco sobre o trilho", () => {
46
+ const t = css("SegmentedControl");
47
+ expect(bloco(t, ".umds-segmented--surface .umds-segmented__count")).toMatch(/background-color:\s*var\(--umds-semantic-color-background-white\)/);
48
+ expect(bloco(t, ".umds-segmented--surface .umds-segmented__input:checked + .umds-segmented__item .umds-segmented__count")).toMatch(
49
+ /background-color:\s*var\(--umds-semantic-color-neutral-light-medium\)/,
50
+ );
51
+ });
52
+ });
@@ -0,0 +1,69 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { fireEvent, render, screen } from "@testing-library/react";
3
+ import Select from "../components/Select/Select";
4
+
5
+ const OPCOES = [
6
+ { label: "Urgente", value: "urgente", etiqueta: "Prazo hoje" },
7
+ { label: "Rotina", value: "rotina", etiqueta: "Sem prazo" },
8
+ ];
9
+ const item = ({ optionData }: any) => (
10
+ <span data-testid="item">
11
+ <b>{optionData.label}</b> <i>{optionData.etiqueta}</i>
12
+ </span>
13
+ );
14
+
15
+ describe("Select · item com escopo da opção", () => {
16
+ it("a lista mostra o item de cada opção, recebendo a opção (optionData e o apelido option)", () => {
17
+ const vistos: any[] = [];
18
+ render(
19
+ <Select
20
+ ariaLabel="Prioridade"
21
+ options={OPCOES}
22
+ item={(escopo: any) => {
23
+ vistos.push(escopo);
24
+ return <span data-testid="item">{escopo.optionData.label}</span>;
25
+ }}
26
+ />,
27
+ );
28
+ fireEvent.click(screen.getByRole("combobox"));
29
+ expect(screen.getAllByTestId("item").map((n) => n.textContent)).toEqual(["Urgente", "Rotina"]);
30
+ expect(vistos[0].optionData).toBe(OPCOES[0]);
31
+ expect(vistos[0].option).toBe(OPCOES[0]);
32
+ });
33
+
34
+ it("o gatilho com a opção escolhida mostra o conteúdo do item, não o texto", () => {
35
+ render(<Select ariaLabel="Prioridade" options={OPCOES} value="urgente" item={item} />);
36
+ const gatilho = screen.getByRole("combobox");
37
+ expect(gatilho.querySelector(".umds-select__trigger-item")).not.toBeNull();
38
+ expect(gatilho).toHaveTextContent("Urgente");
39
+ expect(gatilho).toHaveTextContent("Prazo hoje");
40
+ expect(gatilho.querySelector(".umds-select__placeholder")).toBeNull();
41
+ });
42
+
43
+ it("sem valor o gatilho mostra o placeholder; escolher na lista passa o item para o gatilho", () => {
44
+ function App() {
45
+ return <Select ariaLabel="Prioridade" placeholder="Escolha" options={OPCOES} item={item} />;
46
+ }
47
+ render(<App />);
48
+ const gatilho = screen.getByRole("combobox");
49
+ expect(gatilho).toHaveTextContent("Escolha");
50
+ expect(gatilho.querySelector(".umds-select__trigger-item")).toBeNull();
51
+ fireEvent.click(gatilho);
52
+ fireEvent.click(screen.getAllByRole("option")[1]);
53
+ expect(screen.getByRole("combobox").querySelector(".umds-select__trigger-item")).toHaveTextContent("Rotina");
54
+ });
55
+
56
+ it("seleção múltipla segue com o texto no gatilho", () => {
57
+ render(<Select ariaLabel="Prioridade" multiple options={OPCOES} value={["urgente"]} item={item} />);
58
+ expect(screen.getByRole("combobox").querySelector(".umds-select__trigger-item")).toBeNull();
59
+ });
60
+
61
+ it("sem item nada muda: o gatilho mostra o rótulo; item como nó solto (legado) não vai ao gatilho", () => {
62
+ const { rerender } = render(<Select ariaLabel="Prioridade" options={OPCOES} value="urgente" />);
63
+ expect(screen.getByRole("combobox")).toHaveTextContent("Urgente");
64
+ expect(screen.getByRole("combobox").querySelector(".umds-select__trigger-item")).toBeNull();
65
+ rerender(<Select ariaLabel="Prioridade" options={OPCOES} value="urgente" item={<em>fixo</em>} />);
66
+ expect(screen.getByRole("combobox").querySelector(".umds-select__trigger-item")).toBeNull();
67
+ expect(screen.getByRole("combobox")).toHaveTextContent("Urgente");
68
+ });
69
+ });