@1doc/1ds-react 2.12.2 → 2.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/dist/components/Breadcrumb/Breadcrumb.css +50 -0
- package/dist/components/Breadcrumb/Breadcrumb.d.ts +9 -0
- package/dist/components/Breadcrumb/Breadcrumb.js +11 -2
- package/dist/components/Button/Button.css +6 -1
- package/dist/components/Button/Button.d.ts +11 -2
- package/dist/components/Button/Button.js +15 -3
- package/dist/components/Counter/Counter.css +43 -0
- package/dist/components/Counter/Counter.d.ts +57 -0
- package/dist/components/Counter/Counter.js +99 -0
- package/dist/components/Dropdown/Dropdown.css +2 -0
- package/dist/components/Dropdown/Dropdown.d.ts +9 -0
- package/dist/components/Dropdown/Dropdown.js +20 -2
- package/dist/components/List/List.css +47 -0
- package/dist/components/List/ListItem.d.ts +10 -0
- package/dist/components/List/ListItem.js +1 -1
- package/dist/components/StatCard/StatCard.css +57 -2
- package/dist/components/StatCard/StatCard.d.ts +12 -6
- package/dist/components/StatCard/StatCard.js +6 -2
- package/dist/components/Tooltip/Tooltip.css +3 -0
- package/dist/components/Tooltip/Tooltip.js +33 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/teste/BreadcrumbTruncado.test.d.ts +1 -0
- package/dist/teste/BreadcrumbTruncado.test.js +40 -0
- package/dist/teste/PedidosMatheus.test.d.ts +1 -0
- package/dist/teste/PedidosMatheus.test.js +91 -0
- package/dist/teste/StatCardTamanhoSm.test.js +8 -0
- package/package.json +2 -2
- package/src/index.ts +2 -0
- package/src/teste/BreadcrumbTruncado.test.tsx +52 -0
- package/src/teste/PedidosMatheus.test.tsx +129 -0
- package/src/teste/StatCardTamanhoSm.test.tsx +9 -0
|
@@ -77,6 +77,6 @@ function ListItem(props) {
|
|
|
77
77
|
}
|
|
78
78
|
return out;
|
|
79
79
|
}
|
|
80
|
-
return (
|
|
80
|
+
return (_jsx(_Fragment, { children: !props.standalone ? (_jsx(_Fragment, { children: _jsxs("li", { className: "umds-list__item", children: [elementTag() === "button" ? (_jsxs("button", { type: "button", id: props.id, title: props.tooltip, disabled: props.disabled, "aria-current": ariaCurrent(), onClick: (event) => handleClick(event), ...htmlAttrs(), className: classes(), children: [props.start ? (_jsx("span", { className: "umds-list-item__start", children: _jsx(_Fragment, { children: props.start }) })) : null, _jsxs("span", { className: "umds-list-item__body", children: [props.title ? (_jsx("span", { className: "umds-list-item__title", children: _jsx(Text, { variant: "body2", as: "span", weight: "semibold", color: "inherit", children: _jsx(_Fragment, { children: props.title }) }) })) : null, props.subtitle ? (_jsx("span", { className: "umds-list-item__subtitle", children: _jsx(Text, { variant: "body2", as: "span", color: "inherit", children: _jsx(_Fragment, { children: props.subtitle }) }) })) : null, props.meta ? (_jsx("span", { className: "umds-list-item__meta", children: _jsx(Text, { variant: "caption", as: "span", color: "inherit", children: _jsx(_Fragment, { children: props.meta }) }) })) : null, _jsx(_Fragment, { children: props.children })] }), props.end ? (_jsx("span", { className: "umds-list-item__end", children: _jsx(_Fragment, { children: props.end }) })) : null] })) : null, elementTag() === "a" ? (_jsxs("a", { id: props.id, title: props.tooltip, href: hrefValue(), target: props.target, rel: relValue(), "aria-current": ariaCurrent(), "aria-disabled": ariaDisabled(), onClick: (event) => handleClick(event), ...htmlAttrs(), className: classes(), children: [props.start ? (_jsx("span", { className: "umds-list-item__start", children: _jsx(_Fragment, { children: props.start }) })) : null, _jsxs("span", { className: "umds-list-item__body", children: [props.title ? (_jsx("span", { className: "umds-list-item__title", children: _jsx(Text, { variant: "body2", as: "span", weight: "semibold", color: "inherit", children: _jsx(_Fragment, { children: props.title }) }) })) : null, props.subtitle ? (_jsx("span", { className: "umds-list-item__subtitle", children: _jsx(Text, { variant: "body2", as: "span", color: "inherit", children: _jsx(_Fragment, { children: props.subtitle }) }) })) : null, props.meta ? (_jsx("span", { className: "umds-list-item__meta", children: _jsx(Text, { variant: "caption", as: "span", color: "inherit", children: _jsx(_Fragment, { children: props.meta }) }) })) : null, _jsx(_Fragment, { children: props.children })] }), props.end ? (_jsx("span", { className: "umds-list-item__end", children: _jsx(_Fragment, { children: props.end }) })) : null] })) : null, elementTag() === "div" ? (_jsxs("div", { id: props.id, title: props.tooltip, role: divRole(), tabIndex: divTabIndex(), "aria-current": ariaCurrent(), "aria-disabled": ariaDisabled(), onClick: (event) => handleClick(event), onKeyDown: (event) => handleKeyDown(event), ...htmlAttrs(), className: classes(), children: [props.start ? (_jsx("span", { className: "umds-list-item__start", children: _jsx(_Fragment, { children: props.start }) })) : null, _jsxs("span", { className: "umds-list-item__body", children: [props.title ? (_jsx("span", { className: "umds-list-item__title", children: _jsx(Text, { variant: "body2", as: "span", weight: "semibold", color: "inherit", children: _jsx(_Fragment, { children: props.title }) }) })) : null, props.subtitle ? (_jsx("span", { className: "umds-list-item__subtitle", children: _jsx(Text, { variant: "body2", as: "span", color: "inherit", children: _jsx(_Fragment, { children: props.subtitle }) }) })) : null, props.meta ? (_jsx("span", { className: "umds-list-item__meta", children: _jsx(Text, { variant: "caption", as: "span", color: "inherit", children: _jsx(_Fragment, { children: props.meta }) }) })) : null, _jsx(_Fragment, { children: props.children })] }), props.end ? (_jsx("span", { className: "umds-list-item__end", children: _jsx(_Fragment, { children: props.end }) })) : null] })) : null, props.actions ? (_jsx("div", { className: "umds-list-item__actions", children: _jsx(_Fragment, { children: props.actions }) })) : null] }) })) : (_jsx(_Fragment, { children: _jsxs(_Fragment, { children: [elementTag() === "button" ? (_jsxs("button", { type: "button", id: props.id, title: props.tooltip, disabled: props.disabled, "aria-current": ariaCurrent(), onClick: (event) => handleClick(event), ...htmlAttrs(), className: classes(), children: [props.start ? (_jsx("span", { className: "umds-list-item__start", children: _jsx(_Fragment, { children: props.start }) })) : null, _jsxs("span", { className: "umds-list-item__body", children: [props.title ? (_jsx("span", { className: "umds-list-item__title", children: _jsx(Text, { variant: "body2", as: "span", weight: "semibold", color: "inherit", children: _jsx(_Fragment, { children: props.title }) }) })) : null, props.subtitle ? (_jsx("span", { className: "umds-list-item__subtitle", children: _jsx(Text, { variant: "body2", as: "span", color: "inherit", children: _jsx(_Fragment, { children: props.subtitle }) }) })) : null, props.meta ? (_jsx("span", { className: "umds-list-item__meta", children: _jsx(Text, { variant: "caption", as: "span", color: "inherit", children: _jsx(_Fragment, { children: props.meta }) }) })) : null, _jsx(_Fragment, { children: props.children })] }), props.end ? (_jsx("span", { className: "umds-list-item__end", children: _jsx(_Fragment, { children: props.end }) })) : null] })) : null, elementTag() === "a" ? (_jsxs("a", { id: props.id, title: props.tooltip, href: hrefValue(), target: props.target, rel: relValue(), "aria-current": ariaCurrent(), "aria-disabled": ariaDisabled(), onClick: (event) => handleClick(event), ...htmlAttrs(), className: classes(), children: [props.start ? (_jsx("span", { className: "umds-list-item__start", children: _jsx(_Fragment, { children: props.start }) })) : null, _jsxs("span", { className: "umds-list-item__body", children: [props.title ? (_jsx("span", { className: "umds-list-item__title", children: _jsx(Text, { variant: "body2", as: "span", weight: "semibold", color: "inherit", children: _jsx(_Fragment, { children: props.title }) }) })) : null, props.subtitle ? (_jsx("span", { className: "umds-list-item__subtitle", children: _jsx(Text, { variant: "body2", as: "span", color: "inherit", children: _jsx(_Fragment, { children: props.subtitle }) }) })) : null, props.meta ? (_jsx("span", { className: "umds-list-item__meta", children: _jsx(Text, { variant: "caption", as: "span", color: "inherit", children: _jsx(_Fragment, { children: props.meta }) }) })) : null, _jsx(_Fragment, { children: props.children })] }), props.end ? (_jsx("span", { className: "umds-list-item__end", children: _jsx(_Fragment, { children: props.end }) })) : null] })) : null, elementTag() === "div" ? (_jsxs("div", { id: props.id, title: props.tooltip, role: divRole(), tabIndex: divTabIndex(), "aria-current": ariaCurrent(), "aria-disabled": ariaDisabled(), onClick: (event) => handleClick(event), onKeyDown: (event) => handleKeyDown(event), ...htmlAttrs(), className: classes(), children: [props.start ? (_jsx("span", { className: "umds-list-item__start", children: _jsx(_Fragment, { children: props.start }) })) : null, _jsxs("span", { className: "umds-list-item__body", children: [props.title ? (_jsx("span", { className: "umds-list-item__title", children: _jsx(Text, { variant: "body2", as: "span", weight: "semibold", color: "inherit", children: _jsx(_Fragment, { children: props.title }) }) })) : null, props.subtitle ? (_jsx("span", { className: "umds-list-item__subtitle", children: _jsx(Text, { variant: "body2", as: "span", color: "inherit", children: _jsx(_Fragment, { children: props.subtitle }) }) })) : null, props.meta ? (_jsx("span", { className: "umds-list-item__meta", children: _jsx(Text, { variant: "caption", as: "span", color: "inherit", children: _jsx(_Fragment, { children: props.meta }) }) })) : null, _jsx(_Fragment, { children: props.children })] }), props.end ? (_jsx("span", { className: "umds-list-item__end", children: _jsx(_Fragment, { children: props.end }) })) : null] })) : null] }) })) }));
|
|
81
81
|
}
|
|
82
82
|
export default ListItem;
|
|
@@ -243,8 +243,25 @@
|
|
|
243
243
|
color: var(--umds-semantic-color-feedback-info-dark);
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
+
/* ── secondaryTone: cor da segunda medida (mesmas variantes do valueTone) ── */
|
|
247
|
+
.umds-stat-card--secundario-primary .umds-stat-card__valor-secundario {
|
|
248
|
+
color: var(--umds-semantic-color-primary-pure);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.umds-stat-card--secundario-positive .umds-stat-card__valor-secundario {
|
|
252
|
+
color: var(--umds-semantic-color-feedback-positive-dark);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.umds-stat-card--secundario-negative .umds-stat-card__valor-secundario {
|
|
256
|
+
color: var(--umds-semantic-color-feedback-negative-dark);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.umds-stat-card--secundario-info .umds-stat-card__valor-secundario {
|
|
260
|
+
color: var(--umds-semantic-color-feedback-info-dark);
|
|
261
|
+
}
|
|
262
|
+
|
|
246
263
|
/* ── Segunda medida (secondaryValue, secondaryLabel) ──
|
|
247
|
-
|
|
264
|
+
À direita da principal, separada por um filete. Vem depois do corpo no HTML. */
|
|
248
265
|
.umds-stat-card__secundaria {
|
|
249
266
|
display: flex;
|
|
250
267
|
flex-direction: column;
|
|
@@ -263,8 +280,9 @@
|
|
|
263
280
|
text-align: center;
|
|
264
281
|
}
|
|
265
282
|
|
|
283
|
+
/* Mesmo tamanho e peso do número principal: as duas medidas se comparam lado a lado. */
|
|
266
284
|
.umds-stat-card__valor-secundario {
|
|
267
|
-
font-size: var(--umds-primitive-font-size-
|
|
285
|
+
font-size: var(--umds-primitive-font-size-md);
|
|
268
286
|
font-weight: var(--umds-primitive-font-weight-bold);
|
|
269
287
|
line-height: 1.15;
|
|
270
288
|
font-variant-numeric: tabular-nums;
|
|
@@ -285,6 +303,30 @@
|
|
|
285
303
|
.umds-stat-card--rotulo-topo .umds-stat-card__secundaria {
|
|
286
304
|
grid-column: 3;
|
|
287
305
|
grid-row: 2;
|
|
306
|
+
/* A grade não tem vão entre colunas: o filete ganha o mesmo respiro dos dois lados. */
|
|
307
|
+
margin-left: var(--umds-primitive-spacing-xxxs);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.umds-stat-card--rotulo-topo .umds-stat-card__valor-secundario {
|
|
311
|
+
font-size: var(--umds-primitive-font-size-lg); /* 32px, como o principal */
|
|
312
|
+
font-weight: var(--umds-primitive-font-weight-extrabold);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/* Com rótulo na segunda medida, ela ocupa as linhas do número e da descrição
|
|
316
|
+
(subgrid): o rótulo dela fica na linha da descrição ("Geral" ao lado de
|
|
317
|
+
"Meu Setor") e o valor na linha do número principal. */
|
|
318
|
+
.umds-stat-card--rotulo-topo .umds-stat-card__secundaria:has(.umds-stat-card__rotulo-secundario) {
|
|
319
|
+
grid-row: 2 / span 2;
|
|
320
|
+
display: grid;
|
|
321
|
+
grid-template-rows: subgrid;
|
|
322
|
+
/* `normal` usa o vão da grade do cartão; o `micro` da regra base deslocaria a linha em 1,5px. */
|
|
323
|
+
row-gap: normal;
|
|
324
|
+
align-items: center;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.umds-stat-card--rotulo-topo.umds-stat-card--com-secundaria .umds-stat-card__descricao {
|
|
328
|
+
grid-column: 1 / 3;
|
|
329
|
+
grid-row: 3;
|
|
288
330
|
}
|
|
289
331
|
|
|
290
332
|
.umds-stat-card--rotulo-topo.umds-stat-card--com-secundaria:not(:has(.umds-stat-card__icone)) .umds-stat-card__valor {
|
|
@@ -298,3 +340,16 @@
|
|
|
298
340
|
font-size: 1.125rem; /* 18px */
|
|
299
341
|
font-weight: var(--umds-primitive-font-weight-extrabold);
|
|
300
342
|
}
|
|
343
|
+
|
|
344
|
+
/* A segunda medida acompanha o número principal. */
|
|
345
|
+
.umds-stat-card.umds-stat-card--sm .umds-stat-card__valor-secundario {
|
|
346
|
+
font-size: 1.125rem; /* 18px */
|
|
347
|
+
font-weight: var(--umds-primitive-font-weight-extrabold);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/* ── loading: o Skeleton ocupa a altura do número (1em da fonte dele) ── */
|
|
351
|
+
.umds-stat-card[aria-busy="true"] .umds-stat-card__valor,
|
|
352
|
+
.umds-stat-card[aria-busy="true"] .umds-stat-card__valor-secundario {
|
|
353
|
+
display: inline-flex;
|
|
354
|
+
align-items: center;
|
|
355
|
+
}
|
|
@@ -14,7 +14,7 @@ import './StatCard.css';
|
|
|
14
14
|
* pessoa a segurar o rótulo na cabeça esperando o valor.
|
|
15
15
|
* - **A variação não depende só da cor.** Sobe e desce vêm com seta, porque
|
|
16
16
|
* verde e vermelho sozinhos não comunicam para quem não distingue cores.
|
|
17
|
-
* @property {
|
|
17
|
+
* @property {any} value - O número ou medida em destaque. Aceita um nó, como um `Counter` para o número contar até o valor (no Vue, slot `value`)
|
|
18
18
|
* @property {string} label - O que o número significa
|
|
19
19
|
* @property {IconProp} icon - Ícone Font Awesome como objeto importado
|
|
20
20
|
* @property {string} variant - Cor de apoio do ícone e da variação
|
|
@@ -24,20 +24,22 @@ import './StatCard.css';
|
|
|
24
24
|
* @property {string} description - Texto auxiliar abaixo do rótulo
|
|
25
25
|
* @property {string} size - 'md' (padrão) | 'sm': o `sm` leva o número a 18px/800, o tamanho dos KPIs da home do CrisDoc (fora da escala de fonte do DS, que vai de 16 a 20). Vale também com `labelPosition="top"`.
|
|
26
26
|
* @property {string} labelPosition - Onde o rótulo aparece: 'bottom' (padrão, embaixo do número) | 'top' (no topo do cartão, acima do ícone e do número, com o número em 32/800). No HTML o número continua vindo antes do rótulo, então o leitor de tela anuncia igual.
|
|
27
|
-
* @property {string|number} secondaryValue - Segunda medida do cartão,
|
|
27
|
+
* @property {string|number} secondaryValue - Segunda medida do cartão, no mesmo tamanho da principal, à direita dela ("22,31%" do setor e "14,11%" do geral). Com `labelPosition="top"`, o `secondaryLabel` fica na linha da `description`. Vem depois do resto no HTML. `0` aparece; vazio e ausente não.
|
|
28
28
|
* @property {string} secondaryLabel - O que a segunda medida significa ("Geral")
|
|
29
29
|
* @property {string} valueTone - Cor do número principal: 'default' (cor do texto) | 'primary' | 'positive' | 'negative' | 'info'. Usa as variantes escuras (contraste AA sobre o branco). Não confunda com `variant`, que pinta o ícone e a variação.
|
|
30
|
+
* @property {string} secondaryTone - Cor da segunda medida, com as mesmas opções de `valueTone` ("Meu Setor" em azul e "Geral" em verde, por exemplo).
|
|
31
|
+
* @property {boolean} loading - Carregando: os números viram Skeleton do tamanho deles (rótulos e ⓘ continuam), a variação some e o cartão ganha `aria-busy`. Dispensa montar o Skeleton por fora.
|
|
30
32
|
* @property {string} infoTooltip - Texto de ajuda: mostra um ⓘ ao lado do rótulo com um Tooltip escuro. É um botão dentro do cartão; com `onClick` no cartão, evite (botão dentro de botão).
|
|
31
33
|
* @property {string} infoLabel - Nome acessível do ⓘ. Troque para traduzir. Padrão: "Mais informações".
|
|
32
34
|
* @property {string} align - Alinhamento do conteúdo
|
|
33
35
|
* @property {string} className - Classes CSS adicionais
|
|
34
36
|
* @property {function} onClick - Torna o cartão acionável (vira botão)
|
|
35
37
|
* @quandoUsar Painel e visão geral: destacar UMA medida por cartão, em linha com outras. O número é o protagonista da leitura.
|
|
36
|
-
* @quandoNaoUsar Para listar dados comparáveis — use `DataTable`. Para um bloco com título e conteúdo livre, `Card`. Para um rótulo curto de estado, `Tag` ou `Badge`. Não empilhe vários números num StatCard só:
|
|
38
|
+
* @quandoNaoUsar Para listar dados comparáveis — use `DataTable`. Para um bloco com título e conteúdo livre, `Card`. Para um rótulo curto de estado, `Tag` ou `Badge`. Não empilhe vários números num StatCard só: uma medida, ou duas comparadas (`secondaryValue`).
|
|
37
39
|
*/
|
|
38
40
|
export interface StatCardProps {
|
|
39
|
-
/** O número ou medida em destaque. */
|
|
40
|
-
value?:
|
|
41
|
+
/** O número ou medida em destaque. Texto, número ou nó (um `Counter`); no Vue, slot `value`. */
|
|
42
|
+
value?: any;
|
|
41
43
|
/** O que o número significa. */
|
|
42
44
|
label?: string;
|
|
43
45
|
/** Ícone Font Awesome como objeto importado. */
|
|
@@ -70,7 +72,7 @@ export interface StatCardProps {
|
|
|
70
72
|
* @default 'bottom'
|
|
71
73
|
*/
|
|
72
74
|
labelPosition?: "bottom" | "top";
|
|
73
|
-
/** Segunda medida,
|
|
75
|
+
/** Segunda medida, no tamanho da principal, à direita dela. 0 aparece; vazio e ausente não. */
|
|
74
76
|
secondaryValue?: string | number;
|
|
75
77
|
/** O que a segunda medida significa. */
|
|
76
78
|
secondaryLabel?: string;
|
|
@@ -78,6 +80,10 @@ export interface StatCardProps {
|
|
|
78
80
|
size?: "md" | "sm";
|
|
79
81
|
/** Cor do número principal. @default 'default' */
|
|
80
82
|
valueTone?: "default" | "primary" | "positive" | "negative" | "info";
|
|
83
|
+
/** Cor da segunda medida. Mesmas opções de `valueTone`. @default 'default' */
|
|
84
|
+
secondaryTone?: "default" | "primary" | "positive" | "negative" | "info";
|
|
85
|
+
/** Carregando: números viram Skeleton, a variação some, `aria-busy` no cartão. @default false */
|
|
86
|
+
loading?: boolean;
|
|
81
87
|
/**
|
|
82
88
|
* Texto de ajuda: mostra o ⓘ ao lado do rótulo, com um Tooltip escuro. É um
|
|
83
89
|
* botão focável dentro do cartão; com `onClick` no cartão, evite.
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import './StatCard.css';
|
|
4
4
|
import { faCircleInfo } from "@fortawesome/free-solid-svg-icons";
|
|
5
5
|
import Icon from "../Icon/Icon";
|
|
6
6
|
import Tooltip from "../Tooltip/Tooltip";
|
|
7
|
+
import Skeleton from "../Skeleton/Skeleton";
|
|
7
8
|
function StatCard(props) {
|
|
8
9
|
function classes() {
|
|
9
10
|
return [
|
|
@@ -16,6 +17,9 @@ function StatCard(props) {
|
|
|
16
17
|
props.valueTone && props.valueTone !== "default"
|
|
17
18
|
? "umds-stat-card--valor-" + props.valueTone
|
|
18
19
|
: "",
|
|
20
|
+
props.secondaryTone && props.secondaryTone !== "default"
|
|
21
|
+
? "umds-stat-card--secundario-" + props.secondaryTone
|
|
22
|
+
: "",
|
|
19
23
|
props.onClick ? "umds-stat-card--acionavel" : "",
|
|
20
24
|
props.className,
|
|
21
25
|
]
|
|
@@ -58,6 +62,6 @@ function StatCard(props) {
|
|
|
58
62
|
if (props.onClick)
|
|
59
63
|
props.onClick();
|
|
60
64
|
}
|
|
61
|
-
return (_jsxs("div", { className: classes(), onClick: (event) => aoClicar(), role: ehAcionavel() ? "button" : undefined, tabIndex: ehAcionavel() ? 0 : undefined, children: [props.icon ? (_jsx("span", { className: "umds-stat-card__icone", "aria-hidden": "true", children: _jsx(Icon, { size: "md", variant: "inherit", icon: props.icon }) })) : null, _jsxs("div", { className: "umds-stat-card__corpo", children: [
|
|
65
|
+
return (_jsxs("div", { className: classes(), onClick: (event) => aoClicar(), role: ehAcionavel() ? "button" : undefined, tabIndex: ehAcionavel() ? 0 : undefined, "aria-busy": props.loading ? "true" : undefined, children: [props.icon ? (_jsx("span", { className: "umds-stat-card__icone", "aria-hidden": "true", children: _jsx(Icon, { size: "md", variant: "inherit", icon: props.icon }) })) : null, _jsxs("div", { className: "umds-stat-card__corpo", children: [_jsxs("span", { className: "umds-stat-card__valor", children: [props.loading ? _jsx(Skeleton, { width: "3em", height: "1em" }) : null, !props.loading ? _jsx(_Fragment, { children: props.value }) : null] }), _jsxs("span", { className: "umds-stat-card__rotulo", children: [props.label, props.infoTooltip ? (_jsx(Tooltip, { variant: "dark", side: "top", content: props.infoTooltip, children: _jsx("button", { type: "button", className: "umds-stat-card__info", "aria-label": props.infoLabel || "Mais informações", children: _jsx(Icon, { variant: "inherit", customSize: "14px", icon: faCircleInfo }) }) })) : null] }), props.description ? (_jsx("span", { className: "umds-stat-card__descricao", children: props.description })) : null, props.trendLabel && !props.loading ? (_jsxs("span", { className: classesDaVariacao(), children: [_jsx("span", { "aria-hidden": "true", children: simboloDaVariacao() }), props.trendLabel] })) : null] }), temSecundaria() ? (_jsxs("div", { className: "umds-stat-card__secundaria", children: [_jsxs("span", { className: "umds-stat-card__valor-secundario", children: [props.loading ? _jsx(Skeleton, { width: "3em", height: "1em" }) : null, !props.loading ? _jsx(_Fragment, { children: props.secondaryValue }) : null] }), props.secondaryLabel ? (_jsx("span", { className: "umds-stat-card__rotulo-secundario", children: props.secondaryLabel })) : null] })) : null] }));
|
|
62
66
|
}
|
|
63
67
|
export default StatCard;
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
───────────────────────────────────────────── */
|
|
9
9
|
|
|
10
10
|
.umds-tooltip {
|
|
11
|
+
/* Bloco de contenção da cópia escondida (__descricao, absolute): sem ele,
|
|
12
|
+
ela escapa do overflow de uma área que rola e estica a página. */
|
|
13
|
+
position: relative;
|
|
11
14
|
display: inline-flex;
|
|
12
15
|
height: fit-content;
|
|
13
16
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import './Tooltip.css';
|
|
4
4
|
import { useState, useRef, useEffect } from "react";
|
|
5
|
-
import { computePosition, autoUpdate, offset, flip, shift, } from "@floating-ui/dom";
|
|
5
|
+
import { computePosition, autoUpdate, offset, flip, shift, arrow, } from "@floating-ui/dom";
|
|
6
6
|
import Icon from "../Icon/Icon";
|
|
7
7
|
import Portal from "../Portal/Portal";
|
|
8
8
|
import { gerarId } from "../../utils/id";
|
|
@@ -28,6 +28,7 @@ function Tooltip(props) {
|
|
|
28
28
|
const [posY, setPosY] = useState(() => 0);
|
|
29
29
|
const [finalSide, setFinalSide] = useState(() => "top");
|
|
30
30
|
const [finalAlign, setFinalAlign] = useState(() => "center");
|
|
31
|
+
const [arrowOffset, setArrowOffset] = useState(() => -1);
|
|
31
32
|
const [bubbleId, setBubbleId] = useState(() => "");
|
|
32
33
|
const [descId, setDescId] = useState(() => "");
|
|
33
34
|
function bubbleIdText() {
|
|
@@ -92,6 +93,12 @@ function Tooltip(props) {
|
|
|
92
93
|
padding: 8,
|
|
93
94
|
}),
|
|
94
95
|
];
|
|
96
|
+
// A seta acompanha o gatilho quando o balão é empurrado para caber.
|
|
97
|
+
if (arrowRef.current)
|
|
98
|
+
middlewares.push(arrow({
|
|
99
|
+
element: arrowRef.current,
|
|
100
|
+
padding: 8,
|
|
101
|
+
}));
|
|
95
102
|
computePosition(triggerRef.current, floatingRef.current, {
|
|
96
103
|
placement,
|
|
97
104
|
strategy: "fixed",
|
|
@@ -105,6 +112,14 @@ function Tooltip(props) {
|
|
|
105
112
|
const parts = position.placement.split("-");
|
|
106
113
|
setFinalSide(parts[0]);
|
|
107
114
|
setFinalAlign(parts[1] || "center");
|
|
115
|
+
// Só quando o shift deslocou o balão a seta sai da posição estática
|
|
116
|
+
// (as 12 classes side×align): nos outros casos o visual não muda.
|
|
117
|
+
const deslocamento = position.middlewareData.shift;
|
|
118
|
+
const seta = position.middlewareData.arrow;
|
|
119
|
+
const empurrado = !!deslocamento && (deslocamento.x !== 0 || deslocamento.y !== 0);
|
|
120
|
+
const vertical = parts[0] === "left" || parts[0] === "right";
|
|
121
|
+
const eixo = seta ? (vertical ? seta.y : seta.x) : undefined;
|
|
122
|
+
setArrowOffset(empurrado && typeof eixo === "number" ? eixo : -1);
|
|
108
123
|
setPositioned(true);
|
|
109
124
|
});
|
|
110
125
|
}
|
|
@@ -273,6 +288,22 @@ function Tooltip(props) {
|
|
|
273
288
|
"umds-tooltip__arrow--align-" + finalAlign,
|
|
274
289
|
].join(" ");
|
|
275
290
|
}
|
|
291
|
+
function arrowStyle() {
|
|
292
|
+
if (arrowOffset < 0)
|
|
293
|
+
return undefined;
|
|
294
|
+
if (finalSide === "left" || finalSide === "right") {
|
|
295
|
+
return {
|
|
296
|
+
top: arrowOffset + "px",
|
|
297
|
+
bottom: "auto",
|
|
298
|
+
transform: "rotate(45deg)",
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
left: arrowOffset + "px",
|
|
303
|
+
right: "auto",
|
|
304
|
+
transform: "rotate(45deg)",
|
|
305
|
+
};
|
|
306
|
+
}
|
|
276
307
|
useEffect(() => {
|
|
277
308
|
if (!triggerRef.current)
|
|
278
309
|
return;
|
|
@@ -315,6 +346,6 @@ function Tooltip(props) {
|
|
|
315
346
|
stopKeydownWatch();
|
|
316
347
|
};
|
|
317
348
|
}, []);
|
|
318
|
-
return (_jsxs("span", { ref: triggerRef, className: ["umds-tooltip", props.className].filter(Boolean).join(" "), onMouseEnter: (event) => handleOpen(), onMouseLeave: (event) => handleClose(event), children: [_jsx(_Fragment, { children: props.children }), _jsxs("span", { className: "umds-tooltip__descricao", "aria-hidden": "true", id: descIdText(), children: [props.title ? _jsx(_Fragment, { children: tituloDescrito() }) : null, props.content ? _jsx(_Fragment, { children: props.content }) : null] }), _jsx(Portal, { children: mounted ? (_jsx("span", { ref: floatingRef, className: floatingClasses(), style: floatingStyle(), children: _jsxs("span", { role: "tooltip", className: bubbleClasses(), id: bubbleIdText(), "aria-hidden": !open, children: [_jsxs("span", { className: "umds-tooltip__row", children: [props.icon ? (_jsx(Icon, { size: "sm", variant: "inherit", icon: props.icon })) : null, _jsxs("span", { className: "umds-tooltip__text", children: [props.title ? (_jsx("span", { className: "umds-tooltip__title", children: props.title })) : null, props.content ? (_jsx("span", { className: "umds-tooltip__content", children: _jsx(_Fragment, { children: props.content }) })) : null] })] }), _jsx("span", { ref: arrowRef, className: arrowClasses() })] }) })) : null })] }));
|
|
349
|
+
return (_jsxs("span", { ref: triggerRef, className: ["umds-tooltip", props.className].filter(Boolean).join(" "), onMouseEnter: (event) => handleOpen(), onMouseLeave: (event) => handleClose(event), children: [_jsx(_Fragment, { children: props.children }), _jsxs("span", { className: "umds-tooltip__descricao", "aria-hidden": "true", id: descIdText(), children: [props.title ? _jsx(_Fragment, { children: tituloDescrito() }) : null, props.content ? _jsx(_Fragment, { children: props.content }) : null] }), _jsx(Portal, { children: mounted ? (_jsx("span", { ref: floatingRef, className: floatingClasses(), style: floatingStyle(), children: _jsxs("span", { role: "tooltip", className: bubbleClasses(), id: bubbleIdText(), "aria-hidden": !open, children: [_jsxs("span", { className: "umds-tooltip__row", children: [props.icon ? (_jsx(Icon, { size: "sm", variant: "inherit", icon: props.icon })) : null, _jsxs("span", { className: "umds-tooltip__text", children: [props.title ? (_jsx("span", { className: "umds-tooltip__title", children: props.title })) : null, props.content ? (_jsx("span", { className: "umds-tooltip__content", children: _jsx(_Fragment, { children: props.content }) })) : null] })] }), _jsx("span", { ref: arrowRef, className: arrowClasses(), style: arrowStyle() })] }) })) : null })] }));
|
|
319
350
|
}
|
|
320
351
|
export default Tooltip;
|
package/dist/index.d.ts
CHANGED
|
@@ -50,6 +50,8 @@ export { default as Input } from "./components/Input/Input";
|
|
|
50
50
|
export { default as Kanban } from "./components/Kanban/Kanban";
|
|
51
51
|
export { default as KanbanCard } from "./components/Kanban/KanbanCard";
|
|
52
52
|
export { default as KanbanColumn } from "./components/Kanban/KanbanColumn";
|
|
53
|
+
export { default as Counter } from "./components/Counter/Counter";
|
|
54
|
+
export type * from "./components/Counter/Counter";
|
|
53
55
|
export { default as Kbd } from "./components/Kbd/Kbd";
|
|
54
56
|
export { default as Link } from "./components/Link/Link";
|
|
55
57
|
export { default as LineChart } from "./components/Chart/LineChart";
|
package/dist/index.js
CHANGED
|
@@ -56,6 +56,7 @@ export { default as Input } from "./components/Input/Input";
|
|
|
56
56
|
export { default as Kanban } from "./components/Kanban/Kanban";
|
|
57
57
|
export { default as KanbanCard } from "./components/Kanban/KanbanCard";
|
|
58
58
|
export { default as KanbanColumn } from "./components/Kanban/KanbanColumn";
|
|
59
|
+
export { default as Counter } from "./components/Counter/Counter";
|
|
59
60
|
export { default as Kbd } from "./components/Kbd/Kbd";
|
|
60
61
|
export { default as Link } from "./components/Link/Link";
|
|
61
62
|
export { default as LineChart } from "./components/Chart/LineChart";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import Breadcrumb from "../components/Breadcrumb/Breadcrumb";
|
|
4
|
+
import { render } from "@testing-library/react";
|
|
5
|
+
import { lerArquivo } from "./lerArquivo";
|
|
6
|
+
const cssBreadcrumb = (await lerArquivo("../core/src/components/Breadcrumb/Breadcrumb.css")).replace(/\r\n/g, "\n");
|
|
7
|
+
const bloco = (texto, seletor) => {
|
|
8
|
+
const inicio = texto.indexOf(seletor + " {");
|
|
9
|
+
expect(inicio, seletor).toBeGreaterThanOrEqual(0);
|
|
10
|
+
return texto.slice(inicio, texto.indexOf("}", inicio));
|
|
11
|
+
};
|
|
12
|
+
const ITENS = [
|
|
13
|
+
{ label: "1Doc", href: "#" },
|
|
14
|
+
{ label: "Proc. Administrativo 412/2026", onClick: () => { } },
|
|
15
|
+
{ label: "Reforma da cobertura do Ginásio Poliesportivo Municipal" },
|
|
16
|
+
];
|
|
17
|
+
describe("Breadcrumb · truncate", () => {
|
|
18
|
+
it("truncate escreve a classe na raiz; sem a prop, não", () => {
|
|
19
|
+
const { container } = render(_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: ITENS, truncate: true }), _jsx(Breadcrumb, { items: ITENS })] }));
|
|
20
|
+
expect(container.children[0]).toHaveClass("umds-breadcrumb--truncate");
|
|
21
|
+
expect(container.children[1]).not.toHaveClass("umds-breadcrumb--truncate");
|
|
22
|
+
});
|
|
23
|
+
it("com truncate, o rótulo inteiro vai no title do link, do item clicável e da página atual; sem, nenhum title", () => {
|
|
24
|
+
const { container } = render(_jsx(Breadcrumb, { items: ITENS, truncate: true }));
|
|
25
|
+
expect(container.querySelector("a.umds-breadcrumb__link")).toHaveAttribute("title", "1Doc");
|
|
26
|
+
expect(container.querySelector("[role=button].umds-breadcrumb__link")).toHaveAttribute("title", "Proc. Administrativo 412/2026");
|
|
27
|
+
expect(container.querySelector(".umds-breadcrumb__current")).toHaveAttribute("title", "Reforma da cobertura do Ginásio Poliesportivo Municipal");
|
|
28
|
+
const sem = render(_jsx(Breadcrumb, { items: ITENS }));
|
|
29
|
+
expect(sem.container.querySelectorAll("[title]")).toHaveLength(0);
|
|
30
|
+
});
|
|
31
|
+
it("CSS: uma linha; a página atual encolhe primeiro até 80px; o texto ganha reticências; o padrão segue quebrando linha", () => {
|
|
32
|
+
expect(bloco(cssBreadcrumb, ".umds-breadcrumb--truncate .umds-breadcrumb__list")).toMatch(/flex-wrap:\s*nowrap/);
|
|
33
|
+
const ultimo = bloco(cssBreadcrumb, ".umds-breadcrumb--truncate .umds-breadcrumb__item:last-child");
|
|
34
|
+
expect(ultimo).toMatch(/flex-shrink:\s*100000;/);
|
|
35
|
+
expect(ultimo).toMatch(/min-width:\s*var\(--umds-primitive-spacing-xxl\)/);
|
|
36
|
+
expect(bloco(cssBreadcrumb, ".umds-breadcrumb--truncate .umds-breadcrumb__sep")).toMatch(/flex-shrink:\s*0/);
|
|
37
|
+
expect(cssBreadcrumb).toMatch(/\.umds-breadcrumb--truncate \.umds-breadcrumb__current \{[^}]*text-overflow:\s*ellipsis/);
|
|
38
|
+
expect(bloco(cssBreadcrumb, ".umds-breadcrumb__list")).toMatch(/flex-wrap:\s*wrap/);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
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 { act, fireEvent, render, screen } from "@testing-library/react";
|
|
4
|
+
import Button from "../components/Button/Button";
|
|
5
|
+
import Counter from "../components/Counter/Counter";
|
|
6
|
+
import Dropdown from "../components/Dropdown/Dropdown";
|
|
7
|
+
import ListItem from "../components/List/ListItem";
|
|
8
|
+
import StatCard from "../components/StatCard/StatCard";
|
|
9
|
+
describe("Counter", () => {
|
|
10
|
+
it("sem animação mostra o valor final formatado em pt-BR", () => {
|
|
11
|
+
const { container } = render(_jsx(Counter, { value: 1284, disableAnimation: true }));
|
|
12
|
+
expect(container.querySelector(".umds-counter__numero")).toHaveTextContent("1.284");
|
|
13
|
+
expect(container.querySelector(".umds-counter__numero")).toHaveAttribute("aria-hidden", "true");
|
|
14
|
+
expect(container.querySelector(".umds-counter__leitura")).toHaveTextContent("1.284");
|
|
15
|
+
});
|
|
16
|
+
it("casas decimais seguem o value; prefixo e sufixo entram no texto", () => {
|
|
17
|
+
const { container } = render(_jsxs(_Fragment, { children: [_jsx(Counter, { value: 22.31, suffix: "%", disableAnimation: true }), _jsx(Counter, { value: 15890.5, prefix: "R$ ", decimals: 2, disableAnimation: true })] }));
|
|
18
|
+
const leituras = container.querySelectorAll(".umds-counter__leitura");
|
|
19
|
+
expect(leituras[0]).toHaveTextContent("22,31%");
|
|
20
|
+
expect(leituras[1].textContent).toBe("R$ 15.890,50");
|
|
21
|
+
});
|
|
22
|
+
it("conta do from até o valor e termina no valor exato", async () => {
|
|
23
|
+
vi.useFakeTimers();
|
|
24
|
+
const { container } = render(_jsx(Counter, { value: 500, duration: 100 }));
|
|
25
|
+
const numero = () => container.querySelector(".umds-counter__numero").textContent;
|
|
26
|
+
expect(numero()).toBe("0");
|
|
27
|
+
await act(async () => {
|
|
28
|
+
vi.advanceTimersByTime(500);
|
|
29
|
+
});
|
|
30
|
+
expect(numero()).toBe("500");
|
|
31
|
+
// A reserva e a leitura já nascem com o valor final.
|
|
32
|
+
expect(container.querySelector(".umds-counter__leitura")).toHaveTextContent("500");
|
|
33
|
+
vi.useRealTimers();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe("ListItem · actions", () => {
|
|
37
|
+
it("as ações ficam no li, fora do elemento clicável", () => {
|
|
38
|
+
const { container } = render(_jsx("ul", { children: _jsx(ListItem, { href: "#doc", title: "Memorando", actions: _jsx("button", { type: "button", children: "Mais" }) }) }));
|
|
39
|
+
const acao = screen.getByRole("button", { name: "Mais" });
|
|
40
|
+
expect(acao.closest("a")).toBeNull();
|
|
41
|
+
expect(acao.parentElement).toHaveClass("umds-list-item__actions");
|
|
42
|
+
expect(container.querySelector("li > a")).toHaveAttribute("href", "#doc");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe("Button · href", () => {
|
|
46
|
+
it("com href é um link; desabilitado volta a ser botão", () => {
|
|
47
|
+
render(_jsxs(_Fragment, { children: [_jsx(Button, { href: "#inbox", children: "Inbox" }), _jsx(Button, { href: "#cal", target: "_blank", children: "Calend\u00E1rio" }), _jsx(Button, { href: "#fila", disabled: true, children: "Fila" })] }));
|
|
48
|
+
expect(screen.getByRole("link", { name: "Inbox" })).toHaveAttribute("href", "#inbox");
|
|
49
|
+
expect(screen.getByRole("link", { name: "Calendário" })).toHaveAttribute("rel", "noopener noreferrer");
|
|
50
|
+
expect(screen.getByRole("button", { name: "Fila" })).toBeDisabled();
|
|
51
|
+
});
|
|
52
|
+
it("loading segura a navegação do link", () => {
|
|
53
|
+
render(_jsx(Button, { href: "#inbox", loading: true, loadingPosition: "start", children: "Inbox" }));
|
|
54
|
+
const link = screen.getByRole("link", { name: "Inbox" });
|
|
55
|
+
const evento = new MouseEvent("click", { bubbles: true, cancelable: true });
|
|
56
|
+
link.dispatchEvent(evento);
|
|
57
|
+
expect(evento.defaultPrevented).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe("Dropdown · href", () => {
|
|
61
|
+
it("item com href vira <a role=menuitem>, e o onSelect continua", async () => {
|
|
62
|
+
const onSelect = vi.fn();
|
|
63
|
+
render(_jsx(Dropdown, { ariaLabel: "A\u00E7\u00F5es", onSelect: onSelect, items: [
|
|
64
|
+
{ key: "assinar", label: "Assinar", href: "#assinar" },
|
|
65
|
+
{ key: "arquivar", label: "Arquivar" },
|
|
66
|
+
], children: _jsx("button", { type: "button", children: "Abrir" }) }));
|
|
67
|
+
fireEvent.click(screen.getByRole("button", { name: "Abrir" }));
|
|
68
|
+
const assinar = await screen.findByRole("menuitem", { name: "Assinar" });
|
|
69
|
+
expect(assinar.tagName).toBe("A");
|
|
70
|
+
expect(assinar).toHaveAttribute("href", "#assinar");
|
|
71
|
+
expect(screen.getByRole("menuitem", { name: "Arquivar" }).tagName).toBe("BUTTON");
|
|
72
|
+
fireEvent.click(assinar);
|
|
73
|
+
expect(onSelect).toHaveBeenCalledWith(expect.objectContaining({ key: "assinar" }));
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe("StatCard · loading e secondaryTone", () => {
|
|
77
|
+
it("loading troca os números por Skeleton e marca aria-busy", () => {
|
|
78
|
+
const { container } = render(_jsx(StatCard, { value: "22,31%", label: "Qualidade", secondaryValue: "14,11%", trendLabel: "+2%", trend: "up", loading: true }));
|
|
79
|
+
const cartao = container.querySelector(".umds-stat-card");
|
|
80
|
+
expect(cartao).toHaveAttribute("aria-busy", "true");
|
|
81
|
+
expect(cartao.textContent).not.toContain("22,31%");
|
|
82
|
+
expect(cartao.textContent).not.toContain("14,11%");
|
|
83
|
+
expect(cartao.textContent).not.toContain("+2%");
|
|
84
|
+
expect(cartao.textContent).toContain("Qualidade");
|
|
85
|
+
});
|
|
86
|
+
it("secondaryTone escreve a classe; default não", () => {
|
|
87
|
+
const { container } = render(_jsxs(_Fragment, { children: [_jsx(StatCard, { value: "1", secondaryValue: "2", secondaryTone: "positive" }), _jsx(StatCard, { value: "1", secondaryValue: "2", secondaryTone: "default" })] }));
|
|
88
|
+
expect(container.children[0]).toHaveClass("umds-stat-card--secundario-positive");
|
|
89
|
+
expect(container.children[1].className).not.toContain("secundario-");
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -23,6 +23,14 @@ describe("StatCard · size", () => {
|
|
|
23
23
|
const { container } = render(_jsx(StatCard, { value: 44, label: "N\u00E3o lidos", size: "sm", labelPosition: "top" }));
|
|
24
24
|
expect(container.firstElementChild).toHaveClass("umds-stat-card--sm", "umds-stat-card--rotulo-topo");
|
|
25
25
|
});
|
|
26
|
+
it("a segunda medida tem o mesmo tamanho e peso do número, no sm e no rótulo no topo", () => {
|
|
27
|
+
const sm = bloco(".umds-stat-card.umds-stat-card--sm .umds-stat-card__valor-secundario");
|
|
28
|
+
expect(sm).toMatch(/font-size:\s*1\.125rem/);
|
|
29
|
+
expect(sm).toMatch(/font-weight:\s*var\(--umds-primitive-font-weight-extrabold\)/);
|
|
30
|
+
const topo = bloco(".umds-stat-card--rotulo-topo .umds-stat-card__valor-secundario");
|
|
31
|
+
expect(topo).toMatch(/font-size:\s*var\(--umds-primitive-font-size-lg\)/);
|
|
32
|
+
expect(topo).toMatch(/font-weight:\s*var\(--umds-primitive-font-weight-extrabold\)/);
|
|
33
|
+
});
|
|
26
34
|
it("o número continua antes do rótulo no HTML", () => {
|
|
27
35
|
const { container } = render(_jsx(StatCard, { value: 44, label: "N\u00E3o lidos", size: "sm" }));
|
|
28
36
|
const valor = container.querySelector(".umds-stat-card__valor");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1doc/1ds-react",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"sideEffects": [
|
|
6
6
|
"**/*.css"
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"react-dom": "^18.0.0",
|
|
46
46
|
"typescript": "^5.0.0",
|
|
47
47
|
"vitest": "^3.2.7",
|
|
48
|
-
"@1doc/1ds-core": "2.
|
|
48
|
+
"@1doc/1ds-core": "2.14.0",
|
|
49
49
|
"@1doc/1ds-tokens": "0.7.0"
|
|
50
50
|
},
|
|
51
51
|
"publishConfig": {
|
package/src/index.ts
CHANGED
|
@@ -58,6 +58,8 @@ export { default as Input } from "./components/Input/Input";
|
|
|
58
58
|
export { default as Kanban } from "./components/Kanban/Kanban";
|
|
59
59
|
export { default as KanbanCard } from "./components/Kanban/KanbanCard";
|
|
60
60
|
export { default as KanbanColumn } from "./components/Kanban/KanbanColumn";
|
|
61
|
+
export { default as Counter } from "./components/Counter/Counter";
|
|
62
|
+
export type * from "./components/Counter/Counter";
|
|
61
63
|
export { default as Kbd } from "./components/Kbd/Kbd";
|
|
62
64
|
export { default as Link } from "./components/Link/Link";
|
|
63
65
|
export { default as LineChart } from "./components/Chart/LineChart";
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import Breadcrumb from "../components/Breadcrumb/Breadcrumb";
|
|
3
|
+
import { render } from "@testing-library/react";
|
|
4
|
+
import { lerArquivo } from "./lerArquivo";
|
|
5
|
+
|
|
6
|
+
const cssBreadcrumb = (await lerArquivo("../core/src/components/Breadcrumb/Breadcrumb.css")).replace(/\r\n/g, "\n");
|
|
7
|
+
const bloco = (texto: string, seletor: string) => {
|
|
8
|
+
const inicio = texto.indexOf(seletor + " {");
|
|
9
|
+
expect(inicio, seletor).toBeGreaterThanOrEqual(0);
|
|
10
|
+
return texto.slice(inicio, texto.indexOf("}", inicio));
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const ITENS = [
|
|
14
|
+
{ label: "1Doc", href: "#" },
|
|
15
|
+
{ label: "Proc. Administrativo 412/2026", onClick: () => {} },
|
|
16
|
+
{ label: "Reforma da cobertura do Ginásio Poliesportivo Municipal" },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
describe("Breadcrumb · truncate", () => {
|
|
20
|
+
it("truncate escreve a classe na raiz; sem a prop, não", () => {
|
|
21
|
+
const { container } = render(
|
|
22
|
+
<>
|
|
23
|
+
<Breadcrumb items={ITENS} truncate />
|
|
24
|
+
<Breadcrumb items={ITENS} />
|
|
25
|
+
</>,
|
|
26
|
+
);
|
|
27
|
+
expect(container.children[0]).toHaveClass("umds-breadcrumb--truncate");
|
|
28
|
+
expect(container.children[1]).not.toHaveClass("umds-breadcrumb--truncate");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("com truncate, o rótulo inteiro vai no title do link, do item clicável e da página atual; sem, nenhum title", () => {
|
|
32
|
+
const { container } = render(<Breadcrumb items={ITENS} truncate />);
|
|
33
|
+
expect(container.querySelector("a.umds-breadcrumb__link")).toHaveAttribute("title", "1Doc");
|
|
34
|
+
expect(container.querySelector("[role=button].umds-breadcrumb__link")).toHaveAttribute("title", "Proc. Administrativo 412/2026");
|
|
35
|
+
expect(container.querySelector(".umds-breadcrumb__current")).toHaveAttribute(
|
|
36
|
+
"title",
|
|
37
|
+
"Reforma da cobertura do Ginásio Poliesportivo Municipal",
|
|
38
|
+
);
|
|
39
|
+
const sem = render(<Breadcrumb items={ITENS} />);
|
|
40
|
+
expect(sem.container.querySelectorAll("[title]")).toHaveLength(0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("CSS: uma linha; a página atual encolhe primeiro até 80px; o texto ganha reticências; o padrão segue quebrando linha", () => {
|
|
44
|
+
expect(bloco(cssBreadcrumb, ".umds-breadcrumb--truncate .umds-breadcrumb__list")).toMatch(/flex-wrap:\s*nowrap/);
|
|
45
|
+
const ultimo = bloco(cssBreadcrumb, ".umds-breadcrumb--truncate .umds-breadcrumb__item:last-child");
|
|
46
|
+
expect(ultimo).toMatch(/flex-shrink:\s*100000;/);
|
|
47
|
+
expect(ultimo).toMatch(/min-width:\s*var\(--umds-primitive-spacing-xxl\)/);
|
|
48
|
+
expect(bloco(cssBreadcrumb, ".umds-breadcrumb--truncate .umds-breadcrumb__sep")).toMatch(/flex-shrink:\s*0/);
|
|
49
|
+
expect(cssBreadcrumb).toMatch(/\.umds-breadcrumb--truncate \.umds-breadcrumb__current \{[^}]*text-overflow:\s*ellipsis/);
|
|
50
|
+
expect(bloco(cssBreadcrumb, ".umds-breadcrumb__list")).toMatch(/flex-wrap:\s*wrap/);
|
|
51
|
+
});
|
|
52
|
+
});
|