@block-auth.io/angular 0.2.10 → 0.3.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.
@@ -1,37 +1,40 @@
1
-
2
- > @block-auth.io/angular@0.2.10 build /home/darius_jvc/projects/blockauth-sdk/packages/angular
3
- > pnpm build:css && ng-packagr -p ng-package.json -c tsconfig.lib.json
4
-
5
-
6
- > @block-auth.io/angular@0.2.10 build:css /home/darius_jvc/projects/blockauth-sdk/packages/angular
7
- > tailwindcss -i ../ui-styles/src/input.css -c ./tailwind.config.cjs -o ./src/styles.generated.css --minify && node scripts/css-to-ts.mjs
8
-
9
- Browserslist: caniuse-lite is outdated. Please run:
10
- npx update-browserslist-db@latest
11
- Why you should do it regularly: https://github.com/browserslist/update-db#readme
12
-
13
- Rebuilding...
14
-
15
- Done in 628ms.
16
- Building Angular Package
17
- 
18
- ------------------------------------------------------------------------------
19
- Building entry point '@block-auth.io/angular'
20
- ------------------------------------------------------------------------------
21
- ⠋ Compiling with Angular sources in partial compilation mode.⠙ Compiling with Angular sources in partial compilation mode.
22
- ✔ Compiling with Angular sources in partial compilation mode.
23
- ✔ Generating FESM and DTS bundles
24
- ⠋ Copying assets✔ Copying assets
25
- ⠋ Writing package manifestℹ Removing scripts section in package.json as it's considered a potential security vulnerability.
26
- ℹ Removing devDependencies section in package.json.
27
- ✔ Writing package manifest
28
- ✔ Built @block-auth.io/angular
29
- 
30
- ------------------------------------------------------------------------------
31
- Built Angular Package
32
- - from: /home/darius_jvc/projects/blockauth-sdk/packages/angular
33
- - to: /home/darius_jvc/projects/blockauth-sdk/packages/angular/dist
34
- ------------------------------------------------------------------------------
35
- 
36
- Build at: 2026-10-04T12:15:58.546Z - Time: 1297ms
37
- 
1
+
2
+ > @block-auth.io/angular@0.3.0 build /home/vlad/Escritorio/blockauth-code/blockauth-sdk/packages/angular
3
+ > pnpm build:css && ng-packagr -p ng-package.json -c tsconfig.lib.json
4
+
5
+
6
+ > @block-auth.io/angular@0.3.0 build:css /home/vlad/Escritorio/blockauth-code/blockauth-sdk/packages/angular
7
+ > tailwindcss -i ../ui-styles/src/input.css -c ./tailwind.config.cjs -o ./src/styles.generated.css --minify && node scripts/css-to-ts.mjs
8
+
9
+ Browserslist: caniuse-lite is outdated. Please run:
10
+ npx update-browserslist-db@latest
11
+ Why you should do it regularly: https://github.com/browserslist/update-db#readme
12
+
13
+ Rebuilding...
14
+
15
+ Done in 2518ms.
16
+ Building Angular Package
17
+
18
+ ------------------------------------------------------------------------------
19
+ Building entry point '@block-auth.io/angular'
20
+ ------------------------------------------------------------------------------
21
+ - Compiling with Angular sources in partial compilation mode.
22
+
23
+ ✔ Compiling with Angular sources in partial compilation mode.
24
+ ✔ Generating FESM and DTS bundles
25
+ - Copying assets
26
+ ✔ Copying assets
27
+ - Writing package manifest
28
+ ℹ Removing scripts section in package.json as it's considered a potential security vulnerability.
29
+ ℹ Removing devDependencies section in package.json.
30
+ ✔ Writing package manifest
31
+ ✔ Built @block-auth.io/angular
32
+
33
+ ------------------------------------------------------------------------------
34
+ Built Angular Package
35
+ - from: /home/vlad/Escritorio/blockauth-code/blockauth-sdk/packages/angular
36
+ - to: /home/vlad/Escritorio/blockauth-code/blockauth-sdk/packages/angular/dist
37
+ ------------------------------------------------------------------------------
38
+
39
+ Build at: 2026-10-15T00:14:24.516Z - Time: 9555ms
40
+
@@ -0,0 +1,4 @@
1
+
2
+ > @block-auth.io/angular@0.3.0 lint /home/vlad/Escritorio/blockauth-code/blockauth-sdk/packages/angular
3
+ > eslint src
4
+
@@ -0,0 +1,102 @@
1
+
2
+ > @block-auth.io/angular@0.3.0 pretest /home/vlad/Escritorio/blockauth-code/blockauth-sdk/packages/angular
3
+ > pnpm build:css
4
+
5
+
6
+ > @block-auth.io/angular@0.3.0 build:css /home/vlad/Escritorio/blockauth-code/blockauth-sdk/packages/angular
7
+ > tailwindcss -i ../ui-styles/src/input.css -c ./tailwind.config.cjs -o ./src/styles.generated.css --minify && node scripts/css-to-ts.mjs
8
+
9
+ Browserslist: caniuse-lite is outdated. Please run:
10
+ npx update-browserslist-db@latest
11
+ Why you should do it regularly: https://github.com/browserslist/update-db#readme
12
+
13
+ Rebuilding...
14
+
15
+ Done in 5726ms.
16
+
17
+ > @block-auth.io/angular@0.3.0 test /home/vlad/Escritorio/blockauth-code/blockauth-sdk/packages/angular
18
+ > vitest run
19
+
20
+
21
+ RUN v3.2.7 /home/vlad/Escritorio/blockauth-code/blockauth-sdk/packages/angular
22
+
23
+ ✓ src/lib/components/dds/dropdown-profile-item.component.test.ts (5 tests) 184ms
24
+ ✓ src/lib/components/modals/modal-overlay.component.test.ts (4 tests) 287ms
25
+ ✓ src/lib/components/txns/internals/txn-error.component.test.ts (5 tests) 232ms
26
+ ✓ src/lib/components/modals/modal-restoration-expired.component.test.ts (4 tests) 329ms
27
+ ✓ src/lib/components/modals/modal-restore.component.test.ts (4 tests) 530ms
28
+ ✓ src/lib/components/dds/dropdown-profile.component.test.ts (6 tests) 497ms
29
+ ✓ src/lib/components/modals/modal-signin.component.test.ts (3 tests) 268ms
30
+ ✓ src/lib/runtime/block-auth-runtime.directive.test.ts (4 tests) 222ms
31
+ ✓ src/lib/components/btns/btn-block-auth.component.test.ts (4 tests) 556ms
32
+ ✓ BtnBlockAuth > renderiza con el id de provider y es clickable 364ms
33
+ ✓ src/lib/components/flow-block-auth.component.test.ts (3 tests) 688ms
34
+ ✓ FlowBlockAuth > renderiza el contenedor del orquestador sin romperse 405ms
35
+ ✓ src/lib/block-auth.service.test.ts (2 tests) 449ms
36
+ ✓ BlockAuthService > expone signals reactivos del estado del core y las acciones 400ms
37
+ ✓ src/lib/components/contents/content-signup.component.test.ts (3 tests) 752ms
38
+ ✓ ContentSignup > renderiza el contenido de signup 503ms
39
+ ✓ src/lib/components/dds/dropdown-profile-btn.component.test.ts (2 tests) 336ms
40
+ ✓ src/lib/primitives/lifetime-slider-base.component.test.ts (5 tests) 1370ms
41
+ ✓ LifetimeSliderBaseComponent > renderiza el slider (SliderBase) y el input del TimePicker con el valor HH:MM:SS 414ms
42
+ ✓ LifetimeSliderBaseComponent > emite toggleTimerDropdown al pulsar el botón del picker 351ms
43
+ ✓ src/lib/primitives/dropdown-base.component.test.ts (4 tests) 837ms
44
+ ✓ DropdownBaseComponent > al pulsar el boton emite isOpenChange con el valor invertido 699ms
45
+ ✓ src/lib/components/txns/txns-overview.component.test.ts (20 tests) 2756ms
46
+ ✓ TxnsOverview > con datos solicitados > al guardar vacío un dato cedido deja de cederlo sin enviarlo 378ms
47
+ ✓ src/lib/components/txns/internals/solicited-data-modal.component.test.ts (30 tests) 2736ms
48
+ ✓ SolicitedDataModal > renderiza el modal de datos solicitados con campos requeridos y opcionales 436ms
49
+ ✓ SolicitedDataModal > emite paramChange al escribir y validationChange según validez 306ms
50
+ ✓ src/lib/components/dds/change-account.component.test.ts (8 tests) 607ms
51
+ ✓ src/lib/components/txns/internals/txn-type.component.test.ts (3 tests) 229ms
52
+ ✓ src/lib/components/txns/internals/txn.component.test.ts (29 tests) 3829ms
53
+ ✓ Txn > el boton de configurar sesion revela el LifetimeSliderBase (slider + TimePicker) 328ms
54
+ ✓ Txn > "Quiero configurar el tiempo de mi sesión" abre el selector en un popup 518ms
55
+ ✓ Txn > Aceptar cierra el popup y conserva el tiempo elegido junto al enlace 623ms
56
+ ✓ Txn > cerrar con la x o fuera del popup conserva el tiempo elegido 307ms
57
+ ✓ src/lib/components/txns/internals/phone-input.component.test.ts (7 tests) 3141ms
58
+ ✓ PhoneInputComponent > busca y selecciona un país con el teclado y cierra con Escape 1012ms
59
+ ✓ PhoneInputComponent > mueve la opción activa con las flechas 1252ms
60
+ ✓ PhoneInputComponent > avisa si no hay países y usa el idioma activo 361ms
61
+ ✓ src/lib/primitives/btn-base.component.test.ts (3 tests) 277ms
62
+ ✓ src/lib/components/sections/login-section.component.test.ts (2 tests) 303ms
63
+ ✓ src/lib/provide-block-auth.test.ts (3 tests) 312ms
64
+ ✓ src/lib/primitives/arrow-icon.component.test.ts (2 tests) 203ms
65
+ ✓ src/lib/components/modals/modal-signup.component.test.ts (3 tests) 411ms
66
+ ✓ src/lib/primitives/btn-arrow-dir.component.test.ts (2 tests) 247ms
67
+ ✓ src/lib/__tests__/integration.test.ts (1 test) 853ms
68
+ ✓ integración Angular + core > conectar wallet propaga la address al estado consumido por Angular 851ms
69
+ ✓ src/lib/components/btns/btn-signin.component.test.ts (4 tests) 564ms
70
+ ✓ BtnSignin > renderiza con el id correcto 375ms
71
+ ✓ src/lib/teleport-body.directive.test.ts (2 tests) 96ms
72
+ ✓ src/lib/components/contents/content-signin.component.test.ts (2 tests) 133ms
73
+ ✓ src/lib/components/sections/internals/selected-button.component.test.ts (1 test) 140ms
74
+ ✓ src/lib/primitives/horizontal-loader.component.test.ts (2 tests) 134ms
75
+ ✓ src/lib/primitives/input-base.component.test.ts (2 tests) 152ms
76
+ ✓ src/lib/icons/icons.test.ts (17 tests) 431ms
77
+ ✓ src/lib/components/txns/internals/permissions-required-modal.component.test.ts (3 tests) 194ms
78
+ ✓ src/lib/primitives/icon-base.component.test.ts (2 tests) 146ms
79
+ ✓ src/lib/components/btns/btn-base-provider.component.test.ts (1 test) 161ms
80
+ ✓ src/lib/components/sections/internals/providers-buttons.component.test.ts (1 test) 200ms
81
+ ✓ src/lib/primitives/progressbar.component.test.ts (2 tests) 123ms
82
+ ✓ src/lib/inject-store.test.ts (1 test) 124ms
83
+ ✓ src/lib/components/sections/footer-section.component.test.ts (1 test) 183ms
84
+ ✓ src/lib/components/sections/internals/providers-filters.component.test.ts (1 test) 122ms
85
+ ✓ src/lib/primitives/img-loader.component.test.ts (1 test) 134ms
86
+ ✓ src/lib/primitives/dropdown-arrow.component.test.ts (1 test) 88ms
87
+ ✓ src/lib/primitives/divider-base.component.test.ts (1 test) 123ms
88
+ ✓ src/lib/components/btns/btn-metamask.component.test.ts (1 test) 125ms
89
+ ✓ src/lib/primitives/oval-spinner.component.test.ts (1 test) 71ms
90
+ ✓ src/lib/components/btns/btn-phantom.component.test.ts (1 test) 151ms
91
+ ✓ src/lib/components/btns/btn-wallet-connect.component.test.ts (1 test) 88ms
92
+ ✓ src/lib/components/sections/providers-section.component.test.ts (1 test) 97ms
93
+ ✓ src/lib/components/btns/btn-microsoft.component.test.ts (1 test) 93ms
94
+ ✓ src/lib/components/btns/btn-gmail.component.test.ts (1 test) 82ms
95
+ ✓ src/lib/components/btns/btn-google-base.component.test.ts (1 test) 75ms
96
+
97
+ Test Files 54 passed (54)
98
+ Tests 224 passed (224)
99
+ Start at 02:15:05
100
+ Duration 16.65s (transform 11.13s, setup 48.88s, collect 110.62s, tests 27.47s, environment 23.55s, prepare 32.88s)
101
+
102
+  ELIFECYCLE  Test failed. See above for more details.
@@ -0,0 +1,19 @@
1
+
2
+ > @block-auth.io/angular@0.3.0 pretypecheck /home/vlad/Escritorio/blockauth-code/blockauth-sdk/packages/angular
3
+ > pnpm build:css
4
+
5
+
6
+ > @block-auth.io/angular@0.3.0 build:css /home/vlad/Escritorio/blockauth-code/blockauth-sdk/packages/angular
7
+ > tailwindcss -i ../ui-styles/src/input.css -c ./tailwind.config.cjs -o ./src/styles.generated.css --minify && node scripts/css-to-ts.mjs
8
+
9
+ Browserslist: caniuse-lite is outdated. Please run:
10
+ npx update-browserslist-db@latest
11
+ Why you should do it regularly: https://github.com/browserslist/update-db#readme
12
+
13
+ Rebuilding...
14
+
15
+ Done in 2017ms.
16
+
17
+ > @block-auth.io/angular@0.3.0 typecheck /home/vlad/Escritorio/blockauth-code/blockauth-sdk/packages/angular
18
+ > tsc -p tsconfig.lib.json --noEmit
19
+
package/CHANGELOG.md CHANGED
@@ -1,5 +1,118 @@
1
1
  # @block-auth.io/angular
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - cf32d5e: El teléfono de los datos solicitados se valida con `libphonenumber-js`
8
+ (1.13.14, metadatos `min`) y se envía en `userParams['users-phone']` en formato
9
+ E.164 (`+34634343343`). `@block-auth.io/core` exporta utilidades de teléfono
10
+ independientes del framework: `getPhoneCountries(locale)` (ISO2, prefijo,
11
+ bandera emoji y nombre localizado con `Intl.DisplayNames`),
12
+ `searchPhoneCountries`, `getPhoneCountryFlag`, `getPhoneDialCode`,
13
+ `getDefaultPhoneCountry` (región del idioma del navegador o `ES`),
14
+ `parsePhoneValue`, `formatPhoneForDisplay` (internacional, o el texto tal cual
15
+ si no se entiende), `isValidPhoneValue`, `toPhoneE164`, `buildPhoneE164`,
16
+ `buildPhoneValue`, `updatePhoneDraft` y `resolvePhoneDraft`. Se acepta también
17
+ el formato antiguo `34_634343343_es`. `solicitedData` añade
18
+ `PARAM_NORMALIZERS`, `normalizeParamValue`, `PARAM_DISPLAY_FORMATTERS` y
19
+ `formatParamValue`, y no marca como cambiado el mismo número escrito en otro
20
+ formato. Nuevos textos `phoneCountry`, `searchPhoneCountry` y
21
+ `noPhoneCountryResults`.
22
+
23
+ En React, Vue y Angular el campo de teléfono de `SolicitedDataModal` es un
24
+ selector de país (bandera y prefijo, con lista de países buscable por nombre,
25
+ código o prefijo, navegable con flechas, Enter para elegir y Escape para
26
+ cerrar) más el número nacional. El país por defecto es el del valor existente,
27
+ si no la región del idioma del navegador y si no España.
28
+ Nuevos componentes internos `PhoneInput` (React y Vue) y `ba-phone-input`
29
+ (Angular). `@block-auth.io/ui-styles` añade las clases `bal-ba-glass-phone`,
30
+ `bal-ba-glass-phone-row`, `bal-ba-glass-phone-country`,
31
+ `bal-ba-glass-phone-flag`, `bal-ba-glass-phone-chevron`,
32
+ `bal-ba-glass-phone-menu`, `bal-ba-glass-phone-search`,
33
+ `bal-ba-glass-phone-list`, `bal-ba-glass-phone-option(-active|-name|-dial)` y
34
+ `bal-ba-glass-phone-empty`.
35
+
36
+ - df80cbe: Los datos solicitados de un login se piden al servidor. Nuevo
37
+ `client.txns.dataRequest(txnId)`: emite `user:txns:data` y resuelve con los
38
+ datos que pide la app (`slug`, `mode`, `status`, `value`), o `null` si el
39
+ servidor falla o no responde a tiempo. `client.txns.accept` acepta
40
+ `grantedParams` y `revokedParams` y los envía en `provider:signin`. Si el servidor rechaza la
41
+ aceptación por datos obligatorios ausentes, no autorizados o inválidos, el
42
+ login sigue abierto con `$ui.acceptTxnError` en lugar de llamar a `_onError`.
43
+ Nuevos tipos `TxnDataParam` y `TxnDataRequest`, y textos
44
+ `completeRequiredData`, `requiredDataError`, `shareData`, `saveData`,
45
+ `undoChange`, `savedToProfileHint`, `pendingChangeHint`, `notSharedHint`,
46
+ `clearField` y `acceptData`.
47
+
48
+ `TxnsOverview` usa `dataRequest` al seleccionar la transacción y, si devuelve
49
+ `null`, mantiene el cálculo con `uiConfig`. `SolicitedDataModal` muestra todos
50
+ los datos que pide la app como campos rellenos con el valor del perfil, cada
51
+ uno editable con una casilla "Ceder" (marcada por defecto en los `shared` y
52
+ `granted`, desmarcada en `consent`/`missing` salvo al escribir un valor válido
53
+ en un campo vacío), un botón "x" para limpiar el campo cuando
54
+ tiene contenido y, si el valor cambia, la decisión "Guardar" / "Deshacer".
55
+ Guardar un valor nuevo marca "Ceder"; si después se desmarca, el valor no se
56
+ envía y se indica que no se cederá. La lógica es pura y se exporta desde
57
+ `@block-auth.io/core` para los tres frameworks (`getSolicitedParams`,
58
+ `getSolicitedFields`, `getSolicitedAcceptParams`, `getSolicitedHintKey`,
59
+ `isSolicitedHintSuccess`, `isSolicitedDataComplete`, `setSolicitedValue`,
60
+ `setSolicitedShared`, `saveSolicitedValue`, `undoSolicitedValue` y las
61
+ constantes `PARAM_*`). ACEPTAR
62
+ queda deshabilitado mientras falte un obligatorio por ceder o haya un cambio
63
+ cedido sin decidir, y envía `userParams` con los valores nuevos guardados,
64
+ `grantedParams` con los valores del perfil `consent`/`granted` cedidos y
65
+ `revokedParams` con los opcionales `shared` o `granted` desmarcados. Un dato
66
+ `shared` cedido sin cambios no se envía. `SolicitedDataModal` acepta `params`,
67
+ `sharedParams`, `handleShareChange`, `savedParams`, `handleSaveParam`,
68
+ `handleUndoParam` y `errorTxt`; `Txn` acepta `grantedParams`, `revokedParams`
69
+ y `solicitedDataPending`.
70
+
71
+ En Vue, `TxnsOverview`, `Txn` y `SolicitedDataModal` replican el mismo
72
+ comportamiento: `SolicitedDataModal` acepta `params`, `sharedParams`,
73
+ `savedParams` y `errorTxt`, y emite `shareChange`, `saveParam` y `undoParam`;
74
+ `Txn` acepta `grantedParams`, `revokedParams` y `solicitedDataPending`.
75
+
76
+ En Angular, `TxnsOverviewComponent`, `TxnComponent` y
77
+ `SolicitedDataModalComponent` replican el mismo comportamiento y diseño:
78
+ `ba-solicited-data-modal`
79
+ acepta `params`, `sharedParams`, `savedParams` y `errorTxt` y emite
80
+ `shareChange` (`ShareChange`), `saveParam` y `undoParam`, y `ba-txn` acepta
81
+ `grantedParams`, `revokedParams` y `solicitedDataPending`.
82
+
83
+ En la aprobación del login, "Permisos requeridos" y "Datos solicitados" son
84
+ botones de cristal y las flechas animadas aparecen en "Datos solicitados"
85
+ siempre que quede algo pendiente para continuar, hasta que se resuelva. Al
86
+ pasar sobre ACEPTAR deshabilitado se indica qué falta: vibra la casilla de la
87
+ política si no está aceptada y, si no, vibra "Datos solicitados".
88
+ `ArrowIcon` (`ba-arrow-icon` en
89
+ Angular) acepta `visible`. `@block-auth.io/ui-styles` añade la clase
90
+ `bal-ba-head-shake` (keyframes `bal-ba-animate-head-shake`, equivalente a
91
+ `animate__headShake` de animate.css, desactivada con
92
+ `prefers-reduced-motion`).
93
+
94
+ Rediseño visual "crystal / liquid glass" de la pantalla de aprobación, los
95
+ modales de permisos y datos solicitados y el selector de tiempo de sesión, sin
96
+ cambiar tamaños ni contenido. `@block-auth.io/ui-styles` añade las clases
97
+ `bal-ba-glass-panel`, `bal-ba-glass-pop`, `bal-ba-glass-backdrop`,
98
+ `bal-ba-glass-rise`, `bal-ba-glass-stagger`, `bal-ba-glass-scroll`,
99
+ `bal-ba-glass-logo`, `bal-ba-glass-divider(-end)`, `bal-ba-glass-row`,
100
+ `bal-ba-glass-icon`, `bal-ba-glass-field`, `bal-ba-glass-label`,
101
+ `bal-ba-glass-badge(-required)`, `bal-ba-glass-input(-wrap|-clear)`,
102
+ `bal-ba-glass-check(-input|-box)`, `bal-ba-glass-decision`,
103
+ `bal-ba-glass-chip-btn(-primary)`, `bal-ba-glass-hint(-success)`,
104
+ `bal-ba-glass-error`, `bal-ba-glass-link`, `bal-ba-glass-consent(-checked)`,
105
+ `bal-ba-glass-action(-reject|-accept)`, `bal-ba-glass-time`,
106
+ `bal-ba-glass-slider` y `bal-ba-glass-btn-pending`, con animaciones cortas
107
+ desactivadas con `prefers-reduced-motion`.
108
+
109
+ ### Patch Changes
110
+
111
+ - Updated dependencies [cf32d5e]
112
+ - Updated dependencies [df80cbe]
113
+ - @block-auth.io/core@0.10.0
114
+ - @block-auth.io/ui-styles@0.5.0
115
+
3
116
  ## 0.2.10
4
117
 
5
118
  ### Patch Changes
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@block-auth.io/angular",
3
- "version": "0.2.10",
3
+ "version": "0.3.0",
4
4
  "dependencies": {
5
5
  "nanostores": "^0.10.0",
6
6
  "tslib": "^2.6.0",
7
- "@block-auth.io/core": "0.9.1",
8
- "@block-auth.io/ui-styles": "0.4.0"
7
+ "@block-auth.io/ui-styles": "0.5.0",
8
+ "@block-auth.io/core": "0.10.0"
9
9
  },
10
10
  "peerDependencies": {
11
11
  "@angular/core": ">=20.0.0",
@@ -19,6 +19,15 @@ describe('PermissionsRequiredModal', () => {
19
19
  ).toBeDefined();
20
20
  });
21
21
 
22
+ it('usa el panel glass con filas e iconos animados', async () => {
23
+ const { container } = await render(PermissionsRequiredModalComponent);
24
+ const panel = container.querySelector('.bal-ba-glass-panel');
25
+ expect(panel?.className).toContain('bal-ba-glass-pop');
26
+ expect(panel?.className).toContain('bal-ba-glass-stagger');
27
+ expect(container.querySelectorAll('.bal-ba-glass-row')).toHaveLength(3);
28
+ expect(container.querySelectorAll('.bal-ba-glass-icon')).toHaveLength(3);
29
+ });
30
+
22
31
  it('respeta el locale activo', async () => {
23
32
  setLocale('es');
24
33
  await render(PermissionsRequiredModalComponent);
@@ -4,7 +4,7 @@ import { injectStore } from '../../../inject-store';
4
4
 
5
5
  // Port literal de packages/react/src/components/txns/internals/PermissionsRequiredModal.tsx
6
6
  // (mapa: packages/vue/.../PermissionsRequiredModal.vue). Sin props/outputs.
7
- const iconClassNames = 'bas-w-6 bas-h-6 bas-text-[#009F6B]';
7
+ const iconClassNames = 'bal-ba-glass-icon bas-text-[#009F6B]';
8
8
 
9
9
  @Component({
10
10
  selector: 'ba-permissions-required-modal',
@@ -12,10 +12,10 @@ const iconClassNames = 'bas-w-6 bas-h-6 bas-text-[#009F6B]';
12
12
  changeDetection: ChangeDetectionStrategy.OnPush,
13
13
  template: `
14
14
  <div
15
- class="bal-ba-transition-2 bas-box-border bas-grid bas-w-[334px] bas-grid-rows-3 bas-gap-3 bas-rounded-md bas-border-2 bas-border-solid bas-border-[var(--bal-primary-100)] bas-bg-[var(--bal-primary-10)] bas-py-4 bas-pl-3 hover:bas-border-[var(--bal-primary-300)]"
15
+ class="bal-ba-glass-panel bal-ba-glass-pop bal-ba-glass-stagger bas-box-border bas-grid bas-w-[334px] bas-grid-rows-3 bas-gap-3 bas-py-4 bas-pl-3"
16
16
  >
17
17
  @for (permission of requiredPermissions(); track $index) {
18
- <div class="bas-flex bas-flex-row bas-items-center bas-gap-2">
18
+ <div class="bal-ba-glass-row">
19
19
  <span [class]="iconClassNames" aria-hidden="true">{{
20
20
  permission.icon
21
21
  }}</span>
@@ -0,0 +1,119 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { Component } from '@angular/core';
3
+ import { render, screen, fireEvent } from '@testing-library/angular';
4
+ import { setLocale } from '@block-auth.io/core';
5
+ import { PhoneInputComponent } from './phone-input.component';
6
+
7
+ const changes: string[] = [];
8
+
9
+ @Component({
10
+ standalone: true,
11
+ imports: [PhoneInputComponent],
12
+ template: `<ba-phone-input inputId="phone" [value]="value" (valueChange)="onChange($event)" />
13
+ <button type="button" (click)="value = ''">reset</button>`,
14
+ })
15
+ class Host {
16
+ value = '';
17
+ onChange(next: string): void {
18
+ this.value = next;
19
+ changes.push(next);
20
+ }
21
+ }
22
+
23
+ const renderHost = async (value: string) => {
24
+ const result = await render(Host, { componentProperties: { value } });
25
+ await result.fixture.whenStable();
26
+ return result;
27
+ };
28
+
29
+ const countryButton = () => document.getElementById('phone-country') as HTMLButtonElement;
30
+ const nationalInput = () => document.getElementById('phone') as HTMLInputElement;
31
+ const search = () => document.getElementById('phone-country-search') as HTMLInputElement | null;
32
+
33
+ describe('PhoneInputComponent', () => {
34
+ afterEach(() => {
35
+ changes.length = 0;
36
+ setLocale('en');
37
+ });
38
+
39
+ it('muestra la bandera, el prefijo y el número nacional del valor', async () => {
40
+ await renderHost('+34634343343');
41
+ expect(countryButton().textContent).toContain('🇪🇸');
42
+ expect(countryButton().textContent).toContain('+34');
43
+ expect(countryButton().getAttribute('aria-label')).toBe('Country: Spain +34');
44
+ expect(nationalInput().value).toBe('634 34 33 43');
45
+ expect(nationalInput().type).toBe('tel');
46
+ });
47
+
48
+ it('acepta el formato antiguo y lo muestra separado', async () => {
49
+ await renderHost('34_634343343_es');
50
+ expect(countryButton().textContent).toContain('+34');
51
+ expect(nationalInput().value).toBe('634 34 33 43');
52
+ });
53
+
54
+ it('envía el número en E.164 al escribir el número nacional', async () => {
55
+ await renderHost('+34');
56
+ await fireEvent.input(nationalInput(), { target: { value: '634 34 33 43' } });
57
+ expect(changes[changes.length - 1]).toBe('+34634343343');
58
+ expect(nationalInput().value).toBe('634 34 33 43');
59
+ });
60
+
61
+ it('busca y selecciona un país con el teclado y cierra con Escape', async () => {
62
+ const { fixture } = await renderHost('+34634343343');
63
+ await fireEvent.click(countryButton());
64
+ await fixture.whenStable();
65
+ expect(countryButton().getAttribute('aria-expanded')).toBe('true');
66
+ expect(document.activeElement).toBe(search());
67
+ expect(screen.getByRole('listbox')).toBeDefined();
68
+ await fireEvent.input(search()!, { target: { value: 'fran' } });
69
+ expect(screen.getAllByRole('option').map((option) => option.id)).toContain('phone-country-FR');
70
+ await fireEvent.keyDown(search()!, { key: 'Escape' });
71
+ expect(search()).toBeNull();
72
+ expect(document.activeElement).toBe(countryButton());
73
+
74
+ await fireEvent.click(countryButton());
75
+ await fireEvent.input(search()!, { target: { value: '+33' } });
76
+ expect(search()!.getAttribute('aria-activedescendant')).toBe('phone-country-FR');
77
+ await fireEvent.keyDown(search()!, { key: 'Enter' });
78
+ expect(search()).toBeNull();
79
+ expect(countryButton().textContent).toContain('+33');
80
+ expect(changes[changes.length - 1]).toBe('+33634343343');
81
+ expect(document.activeElement).toBe(nationalInput());
82
+ });
83
+
84
+ it('mueve la opción activa con las flechas', async () => {
85
+ await renderHost('+34634343343');
86
+ await fireEvent.keyDown(countryButton(), { key: 'ArrowDown' });
87
+ expect(search()!.getAttribute('aria-activedescendant')).toBe('phone-country-ES');
88
+ const options = screen.getAllByRole('option');
89
+ const index = options.findIndex((o) => o.id === 'phone-country-ES');
90
+ await fireEvent.keyDown(search()!, { key: 'ArrowDown' });
91
+ expect(search()!.getAttribute('aria-activedescendant')).toBe(options[index + 1].id);
92
+ await fireEvent.keyDown(search()!, { key: 'ArrowUp' });
93
+ await fireEvent.keyDown(search()!, { key: 'ArrowUp' });
94
+ expect(search()!.getAttribute('aria-activedescendant')).toBe(options[index - 1].id);
95
+ expect(document.getElementById('phone-country-ES')!.getAttribute('aria-selected')).toBe('true');
96
+ });
97
+
98
+ it('avisa si no hay países y usa el idioma activo', async () => {
99
+ setLocale('es');
100
+ await renderHost('+4915112345678');
101
+ expect(countryButton().getAttribute('aria-label')).toBe('País: Alemania +49');
102
+ await fireEvent.click(countryButton());
103
+ expect(search()!.placeholder).toBe('Buscar país o prefijo');
104
+ await fireEvent.input(search()!, { target: { value: 'zzzz' } });
105
+ expect(screen.queryByRole('listbox')).toBeNull();
106
+ expect(screen.getByText('No hay países que coincidan')).toBeDefined();
107
+ });
108
+
109
+ it('cierra la lista al pulsar fuera y conserva el país al vaciar', async () => {
110
+ const { fixture } = await renderHost('+33612345678');
111
+ await fireEvent.click(countryButton());
112
+ await fireEvent.mouseDown(document.body);
113
+ fixture.detectChanges();
114
+ expect(search()).toBeNull();
115
+ await fireEvent.click(screen.getByText('reset'));
116
+ expect(nationalInput().value).toBe('');
117
+ expect(countryButton().textContent).toContain('+33');
118
+ });
119
+ });