@1doc/1ds-react 2.14.0 → 2.15.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 (49) hide show
  1. package/CHANGELOG.md +51 -67
  2. package/dist/components/AILabel/AILabel.css +1 -1
  3. package/dist/components/Alert/Alert.css +1 -1
  4. package/dist/components/AppShell/AppShell.css +4 -0
  5. package/dist/components/AttachmentCard/AttachmentCard.css +2 -2
  6. package/dist/components/Avatar/Avatar.d.ts +8 -3
  7. package/dist/components/Avatar/Avatar.js +28 -4
  8. package/dist/components/BottomNavigation/BottomNavigation.css +2 -2
  9. package/dist/components/BottomSheet/BottomSheet.css +2 -2
  10. package/dist/components/Box/Box.css +3 -3
  11. package/dist/components/Card/Card.css +5 -5
  12. package/dist/components/Chart/Chart.css +1 -1
  13. package/dist/components/Coachmark/Coachmark.css +1 -1
  14. package/dist/components/Combobox/Combobox.css +1 -1
  15. package/dist/components/ConfirmDialog/ConfirmDialog.css +1 -1
  16. package/dist/components/DatePicker/DatePicker.css +1 -1
  17. package/dist/components/Drawer/Drawer.css +2 -2
  18. package/dist/components/Dropdown/Dropdown.css +1 -1
  19. package/dist/components/Dropdown/Dropdown.d.ts +8 -0
  20. package/dist/components/Dropdown/Dropdown.js +2 -0
  21. package/dist/components/EmptyState/EmptyState.css +12 -0
  22. package/dist/components/EmptyState/EmptyState.d.ts +3 -0
  23. package/dist/components/EmptyState/EmptyState.js +1 -0
  24. package/dist/components/Kanban/Kanban.css +2 -2
  25. package/dist/components/Kanban/KanbanCard.css +2 -2
  26. package/dist/components/List/List.css +37 -1
  27. package/dist/components/List/List.d.ts +3 -0
  28. package/dist/components/List/List.js +6 -2
  29. package/dist/components/List/ListItem.d.ts +9 -0
  30. package/dist/components/List/ListItem.js +5 -1
  31. package/dist/components/Modal/Modal.css +3 -4
  32. package/dist/components/MultiSelect/MultiSelect.css +1 -1
  33. package/dist/components/OrgChart/OrgChart.css +2 -2
  34. package/dist/components/PhoneInput/PhoneInput.css +2 -2
  35. package/dist/components/Popover/Popover.css +1 -1
  36. package/dist/components/SegmentedControl/SegmentedControl.css +1 -1
  37. package/dist/components/Select/Select.css +1 -1
  38. package/dist/components/StatCard/StatCard.css +42 -3
  39. package/dist/components/Switch/Switch.css +2 -2
  40. package/dist/components/Text/Text.d.ts +6 -0
  41. package/dist/components/TimePicker/TimePicker.css +1 -1
  42. package/dist/components/Toast/Toast.css +2 -2
  43. package/dist/components/Tooltip/Tooltip.css +20 -3
  44. package/dist/teste/PedidosMatheus2.test.d.ts +1 -0
  45. package/dist/teste/PedidosMatheus2.test.js +82 -0
  46. package/dist/teste/SegmentedControlSuperficie.test.js +1 -1
  47. package/package.json +3 -3
  48. package/src/teste/PedidosMatheus2.test.tsx +102 -0
  49. package/src/teste/SegmentedControlSuperficie.test.tsx +1 -1
@@ -11,9 +11,11 @@
11
11
  align-items: flex-start;
12
12
  gap: var(--umds-primitive-spacing-mini);
13
13
  padding: var(--umds-primitive-spacing-xxxs);
14
- border: 1px solid var(--umds-semantic-color-neutral-light-down);
14
+ /* Mesma borda e superfície do Card: lado a lado, os cartões têm o mesmo
15
+ contorno (#EBEBEB no claro), e no escuro o fundo é o elevado. */
16
+ border: 1px solid var(--umds-semantic-color-neutral-light-medium);
15
17
  border-radius: var(--umds-primitive-border-radius-sm);
16
- background-color: var(--umds-semantic-color-background-white);
18
+ background-color: var(--umds-semantic-color-background-elevated);
17
19
  color: var(--umds-semantic-color-neutral-dark-pure);
18
20
  font-family: var(--umds-semantic-font-family-base);
19
21
  text-align: left;
@@ -38,7 +40,7 @@
38
40
 
39
41
  .umds-stat-card--acionavel:hover {
40
42
  border-color: var(--umds-semantic-color-primary-pure);
41
- box-shadow: var(--umds-semantic-shadow-sm);
43
+ box-shadow: var(--umds-semantic-shadow-bottom-sm);
42
44
  }
43
45
 
44
46
  /* Foco canônico de não-campo. */
@@ -353,3 +355,40 @@
353
355
  display: inline-flex;
354
356
  align-items: center;
355
357
  }
358
+
359
+ /* ── Segunda medida em largura pequena (container query) ──
360
+ Num cartão estreito (cabeçalho mobile, ~115px) as duas medidas lado a lado
361
+ se sobrepunham. O cartão com segunda medida vira container de largura e,
362
+ abaixo de 240px, a segunda medida desce para a linha de baixo, separada por
363
+ um filete em cima em vez do da esquerda.
364
+ `width: 100%`: um container de inline-size não mede o conteúdo, então num
365
+ pai flex com tamanho pelo conteúdo ele encolheria até sumir. Com 100%, numa
366
+ linha flex os cartões dividem o espaço; em grade e em bloco nada muda. */
367
+ .umds-stat-card--com-secundaria {
368
+ container-type: inline-size;
369
+ width: 100%;
370
+ flex-wrap: wrap;
371
+ }
372
+
373
+ /* 240px: duas medidas de 24px com o filete e o respiro não cabem abaixo disso.
374
+ Container query não aceita var(), por isso o literal. */
375
+ @container (max-width: 240px) {
376
+ .umds-stat-card:not(.umds-stat-card--centro) .umds-stat-card__secundaria {
377
+ flex-basis: 100%;
378
+ margin-left: 0;
379
+ padding-left: 0;
380
+ padding-top: var(--umds-primitive-spacing-nano);
381
+ border-left: none;
382
+ border-top: var(--umds-primitive-border-width-hairline) solid var(--umds-semantic-color-neutral-light-medium);
383
+ align-items: flex-start;
384
+ text-align: left;
385
+ }
386
+
387
+ .umds-stat-card--rotulo-topo .umds-stat-card__secundaria,
388
+ .umds-stat-card--rotulo-topo .umds-stat-card__secundaria:has(.umds-stat-card__rotulo-secundario) {
389
+ grid-column: 1 / -1;
390
+ grid-row: auto;
391
+ display: flex;
392
+ margin-left: 0;
393
+ }
394
+ }
@@ -51,7 +51,7 @@
51
51
  .umds-switch__thumb {
52
52
  background-color: var(--umds-semantic-color-neutral-light-pure);
53
53
  border-radius: var(--umds-primitive-border-radius-circular);
54
- box-shadow: var(--umds-semantic-shadow-sm); /* era 0 1px 2px 0.1, 0 1px 1px 0.06 — mapeado para shadow-sm 14/07 (sombra do thumb fica mais sutil) */
54
+ box-shadow: var(--umds-semantic-shadow-bottom-sm); /* era 0 1px 2px 0.1, 0 1px 1px 0.06 — mapeado para shadow-sm 14/07 (sombra do thumb fica mais sutil) */
55
55
  display: flex;
56
56
  align-items: center;
57
57
  justify-content: center;
@@ -144,7 +144,7 @@
144
144
  background-color: var(--umds-semantic-color-neutral-light-pure);
145
145
  padding: var(--umds-primitive-spacing-xxxs);
146
146
  border-radius: var(--umds-primitive-border-radius-sm);
147
- box-shadow: var(--umds-semantic-shadow-sm); /* era 0 4px 8px 0.06 — mapeado para shadow-sm 14/07 (card de fundo sutil) */
147
+ box-shadow: var(--umds-semantic-shadow-bottom-sm); /* era 0 4px 8px 0.06 — mapeado para shadow-sm 14/07 (card de fundo sutil) */
148
148
  }
149
149
 
150
150
  .umds-switch-text {
@@ -62,6 +62,12 @@ export interface TextProps {
62
62
  /**
63
63
  * Cor do texto usando tokens semânticos.
64
64
  *
65
+ * SEM `color`, o texto HERDA a cor do pai (é o que deixa um Text dentro de um
66
+ * botão, alerta ou cartão colorido acompanhar a cor dele). Dentro do
67
+ * `AppShell` o pai já tem a cor de texto do tema. Fora dele, num título solto
68
+ * na página, passe `color="primary"` (`neutral-dark-pure`, o texto principal,
69
+ * não o azul) para o tema escuro funcionar.
70
+ *
65
71
  * `disabled` (`neutral-dark-up`, #A3A3A3, 2,5:1 sobre branco) é SÓ para
66
72
  * texto de controle desabilitado (rótulo, valor ou ajuda de um campo ou
67
73
  * botão inativo), que o WCAG 1.4.3 isenta de contraste. Em qualquer outro
@@ -123,7 +123,7 @@
123
123
  background: var(--umds-semantic-color-background-elevated); /* superfície elevada: igual ao white no light; mais clara no dark (decisão 25/07) */
124
124
  border: 1px solid var(--umds-semantic-color-neutral-light-medium); /* #EBEBEB */
125
125
  border-radius: var(--umds-primitive-border-radius-sm); /* 8px — protótipo --radius-lg 12px, aproximado para sm (coerente com input e dígitos) */
126
- box-shadow: var(--umds-semantic-shadow-md);
126
+ box-shadow: var(--umds-semantic-shadow-bottom-md);
127
127
  padding: var(--umds-primitive-spacing-xxxs); /* 16px — protótipo --space-4 */
128
128
  box-sizing: border-box;
129
129
  animation: umds-timepicker-in var(--umds-semantic-motion-duration-fast) var(--umds-semantic-motion-easing-standard) both;
@@ -4,7 +4,7 @@
4
4
  Variantes semânticas (prop `variant`): positive, negative, info, neutral
5
5
  (warning do protótipo mesclado no info — ambos laranja feedback-info).
6
6
  Tokens do protótipo mapeados para os tokens 1DS.
7
- Snap 16/07 (decisões novas): sombra → --umds-semantic-shadow-md (token novo,
7
+ Snap 16/07 (decisões novas): sombra → --umds-semantic-shadow-bottom-md (token novo,
8
8
  valor idêntico ao literal do protótipo); radius 12px → md 16px (overlay
9
9
  flutuante → md, como Dropdown/Modal); radius 6px → sm 8px; font-size
10
10
  11px → xxxs 12px; spacing 12px → xxxs 16px (mesmas decisões do Alert —
@@ -24,7 +24,7 @@
24
24
  padding: var(--umds-primitive-spacing-xxxs, 16px); /* era 12px 16px (protótipo --space-3/--space-4) — snap 16/07 */
25
25
  border-radius: var(--umds-primitive-border-radius-sm); /* 8px universal (rodada ago/2026) — a exceção "overlay flutuante → md" caiu */
26
26
  border: 1px solid transparent;
27
- box-shadow: var(--umds-semantic-shadow-md); /* protótipo --shadow-md — token novo com valor idêntico */
27
+ box-shadow: var(--umds-semantic-shadow-bottom-md); /* protótipo --shadow-md — token novo com valor idêntico */
28
28
  font-family: var(--umds-semantic-font-family-base);
29
29
  box-sizing: border-box;
30
30
  /* O Portal usa display: contents e não posiciona nada — o próprio toast
@@ -47,9 +47,12 @@
47
47
  flex-direction: column;
48
48
  position: relative;
49
49
  padding: var(--umds-primitive-spacing-mini); /* protótipo --space-3: sem equivalente 1DS exato */
50
- background-color: var(--umds-semantic-color-neutral-light-pure);
50
+ /* Superfície elevada, como o Card: igual ao branco nos temas claros e no
51
+ alto contraste; nos escuros é mais clara que a página (o neutral-light-pure
52
+ de antes era quase da cor do fundo, e o balão sumia). */
53
+ background-color: var(--umds-semantic-color-background-elevated);
51
54
  border-radius: var(--umds-primitive-border-radius-sm); /* 8px */
52
- box-shadow: var(--umds-primitive-shadow-level-2);
55
+ box-shadow: var(--umds-semantic-shadow-bottom-lg);
53
56
  font-family: var(--umds-semantic-font-family-base);
54
57
  font-size: var(--umds-primitive-font-size-xxxs); /* 12px */
55
58
  line-height: 1.25;
@@ -93,7 +96,7 @@
93
96
  position: absolute;
94
97
  width: 8px;
95
98
  height: 8px;
96
- background-color: var(--umds-semantic-color-neutral-light-pure);
99
+ background-color: var(--umds-semantic-color-background-elevated);
97
100
  transform: rotate(45deg);
98
101
  }
99
102
 
@@ -181,6 +184,20 @@
181
184
  border-color: var(--umds-semantic-color-background-dark);
182
185
  }
183
186
 
187
+ /* ── Temas escuros (1doc-dark e os *-dark da paleta) ──
188
+ A sombra preta não aparece sobre fundo escuro: o balão ganha o traço do Card
189
+ (neutral-light-medium), por dentro para não mudar o tamanho. A seta troca o
190
+ traço das duas faces expostas pelo mesmo tom e continua o contorno. O
191
+ variant="dark" (balão claro invertido) não precisa: ele contrasta sozinho. */
192
+ [data-theme$="-dark"] .umds-tooltip__bubble:not(.umds-tooltip__bubble--dark) {
193
+ outline: var(--umds-primitive-border-width-hairline) solid var(--umds-semantic-color-neutral-light-medium);
194
+ outline-offset: calc(-1 * var(--umds-primitive-border-width-hairline));
195
+ }
196
+
197
+ [data-theme$="-dark"] .umds-tooltip__bubble:not(.umds-tooltip__bubble--dark) .umds-tooltip__arrow {
198
+ border-color: var(--umds-semantic-color-neutral-light-medium);
199
+ }
200
+
184
201
  /* ── Alto contraste ──
185
202
  O balão usa neutral-light-pure, que neste tema é preto como a página, e a
186
203
  sombra não aparece: sem traço o balão some. Outline para dentro (não
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,82 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { describe, expect, it, vi } from "vitest";
3
+ import { fireEvent, render, screen } from "@testing-library/react";
4
+ import Avatar from "../components/Avatar/Avatar";
5
+ import Dropdown from "../components/Dropdown/Dropdown";
6
+ import EmptyState from "../components/EmptyState/EmptyState";
7
+ import List from "../components/List/List";
8
+ import ListItem from "../components/List/ListItem";
9
+ import { lerArquivo } from "./lerArquivo";
10
+ describe("Avatar · fallback e name", () => {
11
+ it("a foto que falha volta para as iniciais do name", () => {
12
+ const onError = vi.fn();
13
+ const { container } = render(_jsx(Avatar, { src: "/quebrada.png", name: "Cris Lima", onError: onError }));
14
+ fireEvent.error(container.querySelector("img"));
15
+ expect(container.querySelector("img")).toBeNull();
16
+ expect(container.textContent).toBe("CL");
17
+ expect(container.firstElementChild).toHaveClass("umds-avatar--letter");
18
+ expect(onError).toHaveBeenCalledTimes(1);
19
+ });
20
+ it("sem name, a falha mostra o children", () => {
21
+ const { container } = render(_jsx(Avatar, { src: "/quebrada.png", children: "ML" }));
22
+ fireEvent.error(container.querySelector("img"));
23
+ expect(container.textContent).toBe("ML");
24
+ });
25
+ it("um src novo tenta a foto de novo", () => {
26
+ const { container, rerender } = render(_jsx(Avatar, { src: "/quebrada.png", name: "Cris Lima" }));
27
+ fireEvent.error(container.querySelector("img"));
28
+ rerender(_jsx(Avatar, { src: "/boa.png", name: "Cris Lima" }));
29
+ expect(container.querySelector("img")).toHaveAttribute("src", "/boa.png");
30
+ });
31
+ it("name gera as iniciais (primeiro e último nome) e o nome acessível", () => {
32
+ const { container } = render(_jsx(Avatar, { name: "Matheus de Souza" }));
33
+ expect(container.textContent).toBe("MS");
34
+ expect(container.firstElementChild).toHaveAttribute("aria-label", "Matheus de Souza");
35
+ expect(container.firstElementChild).toHaveAttribute("role", "img");
36
+ });
37
+ });
38
+ describe("Dropdown · onSelect no item", () => {
39
+ it("chama a ação do item com os campos próprios, e depois o onSelect do Dropdown", async () => {
40
+ const ordem = [];
41
+ const doItem = vi.fn((item) => ordem.push("item:" + item.command));
42
+ const doMenu = vi.fn(() => ordem.push("menu"));
43
+ render(_jsx(Dropdown, { ariaLabel: "A\u00E7\u00F5es", onSelect: doMenu, items: [{ key: "a", label: "Assinar", command: "assinar", onSelect: doItem }], children: _jsx("button", { type: "button", children: "Abrir" }) }));
44
+ fireEvent.click(screen.getByRole("button", { name: "Abrir" }));
45
+ fireEvent.click(await screen.findByRole("menuitem", { name: "Assinar" }));
46
+ expect(ordem).toEqual(["item:assinar", "menu"]);
47
+ });
48
+ });
49
+ describe("ListItem · variant e li", () => {
50
+ it("variant primary marca a classe sem aria-current", () => {
51
+ render(_jsx("ul", { children: _jsx(ListItem, { variant: "primary", href: "#c", title: "Circular" }) }));
52
+ const link = screen.getByRole("link", { name: "Circular" });
53
+ expect(link).toHaveClass("umds-list-item--primary");
54
+ expect(link).not.toHaveAttribute("aria-current");
55
+ });
56
+ it("liClassName e liStyle vão para o li", () => {
57
+ const { container } = render(_jsx("ul", { children: _jsx(ListItem, { title: "Memorando", liClassName: "entra", liStyle: { "--ordem": 2 }, onClick: () => { } }) }));
58
+ const li = container.querySelector("li");
59
+ expect(li).toHaveClass("umds-list__item");
60
+ expect(li).toHaveClass("entra");
61
+ expect(li.style.getPropertyValue("--ordem")).toBe("2");
62
+ });
63
+ });
64
+ describe("EmptyState · fill e List · emptyFill", () => {
65
+ it("fill escreve a classe; List repassa emptyFill", () => {
66
+ const { container } = render(_jsxs(_Fragment, { children: [_jsx(EmptyState, { title: "Vazio", fill: true }), _jsx(List, { ariaLabel: "Docs", isEmpty: true, emptyFill: true })] }));
67
+ expect(container.querySelector(".umds-empty-state--fill")).not.toBeNull();
68
+ expect(container.querySelector(".umds-list--empty-fill .umds-empty-state--fill")).not.toBeNull();
69
+ });
70
+ });
71
+ describe("AppShell e StatCard · CSS", () => {
72
+ it("o AppShell dá a cor de texto do tema a quem herda", async () => {
73
+ const css = (await lerArquivo("../core/src/components/AppShell/AppShell.css")).replace(/\r\n/g, "\n");
74
+ const bloco = css.slice(css.indexOf(".umds-app-shell {"), css.indexOf("}", css.indexOf(".umds-app-shell {")));
75
+ expect(bloco).toContain("color: var(--umds-semantic-color-neutral-dark-pure)");
76
+ });
77
+ it("a StatCard com segunda medida empilha em largura pequena", async () => {
78
+ const css = (await lerArquivo("../core/src/components/StatCard/StatCard.css")).replace(/\r\n/g, "\n");
79
+ expect(css).toContain("container-type: inline-size");
80
+ expect(css).toMatch(/@container \(max-width: 240px\)/);
81
+ });
82
+ });
@@ -27,7 +27,7 @@ describe("SegmentedControl · variant surface", () => {
27
27
  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");
28
28
  expect(escolhido).toMatch(/background-color:\s*var\(--umds-semantic-color-background-white\)/);
29
29
  expect(escolhido).toMatch(/font-weight:\s*var\(--umds-primitive-font-weight-bold\)/);
30
- expect(escolhido).toMatch(/box-shadow:\s*var\(--umds-semantic-shadow-md\)/);
30
+ expect(escolhido).toMatch(/box-shadow:\s*var\(--umds-semantic-shadow-bottom-md\)/);
31
31
  expect(bloco(t, ".umds-segmented--surface .umds-segmented__track")).toMatch(/background-color:\s*var\(--umds-semantic-color-neutral-light-medium\)/);
32
32
  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\)/);
33
33
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1doc/1ds-react",
3
- "version": "2.14.0",
3
+ "version": "2.15.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.14.0",
49
- "@1doc/1ds-tokens": "0.7.0"
48
+ "@1doc/1ds-core": "2.15.0",
49
+ "@1doc/1ds-tokens": "0.8.0"
50
50
  },
51
51
  "publishConfig": {
52
52
  "access": "public"
@@ -0,0 +1,102 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { fireEvent, render, screen } from "@testing-library/react";
3
+ import Avatar from "../components/Avatar/Avatar";
4
+ import Dropdown from "../components/Dropdown/Dropdown";
5
+ import EmptyState from "../components/EmptyState/EmptyState";
6
+ import List from "../components/List/List";
7
+ import ListItem from "../components/List/ListItem";
8
+ import { lerArquivo } from "./lerArquivo";
9
+
10
+ describe("Avatar · fallback e name", () => {
11
+ it("a foto que falha volta para as iniciais do name", () => {
12
+ const onError = vi.fn();
13
+ const { container } = render(<Avatar src="/quebrada.png" name="Cris Lima" onError={onError} />);
14
+ fireEvent.error(container.querySelector("img")!);
15
+ expect(container.querySelector("img")).toBeNull();
16
+ expect(container.textContent).toBe("CL");
17
+ expect(container.firstElementChild).toHaveClass("umds-avatar--letter");
18
+ expect(onError).toHaveBeenCalledTimes(1);
19
+ });
20
+
21
+ it("sem name, a falha mostra o children", () => {
22
+ const { container } = render(<Avatar src="/quebrada.png">ML</Avatar>);
23
+ fireEvent.error(container.querySelector("img")!);
24
+ expect(container.textContent).toBe("ML");
25
+ });
26
+
27
+ it("um src novo tenta a foto de novo", () => {
28
+ const { container, rerender } = render(<Avatar src="/quebrada.png" name="Cris Lima" />);
29
+ fireEvent.error(container.querySelector("img")!);
30
+ rerender(<Avatar src="/boa.png" name="Cris Lima" />);
31
+ expect(container.querySelector("img")).toHaveAttribute("src", "/boa.png");
32
+ });
33
+
34
+ it("name gera as iniciais (primeiro e último nome) e o nome acessível", () => {
35
+ const { container } = render(<Avatar name="Matheus de Souza" />);
36
+ expect(container.textContent).toBe("MS");
37
+ expect(container.firstElementChild).toHaveAttribute("aria-label", "Matheus de Souza");
38
+ expect(container.firstElementChild).toHaveAttribute("role", "img");
39
+ });
40
+ });
41
+
42
+ describe("Dropdown · onSelect no item", () => {
43
+ it("chama a ação do item com os campos próprios, e depois o onSelect do Dropdown", async () => {
44
+ const ordem: string[] = [];
45
+ const doItem = vi.fn((item) => ordem.push("item:" + item.command));
46
+ const doMenu = vi.fn(() => ordem.push("menu"));
47
+ render(
48
+ <Dropdown ariaLabel="Ações" onSelect={doMenu} items={[{ key: "a", label: "Assinar", command: "assinar", onSelect: doItem }]}>
49
+ <button type="button">Abrir</button>
50
+ </Dropdown>,
51
+ );
52
+ fireEvent.click(screen.getByRole("button", { name: "Abrir" }));
53
+ fireEvent.click(await screen.findByRole("menuitem", { name: "Assinar" }));
54
+ expect(ordem).toEqual(["item:assinar", "menu"]);
55
+ });
56
+ });
57
+
58
+ describe("ListItem · variant e li", () => {
59
+ it("variant primary marca a classe sem aria-current", () => {
60
+ render(<ul><ListItem variant="primary" href="#c" title="Circular" /></ul>);
61
+ const link = screen.getByRole("link", { name: "Circular" });
62
+ expect(link).toHaveClass("umds-list-item--primary");
63
+ expect(link).not.toHaveAttribute("aria-current");
64
+ });
65
+
66
+ it("liClassName e liStyle vão para o li", () => {
67
+ const { container } = render(
68
+ <ul><ListItem title="Memorando" liClassName="entra" liStyle={{ "--ordem": 2 } as any} onClick={() => {}} /></ul>,
69
+ );
70
+ const li = container.querySelector("li")!;
71
+ expect(li).toHaveClass("umds-list__item");
72
+ expect(li).toHaveClass("entra");
73
+ expect(li.style.getPropertyValue("--ordem")).toBe("2");
74
+ });
75
+ });
76
+
77
+ describe("EmptyState · fill e List · emptyFill", () => {
78
+ it("fill escreve a classe; List repassa emptyFill", () => {
79
+ const { container } = render(
80
+ <>
81
+ <EmptyState title="Vazio" fill />
82
+ <List ariaLabel="Docs" isEmpty emptyFill />
83
+ </>,
84
+ );
85
+ expect(container.querySelector(".umds-empty-state--fill")).not.toBeNull();
86
+ expect(container.querySelector(".umds-list--empty-fill .umds-empty-state--fill")).not.toBeNull();
87
+ });
88
+ });
89
+
90
+ describe("AppShell e StatCard · CSS", () => {
91
+ it("o AppShell dá a cor de texto do tema a quem herda", async () => {
92
+ const css = (await lerArquivo("../core/src/components/AppShell/AppShell.css")).replace(/\r\n/g, "\n");
93
+ const bloco = css.slice(css.indexOf(".umds-app-shell {"), css.indexOf("}", css.indexOf(".umds-app-shell {")));
94
+ expect(bloco).toContain("color: var(--umds-semantic-color-neutral-dark-pure)");
95
+ });
96
+
97
+ it("a StatCard com segunda medida empilha em largura pequena", async () => {
98
+ const css = (await lerArquivo("../core/src/components/StatCard/StatCard.css")).replace(/\r\n/g, "\n");
99
+ expect(css).toContain("container-type: inline-size");
100
+ expect(css).toMatch(/@container \(max-width: 240px\)/);
101
+ });
102
+ });
@@ -37,7 +37,7 @@ describe("SegmentedControl · variant surface", () => {
37
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
38
  expect(escolhido).toMatch(/background-color:\s*var\(--umds-semantic-color-background-white\)/);
39
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\)/);
40
+ expect(escolhido).toMatch(/box-shadow:\s*var\(--umds-semantic-shadow-bottom-md\)/);
41
41
  expect(bloco(t, ".umds-segmented--surface .umds-segmented__track")).toMatch(/background-color:\s*var\(--umds-semantic-color-neutral-light-medium\)/);
42
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
43
  });