ng-comps 0.2.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/.editorconfig +17 -0
  2. package/.github/copilot-instructions.md +55 -0
  3. package/.github/workflows/ci.yml +29 -0
  4. package/.prettierrc +12 -0
  5. package/.storybook/main.ts +21 -0
  6. package/.storybook/preview.ts +27 -0
  7. package/.storybook/tsconfig.doc.json +10 -0
  8. package/.storybook/tsconfig.json +15 -0
  9. package/.storybook/typings.d.ts +4 -0
  10. package/.vscode/extensions.json +4 -0
  11. package/.vscode/launch.json +20 -0
  12. package/.vscode/mcp.json +9 -0
  13. package/.vscode/tasks.json +42 -0
  14. package/ACCESSIBILITY.md +127 -0
  15. package/README.md +79 -62
  16. package/angular.json +105 -0
  17. package/documentation.json +13394 -0
  18. package/ng-package.json +27 -0
  19. package/package.json +58 -45
  20. package/public/favicon.ico +0 -0
  21. package/scripts/prepare-package.mjs +61 -0
  22. package/src/app/a11y/accessibility.utils.ts +35 -0
  23. package/src/app/a11y/index.ts +6 -0
  24. package/src/app/accessibility/ng-comps.a11y.spec.ts +108 -0
  25. package/src/app/app.config.ts +11 -0
  26. package/src/app/app.css +107 -0
  27. package/src/app/app.html +48 -0
  28. package/src/app/app.routes.ts +3 -0
  29. package/src/app/app.spec.ts +23 -0
  30. package/src/app/app.ts +10 -0
  31. package/src/app/components/accordion/index.ts +2 -0
  32. package/src/app/components/accordion/mf-accordion.component.css +38 -0
  33. package/src/app/components/accordion/mf-accordion.component.spec.ts +48 -0
  34. package/src/app/components/accordion/mf-accordion.component.ts +53 -0
  35. package/src/app/components/alert/index.ts +2 -0
  36. package/src/app/components/alert/mf-alert.component.css +100 -0
  37. package/src/app/components/alert/mf-alert.component.spec.ts +59 -0
  38. package/src/app/components/alert/mf-alert.component.ts +68 -0
  39. package/src/app/components/autocomplete/index.ts +5 -0
  40. package/src/app/components/autocomplete/mf-autocomplete.component.css +105 -0
  41. package/src/app/components/autocomplete/mf-autocomplete.component.spec.ts +116 -0
  42. package/src/app/components/autocomplete/mf-autocomplete.component.ts +307 -0
  43. package/src/app/components/avatar/index.ts +2 -0
  44. package/src/app/components/avatar/mf-avatar.component.css +27 -0
  45. package/src/app/components/avatar/mf-avatar.component.spec.ts +49 -0
  46. package/src/app/components/avatar/mf-avatar.component.ts +99 -0
  47. package/src/app/components/badge/index.ts +2 -0
  48. package/src/app/components/badge/mf-badge.component.css +32 -0
  49. package/src/app/components/badge/mf-badge.component.spec.ts +40 -0
  50. package/src/app/components/badge/mf-badge.component.ts +105 -0
  51. package/src/app/components/breadcrumb/index.ts +2 -0
  52. package/src/app/components/breadcrumb/mf-breadcrumb.component.css +61 -0
  53. package/src/app/components/breadcrumb/mf-breadcrumb.component.spec.ts +61 -0
  54. package/src/app/components/breadcrumb/mf-breadcrumb.component.ts +75 -0
  55. package/src/app/components/button/index.ts +2 -0
  56. package/src/app/components/button/mf-button.component.css +136 -0
  57. package/src/app/components/button/mf-button.component.ts +174 -0
  58. package/src/app/components/card/index.ts +2 -0
  59. package/src/app/components/card/mf-card.component.css +82 -0
  60. package/src/app/components/card/mf-card.component.ts +59 -0
  61. package/src/app/components/checkbox/index.ts +1 -0
  62. package/src/app/components/checkbox/mf-checkbox.component.css +75 -0
  63. package/src/app/components/checkbox/mf-checkbox.component.ts +187 -0
  64. package/src/app/components/chip/index.ts +2 -0
  65. package/src/app/components/chip/mf-chip.component.css +69 -0
  66. package/src/app/components/chip/mf-chip.component.spec.ts +47 -0
  67. package/src/app/components/chip/mf-chip.component.ts +77 -0
  68. package/src/app/components/datepicker/index.ts +2 -0
  69. package/src/app/components/datepicker/mf-datepicker.component.css +102 -0
  70. package/src/app/components/datepicker/mf-datepicker.component.spec.ts +69 -0
  71. package/src/app/components/datepicker/mf-datepicker.component.ts +233 -0
  72. package/src/app/components/dialog/index.ts +3 -0
  73. package/src/app/components/dialog/mf-dialog.component.css +73 -0
  74. package/src/app/components/dialog/mf-dialog.component.ts +160 -0
  75. package/src/app/components/dialog/mf-dialog.service.spec.ts +61 -0
  76. package/src/app/components/dialog/mf-dialog.service.ts +52 -0
  77. package/src/app/components/divider/index.ts +2 -0
  78. package/src/app/components/divider/mf-divider.component.css +38 -0
  79. package/src/app/components/divider/mf-divider.component.spec.ts +40 -0
  80. package/src/app/components/divider/mf-divider.component.ts +44 -0
  81. package/src/app/components/form-field/index.ts +1 -0
  82. package/src/app/components/form-field/mf-form-field.component.css +51 -0
  83. package/src/app/components/form-field/mf-form-field.component.ts +74 -0
  84. package/src/app/components/grid-list/index.ts +2 -0
  85. package/src/app/components/grid-list/mf-grid-list.component.css +47 -0
  86. package/src/app/components/grid-list/mf-grid-list.component.spec.ts +57 -0
  87. package/src/app/components/grid-list/mf-grid-list.component.ts +68 -0
  88. package/src/app/components/icon/index.ts +2 -0
  89. package/src/app/components/icon/mf-icon.component.css +56 -0
  90. package/src/app/components/icon/mf-icon.component.ts +41 -0
  91. package/src/app/components/input/index.ts +2 -0
  92. package/src/app/components/input/mf-input.component.css +105 -0
  93. package/src/app/components/input/mf-input.component.ts +217 -0
  94. package/src/app/components/menu/index.ts +2 -0
  95. package/src/app/components/menu/mf-menu.component.css +31 -0
  96. package/src/app/components/menu/mf-menu.component.spec.ts +49 -0
  97. package/src/app/components/menu/mf-menu.component.ts +66 -0
  98. package/src/app/components/paginator/index.ts +1 -0
  99. package/src/app/components/paginator/mf-paginator.component.css +32 -0
  100. package/src/app/components/paginator/mf-paginator.component.spec.ts +44 -0
  101. package/src/app/components/paginator/mf-paginator.component.ts +52 -0
  102. package/src/app/components/progress-bar/index.ts +2 -0
  103. package/src/app/components/progress-bar/mf-progress-bar.component.css +53 -0
  104. package/src/app/components/progress-bar/mf-progress-bar.component.spec.ts +65 -0
  105. package/src/app/components/progress-bar/mf-progress-bar.component.ts +79 -0
  106. package/src/app/components/progress-spinner/index.ts +2 -0
  107. package/src/app/components/progress-spinner/mf-progress-spinner.component.css +38 -0
  108. package/src/app/components/progress-spinner/mf-progress-spinner.component.spec.ts +59 -0
  109. package/src/app/components/progress-spinner/mf-progress-spinner.component.ts +81 -0
  110. package/src/app/components/radio-button/index.ts +2 -0
  111. package/src/app/components/radio-button/mf-radio-button.component.css +86 -0
  112. package/src/app/components/radio-button/mf-radio-button.component.spec.ts +55 -0
  113. package/src/app/components/radio-button/mf-radio-button.component.ts +219 -0
  114. package/src/app/components/select/index.ts +2 -0
  115. package/src/app/components/select/mf-select.component.css +121 -0
  116. package/src/app/components/select/mf-select.component.spec.ts +108 -0
  117. package/src/app/components/select/mf-select.component.ts +252 -0
  118. package/src/app/components/sidenav/index.ts +2 -0
  119. package/src/app/components/sidenav/mf-sidenav.component.css +168 -0
  120. package/src/app/components/sidenav/mf-sidenav.component.spec.ts +57 -0
  121. package/src/app/components/sidenav/mf-sidenav.component.ts +126 -0
  122. package/src/app/components/slide-toggle/index.ts +1 -0
  123. package/src/app/components/slide-toggle/mf-slide-toggle.component.css +42 -0
  124. package/src/app/components/slide-toggle/mf-slide-toggle.component.spec.ts +43 -0
  125. package/src/app/components/slide-toggle/mf-slide-toggle.component.ts +188 -0
  126. package/src/app/components/snackbar/index.ts +2 -0
  127. package/src/app/components/snackbar/mf-snackbar.service.css +31 -0
  128. package/src/app/components/snackbar/mf-snackbar.service.spec.ts +81 -0
  129. package/src/app/components/snackbar/mf-snackbar.service.ts +77 -0
  130. package/src/app/components/table/index.ts +2 -0
  131. package/src/app/components/table/mf-table.component.css +68 -0
  132. package/src/app/components/table/mf-table.component.spec.ts +76 -0
  133. package/src/app/components/table/mf-table.component.ts +117 -0
  134. package/src/app/components/tabs/index.ts +2 -0
  135. package/src/app/components/tabs/mf-tabs.component.css +31 -0
  136. package/src/app/components/tabs/mf-tabs.component.spec.ts +50 -0
  137. package/src/app/components/tabs/mf-tabs.component.ts +62 -0
  138. package/src/app/components/textarea/index.ts +2 -0
  139. package/src/app/components/textarea/mf-textarea.component.css +48 -0
  140. package/src/app/components/textarea/mf-textarea.component.spec.ts +55 -0
  141. package/src/app/components/textarea/mf-textarea.component.ts +227 -0
  142. package/src/app/components/toolbar/index.ts +2 -0
  143. package/src/app/components/toolbar/mf-toolbar.component.css +77 -0
  144. package/src/app/components/toolbar/mf-toolbar.component.ts +56 -0
  145. package/src/app/components/tooltip/index.ts +3 -0
  146. package/src/app/components/tooltip/mf-tooltip.component.css +7 -0
  147. package/src/app/components/tooltip/mf-tooltip.component.spec.ts +37 -0
  148. package/src/app/components/tooltip/mf-tooltip.component.ts +47 -0
  149. package/src/app/components/tooltip/mf-tooltip.directive.ts +22 -0
  150. package/src/index.html +18 -0
  151. package/src/main.ts +6 -0
  152. package/src/public-api.ts +31 -0
  153. package/src/stories/About.mdx +72 -0
  154. package/src/stories/Accessibility.mdx +59 -0
  155. package/src/stories/Welcome.mdx +27 -0
  156. package/src/stories/assets/accessibility.png +0 -0
  157. package/src/stories/assets/accessibility.svg +1 -0
  158. package/src/stories/assets/addon-library.png +0 -0
  159. package/src/stories/assets/assets.png +0 -0
  160. package/src/stories/assets/avif-test-image.avif +0 -0
  161. package/src/stories/assets/context.png +0 -0
  162. package/src/stories/assets/discord.svg +1 -0
  163. package/src/stories/assets/docs.png +0 -0
  164. package/src/stories/assets/figma-plugin.png +0 -0
  165. package/src/stories/assets/github.svg +1 -0
  166. package/src/stories/assets/share.png +0 -0
  167. package/src/stories/assets/styling.png +0 -0
  168. package/src/stories/assets/testing.png +0 -0
  169. package/src/stories/assets/theming.png +0 -0
  170. package/src/stories/assets/tutorials.svg +1 -0
  171. package/src/stories/assets/youtube.svg +1 -0
  172. package/src/stories/mf-a11y-contracts.stories.ts +472 -0
  173. package/src/stories/mf-autocomplete.stories.ts +188 -0
  174. package/src/stories/mf-button.stories.ts +156 -0
  175. package/src/stories/mf-card.stories.ts +148 -0
  176. package/src/stories/mf-checkbox.stories.ts +88 -0
  177. package/src/stories/mf-datepicker.stories.ts +118 -0
  178. package/src/stories/mf-dialog.stories.ts +167 -0
  179. package/src/stories/mf-form-field.stories.ts +108 -0
  180. package/src/stories/mf-grid-list.stories.ts +105 -0
  181. package/src/stories/mf-icon.stories.ts +130 -0
  182. package/src/stories/mf-input.stories.ts +158 -0
  183. package/src/stories/mf-menu.stories.ts +71 -0
  184. package/src/stories/mf-progress-bar.stories.ts +119 -0
  185. package/src/stories/mf-progress-spinner.stories.ts +124 -0
  186. package/src/stories/mf-radio-button.stories.ts +111 -0
  187. package/src/stories/mf-select.stories.ts +178 -0
  188. package/src/stories/mf-sidenav.stories.ts +334 -0
  189. package/src/stories/mf-table.stories.ts +80 -0
  190. package/src/stories/mf-toolbar.stories.ts +112 -0
  191. package/src/stories/user.ts +3 -0
  192. package/src/styles.css +58 -21
  193. package/src/theme/tokens.css +7 -4
  194. package/tsconfig.app.json +15 -0
  195. package/tsconfig.json +33 -0
  196. package/tsconfig.spec.json +15 -0
  197. package/vercel.json +6 -0
  198. package/fesm2022/ng-comps.mjs +0 -2479
  199. package/fesm2022/ng-comps.mjs.map +0 -1
  200. package/types/ng-comps.d.ts +0 -917
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "./node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "./dist/ng-comps",
4
+ "lib": {
5
+ "entryFile": "src/public-api.ts"
6
+ },
7
+ "assets": [
8
+ "LICENSE",
9
+ "README.md",
10
+ "src/styles.css",
11
+ "src/theme/**/*"
12
+ ],
13
+ "allowedNonPeerDependencies": [
14
+ "@angular/animations",
15
+ "@angular/cdk",
16
+ "@angular/common",
17
+ "@angular/compiler",
18
+ "@angular/core",
19
+ "@angular/forms",
20
+ "@angular/material",
21
+ "@angular/platform-browser",
22
+ "@angular/router",
23
+ "@fontsource/material-icons",
24
+ "rxjs",
25
+ "tslib"
26
+ ]
27
+ }
package/package.json CHANGED
@@ -1,45 +1,58 @@
1
- {
2
- "name": "ng-comps",
3
- "version": "0.2.0",
4
- "description": "Angular UI components for mf-design-system",
5
- "keywords": [
6
- "angular",
7
- "components",
8
- "ui",
9
- "design-system",
10
- "material"
11
- ],
12
- "license": "MIT",
13
- "type": "module",
14
- "sideEffects": false,
15
- "main": "./fesm2022/ng-comps.mjs",
16
- "module": "./fesm2022/ng-comps.mjs",
17
- "typings": "./types/ng-comps.d.ts",
18
- "exports": {
19
- ".": {
20
- "types": "./types/ng-comps.d.ts",
21
- "default": "./fesm2022/ng-comps.mjs"
22
- },
23
- "./styles.css": "./src/styles.css",
24
- "./theme/tokens.css": "./src/theme/tokens.css",
25
- "./theme/material-theme.scss": "./src/theme/material-theme.scss",
26
- "./package.json": "./package.json"
27
- },
28
- "peerDependencies": {
29
- "@angular/animations": "^21.2.0",
30
- "@angular/cdk": "^21.2.2",
31
- "@angular/common": "^21.2.0",
32
- "@angular/core": "^21.2.0",
33
- "@angular/forms": "^21.2.0",
34
- "@angular/material": "^21.2.2",
35
- "@angular/platform-browser": "^21.2.0",
36
- "@angular/router": "^21.2.0",
37
- "rxjs": "~7.8.0"
38
- },
39
- "dependencies": {
40
- "tslib": "^2.3.0"
41
- },
42
- "publishConfig": {
43
- "access": "public"
44
- }
45
- }
1
+ {
2
+ "name": "ng-comps",
3
+ "version": "1.0.0",
4
+ "license": "MIT",
5
+ "scripts": {
6
+ "ng": "ng",
7
+ "start": "ng serve",
8
+ "build": "ng build",
9
+ "build:lib": "ng-packagr -p ng-package.json",
10
+ "prepare:package": "node scripts/prepare-package.mjs",
11
+ "release:dry-run": "npm run build:lib && npm run prepare:package && npm pack ./dist/ng-comps --dry-run",
12
+ "publish:npm": "npm run build:lib && npm run prepare:package && npm publish ./dist/ng-comps --access public",
13
+ "version:patch": "npm version patch --no-git-tag-version",
14
+ "version:minor": "npm version minor --no-git-tag-version",
15
+ "watch": "ng build --watch --configuration development",
16
+ "test": "ng test",
17
+ "test:ci": "ng test --watch=false",
18
+ "storybook": "ng run ng-comps:storybook",
19
+ "build-storybook": "ng run ng-comps:build-storybook",
20
+ "ci": "npm run build:lib && npm run test:ci && npm run build-storybook"
21
+ },
22
+ "private": false,
23
+ "packageManager": "npm@10.8.2",
24
+ "dependencies": {
25
+ "@angular/animations": "^21.2.4",
26
+ "@angular/cdk": "^21.2.2",
27
+ "@angular/common": "^21.2.0",
28
+ "@angular/compiler": "^21.2.0",
29
+ "@angular/core": "^21.2.0",
30
+ "@angular/forms": "^21.2.0",
31
+ "@angular/material": "^21.2.2",
32
+ "@angular/platform-browser": "^21.2.0",
33
+ "@angular/router": "^21.2.0",
34
+ "@fontsource/material-icons": "^5.2.7",
35
+ "rxjs": "~7.8.0",
36
+ "tslib": "^2.3.0"
37
+ },
38
+ "devDependencies": {
39
+ "@angular-devkit/architect": "^0.2102.0",
40
+ "@angular-devkit/build-angular": "^21.2.0",
41
+ "@angular-devkit/core": "^21.2.0",
42
+ "@angular/build": "^21.2.2",
43
+ "@angular/cli": "^21.2.2",
44
+ "@angular/compiler-cli": "^21.2.0",
45
+ "@angular/platform-browser-dynamic": "^21.2.0",
46
+ "@compodoc/compodoc": "^1.2.1",
47
+ "@storybook/addon-a11y": "^10.2.19",
48
+ "@storybook/addon-docs": "^10.2.19",
49
+ "@storybook/angular": "^10.2.19",
50
+ "axe-core": "^4.11.0",
51
+ "jsdom": "^28.0.0",
52
+ "prettier": "^3.8.1",
53
+ "storybook": "^10.2.19",
54
+ "ng-packagr": "^21.2.0",
55
+ "typescript": "~5.9.2",
56
+ "vitest": "^4.0.8"
57
+ }
58
+ }
Binary file
@@ -0,0 +1,61 @@
1
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+
4
+ const root = process.cwd();
5
+ const rootPkgPath = resolve(root, 'package.json');
6
+ const distDir = resolve(root, 'dist', 'ng-comps');
7
+ const distPkgPath = resolve(distDir, 'package.json');
8
+
9
+ if (!existsSync(distDir)) {
10
+ throw new Error('No existe dist/ng-comps. Ejecuta primero npm run build:lib');
11
+ }
12
+
13
+ const rootPkg = JSON.parse(readFileSync(rootPkgPath, 'utf8'));
14
+ const angularVersion = rootPkg.dependencies['@angular/core'];
15
+ const cdkVersion = rootPkg.dependencies['@angular/cdk'];
16
+ const materialVersion = rootPkg.dependencies['@angular/material'];
17
+ const rxjsVersion = rootPkg.dependencies.rxjs;
18
+ const tslibVersion = rootPkg.dependencies.tslib;
19
+
20
+ const packageJson = {
21
+ name: rootPkg.name,
22
+ version: rootPkg.version,
23
+ description: 'Angular UI components for mf-design-system',
24
+ keywords: ['angular', 'components', 'ui', 'design-system', 'material'],
25
+ license: rootPkg.license || 'MIT',
26
+ type: 'module',
27
+ sideEffects: false,
28
+ main: './fesm2022/ng-comps.mjs',
29
+ module: './fesm2022/ng-comps.mjs',
30
+ typings: './types/ng-comps.d.ts',
31
+ exports: {
32
+ '.': {
33
+ types: './types/ng-comps.d.ts',
34
+ default: './fesm2022/ng-comps.mjs'
35
+ },
36
+ './styles.css': './src/styles.css',
37
+ './theme/tokens.css': './src/theme/tokens.css',
38
+ './theme/material-theme.scss': './src/theme/material-theme.scss',
39
+ './package.json': './package.json'
40
+ },
41
+ peerDependencies: {
42
+ '@angular/animations': angularVersion,
43
+ '@angular/cdk': cdkVersion,
44
+ '@angular/common': angularVersion,
45
+ '@angular/core': angularVersion,
46
+ '@angular/forms': angularVersion,
47
+ '@angular/material': materialVersion,
48
+ '@angular/platform-browser': angularVersion,
49
+ '@angular/router': angularVersion,
50
+ rxjs: rxjsVersion
51
+ },
52
+ dependencies: {
53
+ tslib: tslibVersion
54
+ },
55
+ publishConfig: {
56
+ access: 'public'
57
+ }
58
+ };
59
+
60
+ writeFileSync(distPkgPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8');
61
+ console.log(`package.json optimizado generado en ${distPkgPath}`);
@@ -0,0 +1,35 @@
1
+ import { isDevMode } from '@angular/core';
2
+
3
+ let nextUniqueId = 0;
4
+ const emittedWarnings = new Set<string>();
5
+
6
+ export function createUniqueId(prefix: string): string {
7
+ nextUniqueId += 1;
8
+ return `${prefix}-${nextUniqueId}`;
9
+ }
10
+
11
+ export function mergeAriaIds(
12
+ ...values: Array<string | null | undefined>
13
+ ): string | null {
14
+ const ids = values
15
+ .flatMap((value) => (value ? value.split(/\s+/) : []))
16
+ .map((value) => value.trim())
17
+ .filter(Boolean);
18
+
19
+ return ids.length > 0 ? Array.from(new Set(ids)).join(' ') : null;
20
+ }
21
+
22
+ export function hasAccessibleName(
23
+ ...values: Array<string | null | undefined>
24
+ ): boolean {
25
+ return values.some((value) => Boolean(value?.trim()));
26
+ }
27
+
28
+ export function warnInDev(message: string): void {
29
+ if (!isDevMode() || emittedWarnings.has(message)) {
30
+ return;
31
+ }
32
+
33
+ emittedWarnings.add(message);
34
+ console.warn(`[ng-comps:a11y] ${message}`);
35
+ }
@@ -0,0 +1,6 @@
1
+ export {
2
+ createUniqueId,
3
+ hasAccessibleName,
4
+ mergeAriaIds,
5
+ warnInDev,
6
+ } from './accessibility.utils';
@@ -0,0 +1,108 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
3
+ import * as axe from 'axe-core';
4
+ import { MfButtonComponent } from '../components/button';
5
+ import { MfDialogComponent } from '../components/dialog';
6
+ import { MfInputComponent } from '../components/input';
7
+ import { MfSelectComponent } from '../components/select';
8
+
9
+ async function expectNoAxeViolations(
10
+ fixture: ComponentFixture<unknown>,
11
+ ): Promise<void> {
12
+ await fixture.whenStable();
13
+ const results = await axe.run(fixture.nativeElement as HTMLElement, {
14
+ runOnly: {
15
+ type: 'tag',
16
+ values: ['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa'],
17
+ },
18
+ rules: {
19
+ region: { enabled: false },
20
+ 'page-has-heading-one': { enabled: false },
21
+ 'landmark-one-main': { enabled: false },
22
+ },
23
+ });
24
+
25
+ const summaries = results.violations.map((violation) => ({
26
+ id: violation.id,
27
+ impact: violation.impact,
28
+ nodes: violation.nodes.map((node) => node.target.join(' ')),
29
+ }));
30
+
31
+ expect(summaries).toEqual([]);
32
+ }
33
+
34
+ describe('ng-comps accessibility contract', () => {
35
+ it('renders a labeled text button without axe violations', async () => {
36
+ await TestBed.configureTestingModule({
37
+ imports: [MfButtonComponent],
38
+ }).compileComponents();
39
+
40
+ const fixture = TestBed.createComponent(MfButtonComponent);
41
+ fixture.componentRef.setInput('label', 'Guardar cambios');
42
+ fixture.detectChanges();
43
+
44
+ await expectNoAxeViolations(fixture);
45
+ });
46
+
47
+ it('renders an icon-only button with an explicit accessible name', async () => {
48
+ await TestBed.configureTestingModule({
49
+ imports: [MfButtonComponent],
50
+ }).compileComponents();
51
+
52
+ const fixture = TestBed.createComponent(MfButtonComponent);
53
+ fixture.componentRef.setInput('iconOnly', true);
54
+ fixture.componentRef.setInput('leadingIcon', 'settings');
55
+ fixture.componentRef.setInput('ariaLabel', 'Abrir configuración');
56
+ fixture.detectChanges();
57
+
58
+ await expectNoAxeViolations(fixture);
59
+ });
60
+
61
+ it('renders an input with visible label, hint and error wiring', async () => {
62
+ await TestBed.configureTestingModule({
63
+ imports: [MfInputComponent, NoopAnimationsModule],
64
+ }).compileComponents();
65
+
66
+ const fixture = TestBed.createComponent(MfInputComponent);
67
+ fixture.componentRef.setInput('label', 'Correo electrónico');
68
+ fixture.componentRef.setInput('hint', 'Usa tu correo corporativo');
69
+ fixture.componentRef.setInput('error', 'El correo es obligatorio');
70
+ fixture.detectChanges();
71
+
72
+ await expectNoAxeViolations(fixture);
73
+ });
74
+
75
+ it('renders a select with visible label and helper text', async () => {
76
+ await TestBed.configureTestingModule({
77
+ imports: [MfSelectComponent, NoopAnimationsModule],
78
+ }).compileComponents();
79
+
80
+ const fixture = TestBed.createComponent(MfSelectComponent);
81
+ fixture.componentRef.setInput('label', 'Framework');
82
+ fixture.componentRef.setInput('hint', 'Selecciona una opción');
83
+ fixture.componentRef.setInput('options', [
84
+ { value: 'angular', label: 'Angular' },
85
+ { value: 'react', label: 'React' },
86
+ ]);
87
+ fixture.detectChanges();
88
+
89
+ await expectNoAxeViolations(fixture);
90
+ });
91
+
92
+ it('renders an inline dialog with title and description', async () => {
93
+ await TestBed.configureTestingModule({
94
+ imports: [MfDialogComponent, NoopAnimationsModule],
95
+ }).compileComponents();
96
+
97
+ const fixture = TestBed.createComponent(MfDialogComponent);
98
+ fixture.componentRef.setInput('title', 'Confirmar acción');
99
+ fixture.componentRef.setInput(
100
+ 'message',
101
+ 'Este cambio se aplicará de forma permanente.',
102
+ );
103
+ fixture.componentRef.setInput('showActions', false);
104
+ fixture.detectChanges();
105
+
106
+ await expectNoAxeViolations(fixture);
107
+ });
108
+ });
@@ -0,0 +1,11 @@
1
+ import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
2
+ import { provideRouter } from '@angular/router';
3
+
4
+ import { routes } from './app.routes';
5
+
6
+ export const appConfig: ApplicationConfig = {
7
+ providers: [
8
+ provideBrowserGlobalErrorListeners(),
9
+ provideRouter(routes)
10
+ ]
11
+ };
@@ -0,0 +1,107 @@
1
+ :host {
2
+ display: block;
3
+ min-height: 100dvh;
4
+ background:
5
+ radial-gradient(circle at 10% 5%, rgba(15, 118, 110, 0.12), transparent 35%),
6
+ radial-gradient(circle at 90% 0%, rgba(59, 130, 246, 0.15), transparent 32%),
7
+ #f7fafc;
8
+ }
9
+
10
+ .welcome {
11
+ max-width: 980px;
12
+ margin: 0 auto;
13
+ padding: 32px 20px 40px;
14
+ display: grid;
15
+ gap: 16px;
16
+ color: #0f172a;
17
+ font-family: var(--mf-font-base, 'Segoe UI', sans-serif);
18
+ }
19
+
20
+ .hero {
21
+ background: #ffffff;
22
+ border: 1px solid #d9e4ec;
23
+ border-radius: 16px;
24
+ padding: 24px;
25
+ box-shadow: 0 10px 30px rgba(15, 23, 42, 0.06);
26
+ }
27
+
28
+ .badge {
29
+ display: inline-flex;
30
+ margin: 0 0 10px;
31
+ padding: 4px 10px;
32
+ border-radius: 999px;
33
+ background: #dcfce7;
34
+ color: #166534;
35
+ font-weight: 600;
36
+ font-size: 12px;
37
+ letter-spacing: 0.02em;
38
+ text-transform: uppercase;
39
+ }
40
+
41
+ h1 {
42
+ margin: 0;
43
+ font-family: var(--mf-font-display, 'Segoe UI', sans-serif);
44
+ font-size: clamp(1.8rem, 4vw, 2.4rem);
45
+ line-height: 1.1;
46
+ }
47
+
48
+ .lead {
49
+ margin: 10px 0 0;
50
+ color: #334155;
51
+ max-width: 62ch;
52
+ }
53
+
54
+ .card {
55
+ background: #ffffff;
56
+ border: 1px solid #d9e4ec;
57
+ border-radius: 16px;
58
+ padding: 20px;
59
+ box-shadow: 0 8px 26px rgba(15, 23, 42, 0.05);
60
+ }
61
+
62
+ .card h2 {
63
+ margin: 0 0 10px;
64
+ font-size: 1.1rem;
65
+ }
66
+
67
+ .card p {
68
+ margin: 0 0 10px;
69
+ color: #334155;
70
+ }
71
+
72
+ pre {
73
+ margin: 0;
74
+ padding: 12px;
75
+ border-radius: 10px;
76
+ background: #0b1220;
77
+ color: #e2e8f0;
78
+ border: 1px solid #1e293b;
79
+ overflow-x: auto;
80
+ }
81
+
82
+ code {
83
+ font-family: 'Cascadia Code', Consolas, monospace;
84
+ font-size: 0.88rem;
85
+ }
86
+
87
+ ul {
88
+ margin: 0;
89
+ padding-left: 18px;
90
+ color: #334155;
91
+ }
92
+
93
+ li + li {
94
+ margin-top: 6px;
95
+ }
96
+
97
+ @media (max-width: 640px) {
98
+ .welcome {
99
+ padding: 20px 12px 28px;
100
+ }
101
+
102
+ .hero,
103
+ .card {
104
+ padding: 16px;
105
+ border-radius: 12px;
106
+ }
107
+ }
@@ -0,0 +1,48 @@
1
+ <main class="welcome">
2
+ <header class="hero">
3
+ <p class="badge">UI Library</p>
4
+ <h1>Welcome to {{ title() }}</h1>
5
+ <p class="lead">
6
+ This page explains how to install and start using <strong>ng-comps</strong>
7
+ in any Angular project.
8
+ </p>
9
+ </header>
10
+
11
+ <section class="card" aria-labelledby="install-title">
12
+ <h2 id="install-title">1) Install package</h2>
13
+ <pre><code>npm i ng-comps</code></pre>
14
+ </section>
15
+
16
+ <section class="card" aria-labelledby="styles-title">
17
+ <h2 id="styles-title">2) Load base styles and design tokens</h2>
18
+ <p>Add these imports in your global styles file (for example <code>src/styles.css</code>):</p>
19
+ <pre><code>@import 'ng-comps/theme/tokens.css';
20
+ @import 'ng-comps/styles.css';</code></pre>
21
+ </section>
22
+
23
+ <section class="card" aria-labelledby="usage-title">
24
+ <h2 id="usage-title">3) Use components in standalone Angular pages</h2>
25
+ <p>Import only what you need for better tree-shaking.</p>
26
+ <pre><code>import &#123; Component &#125; from '@angular/core';
27
+ import &#123; MfButtonComponent, MfInputComponent &#125; from 'ng-comps';
28
+
29
+ @Component(&#123;
30
+ selector: 'app-example',
31
+ imports: [MfButtonComponent, MfInputComponent],
32
+ template: `
33
+ &lt;mf-input label="Email" placeholder="you@company.com" /&gt;
34
+ &lt;mf-button label="Save" variant="filled" /&gt;
35
+ `,
36
+ &#125;)
37
+ export class ExampleComponent &#123;&#125;</code></pre>
38
+ </section>
39
+
40
+ <section class="card" aria-labelledby="notes-title">
41
+ <h2 id="notes-title">4) Notes for production projects</h2>
42
+ <ul>
43
+ <li>The package is published with <code>sideEffects: false</code> for max tree-shaking.</li>
44
+ <li>Angular and RxJS are peer dependencies to avoid duplicated framework installs.</li>
45
+ <li>Import components directly from <code>ng-comps</code>; avoid wildcard barrel files in your app.</li>
46
+ </ul>
47
+ </section>
48
+ </main>
@@ -0,0 +1,3 @@
1
+ import { Routes } from '@angular/router';
2
+
3
+ export const routes: Routes = [];
@@ -0,0 +1,23 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+ import { App } from './app';
3
+
4
+ describe('App', () => {
5
+ beforeEach(async () => {
6
+ await TestBed.configureTestingModule({
7
+ imports: [App],
8
+ }).compileComponents();
9
+ });
10
+
11
+ it('should create the app', () => {
12
+ const fixture = TestBed.createComponent(App);
13
+ const app = fixture.componentInstance;
14
+ expect(app).toBeTruthy();
15
+ });
16
+
17
+ it('should render title', async () => {
18
+ const fixture = TestBed.createComponent(App);
19
+ await fixture.whenStable();
20
+ const compiled = fixture.nativeElement as HTMLElement;
21
+ expect(compiled.querySelector('h1')?.textContent).toContain('Welcome to ng-comps');
22
+ });
23
+ });
package/src/app/app.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { Component, signal } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'app-root',
5
+ templateUrl: './app.html',
6
+ styleUrl: './app.css'
7
+ })
8
+ export class App {
9
+ protected readonly title = signal('ng-comps');
10
+ }
@@ -0,0 +1,2 @@
1
+ export { MfAccordionComponent } from './mf-accordion.component';
2
+ export type { MfAccordionPanel } from './mf-accordion.component';
@@ -0,0 +1,38 @@
1
+ :host {
2
+ display: block;
3
+ }
4
+
5
+ .mf-accordion .mat-expansion-panel {
6
+ font-family: var(--mf-font-base) !important;
7
+ border-radius: var(--mf-radius-md) !important;
8
+ box-shadow: var(--mf-shadow-none) !important;
9
+ border: 1px solid var(--mf-color-border);
10
+ margin-bottom: var(--mf-space-2);
11
+ }
12
+
13
+ .mf-accordion .mat-expansion-panel-header {
14
+ font-family: var(--mf-font-base) !important;
15
+ }
16
+
17
+ .mf-accordion .mat-expansion-panel-header-title {
18
+ font-weight: var(--mf-weight-bold) !important;
19
+ color: var(--mf-color-on-surface) !important;
20
+ }
21
+
22
+ .mf-accordion .mat-expansion-panel-header-description {
23
+ color: var(--mf-color-neutral-400) !important;
24
+ }
25
+
26
+ .mf-accordion .mat-expansion-panel-body {
27
+ font-size: var(--mf-text-sm) !important;
28
+ color: var(--mf-color-neutral-600) !important;
29
+ line-height: var(--mf-leading-normal);
30
+ }
31
+
32
+ .mf-accordion .mat-expansion-indicator::after {
33
+ color: var(--mf-color-brand) !important;
34
+ }
35
+
36
+ .mf-accordion .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:hover {
37
+ background-color: var(--mf-color-brand-light) !important;
38
+ }
@@ -0,0 +1,48 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { MfAccordionComponent } from './mf-accordion.component';
3
+ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
4
+
5
+ describe('MfAccordionComponent', () => {
6
+ let fixture: ComponentFixture<MfAccordionComponent>;
7
+ let component: MfAccordionComponent;
8
+
9
+ const testPanels = [
10
+ { title: 'Panel 1', content: 'Content 1' },
11
+ { title: 'Panel 2', description: 'Description', content: 'Content 2' },
12
+ { title: 'Panel 3', content: 'Content 3', disabled: true },
13
+ ];
14
+
15
+ beforeEach(async () => {
16
+ await TestBed.configureTestingModule({
17
+ imports: [MfAccordionComponent, NoopAnimationsModule],
18
+ }).compileComponents();
19
+
20
+ fixture = TestBed.createComponent(MfAccordionComponent);
21
+ component = fixture.componentInstance;
22
+ fixture.componentRef.setInput('panels', testPanels);
23
+ fixture.detectChanges();
24
+ });
25
+
26
+ it('should create', () => {
27
+ expect(component).toBeTruthy();
28
+ });
29
+
30
+ it('should render expansion panels', () => {
31
+ const panels = fixture.nativeElement.querySelectorAll('mat-expansion-panel');
32
+ expect(panels.length).toBe(3);
33
+ });
34
+
35
+ it('should apply host classes', () => {
36
+ expect(component.hostClasses()).toBe('mf-accordion');
37
+ });
38
+
39
+ it('should not allow multi by default', () => {
40
+ expect(component.multi()).toBe(false);
41
+ });
42
+
43
+ it('should render panel titles', () => {
44
+ const titles = fixture.nativeElement.querySelectorAll('mat-panel-title');
45
+ expect(titles.length).toBe(3);
46
+ expect(titles[0].textContent).toContain('Panel 1');
47
+ });
48
+ });