ng-comps 1.0.0 → 1.0.2

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 (38) hide show
  1. package/angular.json +9 -8
  2. package/documentation.json +32 -32
  3. package/package.json +1 -1
  4. package/scripts/prepare-package.mjs +38 -19
  5. package/src/app/components/alert/mf-alert.component.spec.ts +2 -2
  6. package/src/app/components/alert/mf-alert.component.ts +1 -1
  7. package/src/app/components/chip/mf-chip.component.ts +1 -1
  8. package/src/app/components/datepicker/mf-datepicker.component.spec.ts +4 -4
  9. package/src/app/components/datepicker/mf-datepicker.component.ts +1 -1
  10. package/src/app/components/dialog/mf-dialog.component.ts +1 -1
  11. package/src/app/components/dialog/mf-dialog.service.spec.ts +2 -2
  12. package/src/app/components/menu/mf-menu.component.spec.ts +1 -1
  13. package/src/app/components/menu/mf-menu.component.ts +1 -1
  14. package/src/app/components/progress-bar/mf-progress-bar.component.spec.ts +2 -2
  15. package/src/app/components/progress-spinner/mf-progress-spinner.component.spec.ts +3 -3
  16. package/src/app/components/sidenav/mf-sidenav.component.ts +2 -2
  17. package/src/app/components/table/mf-table.component.ts +1 -1
  18. package/src/stories/About.mdx +72 -72
  19. package/src/stories/Welcome.mdx +26 -27
  20. package/src/stories/mf-a11y-contracts.stories.ts +49 -49
  21. package/src/stories/mf-autocomplete.stories.ts +194 -188
  22. package/src/stories/mf-button.stories.ts +152 -156
  23. package/src/stories/mf-card.stories.ts +147 -148
  24. package/src/stories/mf-checkbox.stories.ts +88 -88
  25. package/src/stories/mf-datepicker.stories.ts +118 -118
  26. package/src/stories/mf-dialog.stories.ts +159 -167
  27. package/src/stories/mf-form-field.stories.ts +108 -108
  28. package/src/stories/mf-grid-list.stories.ts +104 -105
  29. package/src/stories/mf-icon.stories.ts +133 -130
  30. package/src/stories/mf-input.stories.ts +158 -158
  31. package/src/stories/mf-menu.stories.ts +10 -10
  32. package/src/stories/mf-progress-bar.stories.ts +119 -119
  33. package/src/stories/mf-progress-spinner.stories.ts +124 -124
  34. package/src/stories/mf-radio-button.stories.ts +111 -111
  35. package/src/stories/mf-select.stories.ts +184 -178
  36. package/src/stories/mf-sidenav.stories.ts +331 -334
  37. package/src/stories/mf-table.stories.ts +13 -13
  38. package/src/stories/mf-toolbar.stories.ts +112 -112
@@ -31,7 +31,7 @@ export type MfChipColor = 'brand' | 'accent' | 'error' | 'neutral';
31
31
  <mat-icon matChipAvatar aria-hidden="true">{{ leadingIcon() }}</mat-icon>
32
32
  }
33
33
  {{ label() }}
34
- <button matChipRemove [attr.aria-label]="'Eliminar ' + label()">
34
+ <button matChipRemove [attr.aria-label]="'Remove ' + label()">
35
35
  <mat-icon>cancel</mat-icon>
36
36
  </button>
37
37
  </mat-chip>
@@ -41,19 +41,19 @@ describe('MfDatepickerComponent', () => {
41
41
  });
42
42
 
43
43
  it('should apply error class when error is provided', () => {
44
- fixture.componentRef.setInput('error', 'Fecha requerida');
44
+ fixture.componentRef.setInput('error', 'Date required');
45
45
  expect(component.hostClasses()).toContain('mf-datepicker--error');
46
46
  });
47
47
 
48
48
  it('should render label when provided', () => {
49
- fixture.componentRef.setInput('label', 'Fecha de inicio');
49
+ fixture.componentRef.setInput('label', 'Start date');
50
50
  fixture.detectChanges();
51
51
  const label = fixture.nativeElement.querySelector('mat-label');
52
- expect(label?.textContent).toContain('Fecha de inicio');
52
+ expect(label?.textContent).toContain('Start date');
53
53
  });
54
54
 
55
55
  it('should render hint when provided', () => {
56
- fixture.componentRef.setInput('hint', 'Selecciona una fecha');
56
+ fixture.componentRef.setInput('hint', 'Select a date');
57
57
  fixture.detectChanges();
58
58
  const hint = fixture.nativeElement.querySelector('mat-hint');
59
59
  expect(hint).toBeTruthy();
@@ -145,7 +145,7 @@ export class MfDatepickerComponent implements ControlValueAccessor {
145
145
  /** Ancho completo */
146
146
  readonly fullWidth = input(false);
147
147
  /** Etiqueta accesible del botón para abrir el calendario */
148
- readonly toggleAriaLabel = input('Abrir calendario');
148
+ readonly toggleAriaLabel = input('Open calendar');
149
149
 
150
150
  readonly mfChange = output<Date | null>();
151
151
  readonly mfBlur = output<void>();
@@ -113,7 +113,7 @@ export class MfDialogComponent {
113
113
  /** Rol del diálogo */
114
114
  readonly role = input<'dialog' | 'alertdialog'>('dialog');
115
115
  /** Etiqueta accesible del botón de cierre */
116
- readonly closeButtonLabel = input('Cerrar diálogo');
116
+ readonly closeButtonLabel = input('Close dialog');
117
117
 
118
118
  readonly mfClose = output<void>();
119
119
 
@@ -29,7 +29,7 @@ describe('MfDialogService', () => {
29
29
  class DialogContentComponent {}
30
30
 
31
31
  service.open(DialogContentComponent, {
32
- ariaLabel: 'Eliminar proyecto',
32
+ ariaLabel: 'Delete project',
33
33
  });
34
34
 
35
35
  expect(dialogSpy.open).toHaveBeenCalledWith(
@@ -38,7 +38,7 @@ describe('MfDialogService', () => {
38
38
  role: 'dialog',
39
39
  autoFocus: 'first-tabbable',
40
40
  restoreFocus: true,
41
- ariaLabel: 'Eliminar proyecto',
41
+ ariaLabel: 'Delete project',
42
42
  panelClass: ['mf-dialog-panel'],
43
43
  }),
44
44
  );
@@ -37,7 +37,7 @@ describe('MfMenuComponent', () => {
37
37
  });
38
38
 
39
39
  it('should have accessible trigger label', () => {
40
- expect(component.triggerLabel()).toBe('Abrir menú');
40
+ expect(component.triggerLabel()).toBe('Open menu');
41
41
  });
42
42
 
43
43
  it('should emit item click', () => {
@@ -57,7 +57,7 @@ export class MfMenuComponent {
57
57
  /** Icono del trigger */
58
58
  readonly triggerIcon = input<string>('more_vert');
59
59
  /** Label accesible del trigger */
60
- readonly triggerLabel = input<string>('Abrir menú');
60
+ readonly triggerLabel = input<string>('Open menu');
61
61
 
62
62
  readonly mfItemClick = output<string>();
63
63
 
@@ -40,10 +40,10 @@ describe('MfProgressBarComponent', () => {
40
40
  });
41
41
 
42
42
  it('should render label when provided', () => {
43
- fixture.componentRef.setInput('label', 'Cargando...');
43
+ fixture.componentRef.setInput('label', 'Loading...');
44
44
  fixture.detectChanges();
45
45
  const label = fixture.nativeElement.querySelector('.mf-progress-bar__label');
46
- expect(label?.textContent).toContain('Cargando...');
46
+ expect(label?.textContent).toContain('Loading...');
47
47
  });
48
48
 
49
49
  it('should show percentage when showValue is true and mode is determinate', () => {
@@ -40,10 +40,10 @@ describe('MfProgressSpinnerComponent', () => {
40
40
  });
41
41
 
42
42
  it('should render label when provided', () => {
43
- fixture.componentRef.setInput('label', 'Procesando...');
43
+ fixture.componentRef.setInput('label', 'Processing...');
44
44
  fixture.detectChanges();
45
45
  const label = fixture.nativeElement.querySelector('.mf-progress-spinner__label');
46
- expect(label?.textContent).toContain('Procesando...');
46
+ expect(label?.textContent).toContain('Processing...');
47
47
  });
48
48
 
49
49
  it('should not render label when not provided', () => {
@@ -53,7 +53,7 @@ describe('MfProgressSpinnerComponent', () => {
53
53
  });
54
54
 
55
55
  it('should add labeled class when label is provided', () => {
56
- fixture.componentRef.setInput('label', 'Cargando');
56
+ fixture.componentRef.setInput('label', 'Loading');
57
57
  expect(component.wrapperClasses()).toContain('mf-progress-spinner__wrapper--labeled');
58
58
  });
59
59
  });
@@ -74,7 +74,7 @@ export interface MfSidenavNavItem {
74
74
  <mat-icon class="mf-sidenav__item-icon" aria-hidden="true">{{ item.icon }}</mat-icon>
75
75
  <span class="mf-sidenav__item-label">{{ item.label }}</span>
76
76
  @if (item.badge && item.badge > 0) {
77
- <span class="mf-sidenav__item-badge" aria-label="{{ item.badge }} notificaciones">
77
+ <span class="mf-sidenav__item-badge" aria-label="{{ item.badge }} notifications">
78
78
  {{ item.badge > 99 ? '99+' : item.badge }}
79
79
  </span>
80
80
  }
@@ -112,7 +112,7 @@ export class MfSidenavComponent {
112
112
  /** Icono Material de la cabecera */
113
113
  readonly headerIcon = input<string | undefined>(undefined);
114
114
  /** Aria-label del elemento nav */
115
- readonly navAriaLabel = input('Navegación principal');
115
+ readonly navAriaLabel = input('Primary navigation');
116
116
 
117
117
  readonly mfOpenedChange = output<boolean>();
118
118
  /** Emite el ítem de navegación pulsado */
@@ -84,7 +84,7 @@ export class MfTableComponent {
84
84
  /** Texto visible del botón de acción por fila */
85
85
  readonly rowActionLabel = input<string | undefined>(undefined);
86
86
  /** Cabecera visible de la columna de acción */
87
- readonly rowActionHeader = input('Acciones');
87
+ readonly rowActionHeader = input('Actions');
88
88
  readonly rowActionAriaLabel = input<
89
89
  ((row: Record<string, unknown>) => string) | undefined
90
90
  >(undefined);
@@ -1,72 +1,72 @@
1
- import { Meta } from '@storybook/addon-docs/blocks';
2
-
3
- <Meta title="About" />
4
-
5
- # ng-comps: Guia rapida de uso
6
-
7
- [ng-comps](https://www.npmjs.com/package/ng-comps) es una libreria de componentes Angular con estilo propio sobre Angular Material.
8
-
9
- La librería fue creada por [Manuel Ferrer](https://manuelferrer.vercel.app/).
10
-
11
- <div style={{ border: '1px solid #e5e7eb', borderRadius: '8px', padding: '12px', display: 'flex', gap: '12px', alignItems: 'center', maxWidth: '560px' }}>
12
- <div style={{ flex: '0 0 64px', height: '64px', background: '#f3f4f6', borderRadius: '6px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700, color: '#111827' }}>PE</div>
13
- <div>
14
- <a href="https://preguntas-entrevista.vercel.app/" target="_blank" rel="noreferrer" style={{ fontWeight: 600, color: '#111827', textDecoration: 'none' }}>preguntas-entrevista</a>
15
- <div style={{ color: '#6b7280', fontSize: '0.9rem' }}>Proyecto de ejemplo que utiliza ng-comps.</div>
16
- </div>
17
- </div>
18
-
19
- ## 1) Instalacion
20
-
21
- ```bash
22
- npm i ng-comps
23
- ```
24
-
25
- ## 2) Cargar estilos globales
26
-
27
- En el archivo global de estilos de tu app (por ejemplo `src/styles.css`), importa:
28
-
29
- ```css
30
- @import 'ng-comps/theme/tokens.css';
31
- @import 'ng-comps/styles.css';
32
- ```
33
-
34
- ## 3) Usar componentes (Standalone)
35
-
36
- Importa solo los componentes que realmente uses para maximizar tree-shaking:
37
-
38
- ```ts
39
- import { Component } from '@angular/core';
40
- import { MfButtonComponent, MfInputComponent } from 'ng-comps';
41
-
42
- @Component({
43
- selector: 'app-example',
44
- imports: [MfButtonComponent, MfInputComponent],
45
- template: `
46
- <mf-input label="Email" placeholder="you@company.com" />
47
- <mf-button label="Guardar" variant="filled" />
48
- `,
49
- })
50
- export class ExampleComponent {}
51
- ```
52
-
53
- ## 4) Requisitos del proyecto consumidor
54
-
55
- `ng-comps` usa `peerDependencies`, por lo que tu proyecto debe tener Angular y RxJS instalados.
56
-
57
- Recomendado:
58
-
59
- - Angular `^21`
60
- - RxJS `~7.8`
61
-
62
- ## 5) Buenas practicas de integracion
63
-
64
- - Importa componentes puntualmente desde `ng-comps`.
65
- - Mantén tus estilos globales con los `tokens.css` de la libreria.
66
- - Usa Storybook como referencia visual y de API para cada componente.
67
-
68
- ## Referencias utiles dentro de este repo
69
-
70
- - Tokens de diseño: `src/theme/tokens.css`
71
- - Tema Material: `src/theme/material-theme.scss`
72
- - Ejemplo base: `src/app/components/button/mf-button.component.ts`
1
+ import { Meta } from '@storybook/addon-docs/blocks';
2
+
3
+ <Meta title="About" />
4
+
5
+ # ng-comps: Quick Start Guide
6
+
7
+ [ng-comps](https://www.npmjs.com/package/ng-comps) is an Angular component library with a custom design layer built on top of Angular Material.
8
+
9
+ The library was created by [Manuel Ferrer](https://manuelferrer.vercel.app/).
10
+
11
+ <div style={{ border: '1px solid #e5e7eb', borderRadius: '8px', padding: '12px', display: 'flex', gap: '12px', alignItems: 'center', maxWidth: '560px' }}>
12
+ <div style={{ flex: '0 0 64px', height: '64px', background: '#f3f4f6', borderRadius: '6px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700, color: '#111827' }}>PE</div>
13
+ <div>
14
+ <a href="https://preguntas-entrevista.vercel.app/" target="_blank" rel="noreferrer" style={{ fontWeight: 600, color: '#111827', textDecoration: 'none' }}>preguntas-entrevista</a>
15
+ <div style={{ color: '#6b7280', fontSize: '0.9rem' }}>Sample project built with ng-comps.</div>
16
+ </div>
17
+ </div>
18
+
19
+ ## 1) Install
20
+
21
+ ```bash
22
+ npm i ng-comps
23
+ ```
24
+
25
+ ## 2) Load global styles
26
+
27
+ In your app global stylesheet, for example `src/styles.css`, import:
28
+
29
+ ```css
30
+ @import 'ng-comps/theme/tokens.css';
31
+ @import 'ng-comps/styles.css';
32
+ ```
33
+
34
+ ## 3) Use components (Standalone)
35
+
36
+ Import only the components you actually use to maximize tree-shaking:
37
+
38
+ ```ts
39
+ import { Component } from '@angular/core';
40
+ import { MfButtonComponent, MfInputComponent } from 'ng-comps';
41
+
42
+ @Component({
43
+ selector: 'app-example',
44
+ imports: [MfButtonComponent, MfInputComponent],
45
+ template: `
46
+ <mf-input label="Email" placeholder="you@company.com" />
47
+ <mf-button label="Save" variant="filled" />
48
+ `,
49
+ })
50
+ export class ExampleComponent {}
51
+ ```
52
+
53
+ ## 4) Consumer project requirements
54
+
55
+ `ng-comps` uses `peerDependencies`, so your project must have Angular and RxJS installed.
56
+
57
+ Recommended:
58
+
59
+ - Angular `^21`
60
+ - RxJS `~7.8`
61
+
62
+ ## 5) Integration best practices
63
+
64
+ - Import components directly from `ng-comps` as needed.
65
+ - Keep your global styles aligned with the library `tokens.css`.
66
+ - Use Storybook as the visual and API reference for each component.
67
+
68
+ ## Useful references in this repo
69
+
70
+ - Design tokens: `src/theme/tokens.css`
71
+ - Material theme: `src/theme/material-theme.scss`
72
+ - Base example: `src/app/components/button/mf-button.component.ts`
@@ -1,27 +1,26 @@
1
- import { Meta } from '@storybook/addon-docs/blocks';
2
-
3
- <Meta title="Welcome" />
4
-
5
- # Bienvenido a MF Components
6
-
7
-
8
- Este Storybook muestra los componentes del proyecto implementados usando Angular Material bajo nuestra capa de estilo.
9
-
10
- - Tema centralizado: tokens y tema de Material.
11
- - Componentes disponibles: `MfButton` (producción).
12
-
13
- [Ver `MfButton`](?path=/story/components-mfbutton--filled)
14
-
15
- ---
16
-
17
- ## Cómo ejecutar Storybook
18
-
19
- ```bash
20
- # Build estático
21
- npm run build-storybook
22
-
23
- # Ejecutar Storybook en desarrollo (si está disponible)
24
- npx storybook dev -p 6006
25
- ```
26
-
27
- Si necesitas que añada más secciones (Guías de diseño, Tokens, Roadmap), dímelo y las agregaré.
1
+ import { Meta } from '@storybook/addon-docs/blocks';
2
+
3
+ <Meta title="Welcome" />
4
+
5
+ # Welcome to MF Components
6
+
7
+ This Storybook showcases the project components implemented with Angular Material under our custom styling layer.
8
+
9
+ - Centralized theme: design tokens and Material theme.
10
+ - Available components: `MfButton` (production ready).
11
+
12
+ [View `MfButton`](?path=/story/components-mfbutton--filled)
13
+
14
+ ---
15
+
16
+ ## How to run Storybook
17
+
18
+ ```bash
19
+ # Static build
20
+ npm run build-storybook
21
+
22
+ # Run Storybook in development (if available)
23
+ npx storybook dev -p 6006
24
+ ```
25
+
26
+ If you want me to add more sections such as Design Guides, Tokens, or a Roadmap, I can add them.
@@ -20,21 +20,21 @@ const FRAMEWORK_OPTIONS = [
20
20
  ];
21
21
 
22
22
  const COUNTRY_OPTIONS = [
23
- { value: 'es', label: 'Espana' },
23
+ { value: 'es', label: 'Spain' },
24
24
  { value: 'mx', label: 'Mexico' },
25
25
  { value: 'ar', label: 'Argentina' },
26
26
  { value: 'co', label: 'Colombia' },
27
27
  ];
28
28
 
29
29
  const MENU_ITEMS: MfMenuItem[] = [
30
- { value: 'edit', label: 'Editar', icon: 'edit' },
31
- { value: 'duplicate', label: 'Duplicar', icon: 'content_copy' },
32
- { value: 'archive', label: 'Archivar', icon: 'archive' },
30
+ { value: 'edit', label: 'Edit', icon: 'edit' },
31
+ { value: 'duplicate', label: 'Duplicate', icon: 'content_copy' },
32
+ { value: 'archive', label: 'Archive', icon: 'archive' },
33
33
  ];
34
34
 
35
35
  const TABLE_COLUMNS = [
36
- { key: 'name', header: 'Nombre' },
37
- { key: 'team', header: 'Equipo' },
36
+ { key: 'name', header: 'Name' },
37
+ { key: 'team', header: 'Team' },
38
38
  ];
39
39
 
40
40
  const TABLE_DATA = [
@@ -47,15 +47,15 @@ const TABLE_DATA = [
47
47
  imports: [MfDialogComponent, MfButtonComponent],
48
48
  template: `
49
49
  <mf-dialog
50
- title="Eliminar proyecto"
51
- message="Esta accion elimina el proyecto de forma permanente."
50
+ title="Delete project"
51
+ message="This action permanently deletes the project."
52
52
  role="alertdialog"
53
53
  [showClose]="false"
54
54
  [showActions]="true"
55
55
  >
56
56
  <div mfDialogActions>
57
- <mf-button label="Cancelar" variant="outlined" size="sm" (mfClick)="close()" />
58
- <mf-button label="Eliminar" variant="filled" size="sm" (mfClick)="close()" />
57
+ <mf-button label="Cancel" variant="outlined" size="sm" (mfClick)="close()" />
58
+ <mf-button label="Delete" variant="filled" size="sm" (mfClick)="close()" />
59
59
  </div>
60
60
  </mf-dialog>
61
61
  `,
@@ -75,7 +75,7 @@ class A11yDialogContentStoryComponent {
75
75
  imports: [MfButtonComponent],
76
76
  template: `
77
77
  <mf-button
78
- label="Abrir dialogo de borrado"
78
+ label="Open delete dialog"
79
79
  variant="filled"
80
80
  (mfClick)="open()"
81
81
  />
@@ -107,7 +107,7 @@ class A11yDialogLauncherStoryComponent {
107
107
  (mfNavItemClick)="onNavItemClick($event)"
108
108
  >
109
109
  <div style="padding: 24px;">
110
- <p style="margin: 0; font: inherit;">Seccion activa: {{ activeLabel() }}</p>
110
+ <p style="margin: 0; font: inherit;">Active section: {{ activeLabel() }}</p>
111
111
  </div>
112
112
  </mf-sidenav>
113
113
  </div>
@@ -117,14 +117,14 @@ class A11ySidenavContractStoryComponent {
117
117
  readonly activeId = signal('home');
118
118
 
119
119
  readonly items = signal<MfSidenavNavItem[]>([
120
- { id: 'home', label: 'Inicio', icon: 'home', active: true },
120
+ { id: 'home', label: 'Home', icon: 'home', active: true },
121
121
  { id: 'dashboard', label: 'Dashboard', icon: 'dashboard' },
122
- { id: 'analytics', label: 'Analiticas', icon: 'bar_chart' },
123
- { id: 'users', label: 'Usuarios', icon: 'group' },
122
+ { id: 'analytics', label: 'Analytics', icon: 'bar_chart' },
123
+ { id: 'users', label: 'Users', icon: 'group' },
124
124
  ]);
125
125
 
126
126
  readonly activeLabel = () =>
127
- this.items().find((item) => item.id === this.activeId())?.label ?? 'Inicio';
127
+ this.items().find((item) => item.id === this.activeId())?.label ?? 'Home';
128
128
 
129
129
  onNavItemClick(item: MfSidenavNavItem): void {
130
130
  this.activeId.set(item.id);
@@ -161,10 +161,10 @@ export const ButtonCorrectUsage: Story = {
161
161
  render: () => ({
162
162
  template: `
163
163
  <div style="display: flex; gap: 12px; align-items: center; flex-wrap: wrap;">
164
- <mf-button label="Guardar cambios" variant="filled" />
164
+ <mf-button label="Save changes" variant="filled" />
165
165
  <mf-button
166
166
  [iconOnly]="true"
167
- ariaLabel="Abrir filtros"
167
+ ariaLabel="Open filters"
168
168
  leadingIcon="filter_list"
169
169
  variant="outlined"
170
170
  />
@@ -179,14 +179,14 @@ export const ButtonIncorrectUsage: Story = {
179
179
  render: () => ({
180
180
  template: `
181
181
  <section style="max-width: 720px; border: 1px solid var(--mf-color-border); border-radius: 16px; padding: 20px; background: var(--mf-color-surface);">
182
- <h2 style="margin: 0 0 12px; font-size: 1rem;">Anti-patrones</h2>
182
+ <h2 style="margin: 0 0 12px; font-size: 1rem;">Anti-patterns</h2>
183
183
  <ul style="margin: 0 0 16px; padding-left: 18px;">
184
- <li>Icon-only button sin \`ariaLabel\`.</li>
185
- <li>Usar un \`div\` clickable como boton.</li>
186
- <li>Eliminar el focus outline sin reemplazo visible.</li>
184
+ <li>Icon-only button without \`ariaLabel\`.</li>
185
+ <li>Using a clickable \`div\` as a button.</li>
186
+ <li>Removing the focus outline without a visible replacement.</li>
187
187
  </ul>
188
188
  <pre style="margin: 0; padding: 16px; border-radius: 12px; background: #111827; color: #f9fafb; overflow: auto;"><code>&lt;mf-button [iconOnly]="true" leadingIcon="filter_list" /&gt;
189
- &lt;div (click)="save()"&gt;Guardar&lt;/div&gt;</code></pre>
189
+ &lt;div (click)="save()"&gt;Save&lt;/div&gt;</code></pre>
190
190
  </section>
191
191
  `,
192
192
  }),
@@ -198,9 +198,9 @@ export const InputCorrectUsage: Story = {
198
198
  template: `
199
199
  <div style="max-width: 360px;">
200
200
  <mf-input
201
- label="Correo de contacto"
202
- hint="Usaremos este email para avisos importantes"
203
- placeholder="nombre@empresa.com"
201
+ label="Contact email"
202
+ hint="We will use this email for important updates"
203
+ placeholder="name@company.com"
204
204
  [required]="true"
205
205
  />
206
206
  </div>
@@ -214,10 +214,10 @@ export const InputIncorrectUsage: Story = {
214
214
  render: () => ({
215
215
  template: `
216
216
  <section style="max-width: 720px; border: 1px solid var(--mf-color-border); border-radius: 16px; padding: 20px; background: var(--mf-color-surface);">
217
- <h2 style="margin: 0 0 12px; font-size: 1rem;">Anti-patrones</h2>
217
+ <h2 style="margin: 0 0 12px; font-size: 1rem;">Anti-patterns</h2>
218
218
  <ul style="margin: 0 0 16px; padding-left: 18px;">
219
- <li>Usar solo placeholder como nombre accesible.</li>
220
- <li>No asociar errores o ayudas con el control.</li>
219
+ <li>Using only the placeholder as the accessible name.</li>
220
+ <li>Not wiring errors or helper text to the control.</li>
221
221
  </ul>
222
222
  <pre style="margin: 0; padding: 16px; border-radius: 12px; background: #111827; color: #f9fafb; overflow: auto;"><code>&lt;mf-input placeholder="Email" /&gt;</code></pre>
223
223
  </section>
@@ -243,7 +243,7 @@ export const DialogFocusContract: Story = {
243
243
 
244
244
  await userEvent.tab();
245
245
  const opener = canvas.getByRole('button', {
246
- name: 'Abrir dialogo de borrado',
246
+ name: 'Open delete dialog',
247
247
  });
248
248
  await waitFor(() => expect(opener).toHaveFocus());
249
249
 
@@ -251,11 +251,11 @@ export const DialogFocusContract: Story = {
251
251
 
252
252
  await waitFor(() =>
253
253
  expect(
254
- body.getByRole('alertdialog', { name: 'Eliminar proyecto' }),
254
+ body.getByRole('alertdialog', { name: 'Delete project' }),
255
255
  ).toBeTruthy(),
256
256
  );
257
257
 
258
- const cancelButton = body.getByRole('button', { name: 'Cancelar' });
258
+ const cancelButton = body.getByRole('button', { name: 'Cancel' });
259
259
  await waitFor(() => expect(cancelButton).toHaveFocus());
260
260
 
261
261
  await userEvent.keyboard('{Enter}');
@@ -273,7 +273,7 @@ export const MenuKeyboardContract: Story = {
273
273
  props: {
274
274
  items: MENU_ITEMS,
275
275
  },
276
- template: `<mf-menu [items]="items" triggerLabel="Abrir acciones" />`,
276
+ template: `<mf-menu [items]="items" triggerLabel="Open actions" />`,
277
277
  moduleMetadata: { imports: [MfMenuComponent] },
278
278
  }),
279
279
  play: async ({ canvasElement }) => {
@@ -282,13 +282,13 @@ export const MenuKeyboardContract: Story = {
282
282
  const body = within(doc.body);
283
283
 
284
284
  await userEvent.tab();
285
- const trigger = canvas.getByRole('button', { name: 'Abrir acciones' });
285
+ const trigger = canvas.getByRole('button', { name: 'Open actions' });
286
286
  await waitFor(() => expect(trigger).toHaveFocus());
287
287
 
288
288
  await userEvent.keyboard('{Enter}');
289
289
 
290
290
  await waitFor(() => expect(body.getByRole('menu')).toBeTruthy());
291
- expect(body.getByRole('menuitem', { name: 'Editar' })).toBeTruthy();
291
+ expect(body.getByRole('menuitem', { name: 'Edit' })).toBeTruthy();
292
292
 
293
293
  await userEvent.keyboard('{Escape}');
294
294
 
@@ -348,8 +348,8 @@ export const AutocompleteKeyboardContract: Story = {
348
348
  <div style="max-width: 360px;">
349
349
  <mf-autocomplete
350
350
  [options]="options"
351
- label="Pais"
352
- placeholder="Escribe un pais"
351
+ label="Country"
352
+ placeholder="Type a country"
353
353
  />
354
354
  </div>
355
355
  `,
@@ -361,13 +361,13 @@ export const AutocompleteKeyboardContract: Story = {
361
361
  const body = within(doc.body);
362
362
 
363
363
  await userEvent.tab();
364
- const input = canvas.getByRole('textbox', { name: 'Pais' });
364
+ const input = canvas.getByRole('textbox', { name: 'Country' });
365
365
  await waitFor(() => expect(input).toHaveFocus());
366
366
 
367
- await userEvent.keyboard('es');
367
+ await userEvent.keyboard('sp');
368
368
 
369
369
  await waitFor(() => expect(body.getByRole('listbox')).toBeTruthy());
370
- expect(body.getByText('Espana')).toBeTruthy();
370
+ expect(body.getByText('Spain')).toBeTruthy();
371
371
 
372
372
  await userEvent.keyboard('{Escape}');
373
373
  await waitFor(() =>
@@ -381,7 +381,7 @@ export const DatepickerKeyboardContract: Story = {
381
381
  render: () => ({
382
382
  template: `
383
383
  <div style="max-width: 360px;">
384
- <mf-datepicker label="Fecha de entrega" />
384
+ <mf-datepicker label="Delivery date" />
385
385
  </div>
386
386
  `,
387
387
  moduleMetadata: { imports: [MfDatepickerComponent] },
@@ -391,11 +391,11 @@ export const DatepickerKeyboardContract: Story = {
391
391
  const doc = canvasElement.ownerDocument;
392
392
 
393
393
  await userEvent.tab();
394
- canvas.getByRole('textbox', { name: 'Fecha de entrega' });
394
+ canvas.getByRole('textbox', { name: 'Delivery date' });
395
395
 
396
396
  await userEvent.tab();
397
397
  const toggleButton = canvas.getByRole('button', {
398
- name: 'Abrir calendario',
398
+ name: 'Open calendar',
399
399
  });
400
400
  await waitFor(() => expect(toggleButton).toHaveFocus());
401
401
 
@@ -420,13 +420,13 @@ export const TableExplicitActionContract: Story = {
420
420
  columns: TABLE_COLUMNS,
421
421
  data: TABLE_DATA,
422
422
  rowActionAriaLabel: (row: Record<string, unknown>) =>
423
- `Ver detalle de ${row['name']}`,
423
+ `View details for ${row['name']}`,
424
424
  },
425
425
  template: `
426
426
  <mf-table
427
427
  [columns]="columns"
428
428
  [data]="data"
429
- rowActionLabel="Ver detalle"
429
+ rowActionLabel="View details"
430
430
  [rowActionAriaLabel]="rowActionAriaLabel"
431
431
  />
432
432
  `,
@@ -435,7 +435,7 @@ export const TableExplicitActionContract: Story = {
435
435
  play: async ({ canvasElement }) => {
436
436
  const canvas = within(canvasElement);
437
437
  const detailButtons = canvas.getAllByRole('button', {
438
- name: /Ver detalle de/,
438
+ name: /View details for/,
439
439
  });
440
440
 
441
441
  expect(detailButtons).toHaveLength(2);
@@ -455,18 +455,18 @@ export const SidenavKeyboardContract: Story = {
455
455
  const canvas = within(canvasElement);
456
456
 
457
457
  await userEvent.tab();
458
- const home = canvas.getByRole('button', { name: 'Inicio' });
458
+ const home = canvas.getByRole('button', { name: 'Home' });
459
459
  await waitFor(() => expect(home).toHaveFocus());
460
460
 
461
461
  await userEvent.tab();
462
462
  await userEvent.tab();
463
- const analytics = canvas.getByRole('button', { name: 'Analiticas' });
463
+ const analytics = canvas.getByRole('button', { name: 'Analytics' });
464
464
  await waitFor(() => expect(analytics).toHaveFocus());
465
465
 
466
466
  await userEvent.keyboard('{Enter}');
467
467
 
468
468
  await waitFor(() =>
469
- expect(canvas.getByText('Seccion activa: Analiticas')).toBeTruthy(),
469
+ expect(canvas.getByText('Active section: Analytics')).toBeTruthy(),
470
470
  );
471
471
  },
472
472
  };