@fragments-sdk/cli 0.5.2 → 0.6.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 (118) hide show
  1. package/dist/bin.js +712 -39
  2. package/dist/bin.js.map +1 -1
  3. package/dist/{chunk-ICAIQ57V.js → chunk-6JBGU74P.js} +5 -3
  4. package/dist/chunk-6JBGU74P.js.map +1 -0
  5. package/dist/{chunk-U4GQ2JTD.js → chunk-D35RGPAG.js} +412 -35
  6. package/dist/chunk-D35RGPAG.js.map +1 -0
  7. package/dist/{chunk-XNWDI6UT.js → chunk-F7ITZPDJ.js} +5 -5
  8. package/dist/{chunk-IOJE35DZ.js → chunk-NWQ4CJOQ.js} +3 -3
  9. package/dist/{chunk-V7YLRR4C.js → chunk-Q7GOHVOK.js} +3 -3
  10. package/dist/{chunk-2DJH4F4P.js → chunk-RVRTRESS.js} +3 -3
  11. package/dist/{chunk-2H2JAA3U.js → chunk-SSLQXHNX.js} +3 -3
  12. package/dist/{core-DKHB7FYV.js → core-SKRPJQZG.js} +4 -4
  13. package/dist/{generate-KL24VZVD.js → generate-7AF7WRVK.js} +5 -5
  14. package/dist/index.d.ts +1 -0
  15. package/dist/index.js +15 -7
  16. package/dist/index.js.map +1 -1
  17. package/dist/{init-NION5S3M.js → init-WKGDPYI4.js} +5 -5
  18. package/dist/mcp-bin.js +8 -220
  19. package/dist/mcp-bin.js.map +1 -1
  20. package/dist/scan-K6JNMCGM.js +12 -0
  21. package/dist/{service-RWUMZ3EW.js → service-F3E4JJM7.js} +5 -5
  22. package/dist/static-viewer-4LQZ5AGA.js +12 -0
  23. package/dist/{test-ECPEXFDN.js → test-CJDNJTPZ.js} +4 -4
  24. package/dist/{tokens-ITADYVPF.js → tokens-JAJABYXP.js} +6 -6
  25. package/dist/viewer-R3Q6WAMJ.js +1822 -0
  26. package/dist/viewer-R3Q6WAMJ.js.map +1 -0
  27. package/package.json +5 -4
  28. package/src/bin.ts +8 -0
  29. package/src/build.ts +104 -13
  30. package/src/cli-commands.ts +18 -0
  31. package/src/commands/__tests__/a11y-scoring.test.ts +278 -0
  32. package/src/commands/a11y-report.ts +625 -0
  33. package/src/commands/a11y.ts +168 -14
  34. package/src/commands/build.ts +16 -0
  35. package/src/core/auto-props.ts +464 -0
  36. package/src/core/schema.ts +2 -0
  37. package/src/core/types.ts +3 -1
  38. package/src/index.ts +4 -0
  39. package/src/mcp/server.ts +13 -220
  40. package/src/theme/__tests__/component-contrast.test.ts +338 -0
  41. package/src/theme/__tests__/contrast-validation.test.ts +326 -0
  42. package/src/theme/contrast.test.ts +331 -0
  43. package/src/theme/contrast.ts +246 -0
  44. package/src/theme/generator.ts +213 -1
  45. package/src/theme/index.ts +16 -0
  46. package/src/theme/types.ts +51 -0
  47. package/src/viewer/__tests__/a11y-fixes.test.ts +358 -0
  48. package/src/viewer/__tests__/viewer-integration.test.ts +2 -7
  49. package/src/viewer/components/AccessibilityPanel.tsx +493 -433
  50. package/src/viewer/components/ActionCapture.tsx +1 -1
  51. package/src/viewer/components/ActionsPanel.tsx +142 -183
  52. package/src/viewer/components/App.tsx +159 -164
  53. package/src/viewer/components/BottomPanel.tsx +40 -80
  54. package/src/viewer/components/CodePanel.tsx +9 -87
  55. package/src/viewer/components/CommandPalette.tsx +117 -74
  56. package/src/viewer/components/ComponentGraph.tsx +143 -126
  57. package/src/viewer/components/ComponentHeader.tsx +46 -43
  58. package/src/viewer/components/ContractPanel.tsx +124 -117
  59. package/src/viewer/components/ErrorBoundary.tsx +47 -35
  60. package/src/viewer/components/FigmaEmbed.tsx +18 -13
  61. package/src/viewer/components/FragmentEditor.tsx +126 -63
  62. package/src/viewer/components/HealthDashboard.tsx +146 -171
  63. package/src/viewer/components/HmrStatusIndicator.tsx +31 -41
  64. package/src/viewer/components/Icons.tsx +99 -98
  65. package/src/viewer/components/InteractionsPanel.tsx +317 -264
  66. package/src/viewer/components/IsolatedPreviewFrame.tsx +52 -27
  67. package/src/viewer/components/IsolatedRender.tsx +12 -6
  68. package/src/viewer/components/KeyboardShortcutsHelp.tsx +34 -70
  69. package/src/viewer/components/LandingPage.tsx +285 -305
  70. package/src/viewer/components/Layout.tsx +7 -9
  71. package/src/viewer/components/LeftSidebar.tsx +78 -108
  72. package/src/viewer/components/MultiViewportPreview.tsx +254 -63
  73. package/src/viewer/components/PreviewArea.tsx +113 -44
  74. package/src/viewer/components/PreviewFrameHost.tsx +6 -5
  75. package/src/viewer/components/PreviewPane.tsx +2 -3
  76. package/src/viewer/components/PreviewToolbar.tsx +61 -104
  77. package/src/viewer/components/PropsEditor.tsx +154 -74
  78. package/src/viewer/components/PropsTable.tsx +95 -82
  79. package/src/viewer/components/RelationsSection.tsx +71 -40
  80. package/src/viewer/components/ResizablePanel.tsx +158 -55
  81. package/src/viewer/components/RightSidebar.tsx +46 -56
  82. package/src/viewer/components/ScreenshotButton.tsx +12 -12
  83. package/src/viewer/components/SkeletonLoader.tsx +99 -83
  84. package/src/viewer/components/StoryRenderer.tsx +4 -11
  85. package/src/viewer/components/Toast.tsx +3 -67
  86. package/src/viewer/components/TokenStylePanel.tsx +136 -118
  87. package/src/viewer/components/UsageSection.tsx +26 -26
  88. package/src/viewer/components/VariantMatrix.tsx +140 -47
  89. package/src/viewer/components/VariantTabs.tsx +24 -68
  90. package/src/viewer/components/ViewportSelector.tsx +106 -110
  91. package/src/viewer/constants/ui.ts +19 -18
  92. package/src/viewer/entry.tsx +8 -3
  93. package/src/viewer/index.ts +3 -6
  94. package/src/viewer/preview-frame.html +21 -5
  95. package/src/viewer/server.ts +7 -16
  96. package/src/viewer/styles/globals.css +4 -4
  97. package/src/viewer/utils/a11y-fixes.ts +53 -30
  98. package/dist/chunk-ICAIQ57V.js.map +0 -1
  99. package/dist/chunk-U4GQ2JTD.js.map +0 -1
  100. package/dist/scan-ESEXV7LF.js +0 -12
  101. package/dist/static-viewer-O37MJ5B6.js +0 -12
  102. package/dist/viewer-YDGFDTK5.js +0 -11104
  103. package/dist/viewer-YDGFDTK5.js.map +0 -1
  104. package/src/viewer/postcss.config.js +0 -6
  105. package/src/viewer/tailwind.config.js +0 -37
  106. /package/dist/{chunk-XNWDI6UT.js.map → chunk-F7ITZPDJ.js.map} +0 -0
  107. /package/dist/{chunk-IOJE35DZ.js.map → chunk-NWQ4CJOQ.js.map} +0 -0
  108. /package/dist/{chunk-V7YLRR4C.js.map → chunk-Q7GOHVOK.js.map} +0 -0
  109. /package/dist/{chunk-2DJH4F4P.js.map → chunk-RVRTRESS.js.map} +0 -0
  110. /package/dist/{chunk-2H2JAA3U.js.map → chunk-SSLQXHNX.js.map} +0 -0
  111. /package/dist/{core-DKHB7FYV.js.map → core-SKRPJQZG.js.map} +0 -0
  112. /package/dist/{generate-KL24VZVD.js.map → generate-7AF7WRVK.js.map} +0 -0
  113. /package/dist/{init-NION5S3M.js.map → init-WKGDPYI4.js.map} +0 -0
  114. /package/dist/{scan-ESEXV7LF.js.map → scan-K6JNMCGM.js.map} +0 -0
  115. /package/dist/{service-RWUMZ3EW.js.map → service-F3E4JJM7.js.map} +0 -0
  116. /package/dist/{static-viewer-O37MJ5B6.js.map → static-viewer-4LQZ5AGA.js.map} +0 -0
  117. /package/dist/{test-ECPEXFDN.js.map → test-CJDNJTPZ.js.map} +0 -0
  118. /package/dist/{tokens-ITADYVPF.js.map → tokens-JAJABYXP.js.map} +0 -0
@@ -0,0 +1,278 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { calculateA11yScore, formatGitHub } from '../a11y.js';
3
+ import type { A11ySummary, A11yComponentResult, A11yScore } from '../a11y.js';
4
+
5
+ function makeSummary(overrides?: Partial<A11ySummary>): A11ySummary {
6
+ return {
7
+ totalComponents: 5,
8
+ accessibleComponents: 5,
9
+ accessiblePercent: 100,
10
+ components: [],
11
+ totalViolations: 0,
12
+ totalCritical: 0,
13
+ totalSerious: 0,
14
+ totalModerate: 0,
15
+ totalMinor: 0,
16
+ passed: true,
17
+ ...overrides,
18
+ };
19
+ }
20
+
21
+ function makeComponent(overrides?: Partial<A11yComponentResult>): A11yComponentResult {
22
+ return {
23
+ component: 'Button',
24
+ results: [],
25
+ status: 'PASS',
26
+ totalViolations: 0,
27
+ totalCritical: 0,
28
+ totalSerious: 0,
29
+ ...overrides,
30
+ };
31
+ }
32
+
33
+ describe('calculateA11yScore', () => {
34
+ describe('score calculation', () => {
35
+ it('0 violations → 100', () => {
36
+ const score = calculateA11yScore(makeSummary());
37
+ expect(score.score).toBe(100);
38
+ });
39
+
40
+ it('critical violations deduct 10 each', () => {
41
+ const score = calculateA11yScore(makeSummary({ totalCritical: 3 }));
42
+ expect(score.score).toBe(70);
43
+ });
44
+
45
+ it('serious violations deduct 5 each', () => {
46
+ const score = calculateA11yScore(makeSummary({ totalSerious: 4 }));
47
+ expect(score.score).toBe(80);
48
+ });
49
+
50
+ it('moderate violations deduct 2 each', () => {
51
+ const score = calculateA11yScore(makeSummary({ totalModerate: 5 }));
52
+ expect(score.score).toBe(90);
53
+ });
54
+
55
+ it('minor violations deduct 1 each', () => {
56
+ const score = calculateA11yScore(makeSummary({ totalMinor: 10 }));
57
+ expect(score.score).toBe(90);
58
+ });
59
+
60
+ it('mixed violations: 2 critical + 3 serious + 5 moderate + 10 minor = 45', () => {
61
+ const score = calculateA11yScore(makeSummary({
62
+ totalCritical: 2,
63
+ totalSerious: 3,
64
+ totalModerate: 5,
65
+ totalMinor: 10,
66
+ }));
67
+ // 100 - 20 - 15 - 10 - 10 = 45
68
+ expect(score.score).toBe(45);
69
+ });
70
+
71
+ it('floors at 0 when deductions exceed 100', () => {
72
+ const score = calculateA11yScore(makeSummary({ totalCritical: 15 }));
73
+ expect(score.score).toBe(0);
74
+ });
75
+ });
76
+
77
+ describe('aaPercent', () => {
78
+ it('all clean components → 100%', () => {
79
+ const components = [
80
+ makeComponent({ component: 'Button' }),
81
+ makeComponent({ component: 'Input' }),
82
+ ];
83
+ const score = calculateA11yScore(makeSummary({
84
+ totalComponents: 2,
85
+ components,
86
+ }));
87
+ expect(score.aaPercent).toBe(100);
88
+ });
89
+
90
+ it('all failing components → 0%', () => {
91
+ const components = [
92
+ makeComponent({ component: 'Button', totalCritical: 2, totalSerious: 0 }),
93
+ makeComponent({ component: 'Input', totalCritical: 0, totalSerious: 1 }),
94
+ ];
95
+ const score = calculateA11yScore(makeSummary({
96
+ totalComponents: 2,
97
+ components,
98
+ }));
99
+ expect(score.aaPercent).toBe(0);
100
+ });
101
+
102
+ it('partial → correct percentage', () => {
103
+ const components = [
104
+ makeComponent({ component: 'Button' }), // passes AA
105
+ makeComponent({ component: 'Input', totalCritical: 1 }), // fails AA
106
+ makeComponent({ component: 'Card' }), // passes AA
107
+ makeComponent({ component: 'Dialog', totalSerious: 2 }), // fails AA
108
+ ];
109
+ const score = calculateA11yScore(makeSummary({
110
+ totalComponents: 4,
111
+ components,
112
+ }));
113
+ expect(score.aaPercent).toBe(50); // 2/4
114
+ });
115
+
116
+ it('0 components → 100%', () => {
117
+ const score = calculateA11yScore(makeSummary({ totalComponents: 0, components: [] }));
118
+ expect(score.aaPercent).toBe(100);
119
+ });
120
+ });
121
+
122
+ describe('aaaPercent', () => {
123
+ it('zero violations → 100%', () => {
124
+ const components = [
125
+ makeComponent({ component: 'Button', totalViolations: 0 }),
126
+ makeComponent({ component: 'Input', totalViolations: 0 }),
127
+ ];
128
+ const score = calculateA11yScore(makeSummary({
129
+ totalComponents: 2,
130
+ components,
131
+ }));
132
+ expect(score.aaaPercent).toBe(100);
133
+ });
134
+
135
+ it('any violations → 0% for affected components', () => {
136
+ const components = [
137
+ makeComponent({ component: 'Button', totalViolations: 1 }),
138
+ makeComponent({ component: 'Input', totalViolations: 0 }),
139
+ ];
140
+ const score = calculateA11yScore(makeSummary({
141
+ totalComponents: 2,
142
+ components,
143
+ }));
144
+ expect(score.aaaPercent).toBe(50);
145
+ });
146
+
147
+ it('all with violations → 0%', () => {
148
+ const components = [
149
+ makeComponent({ component: 'Button', totalViolations: 2 }),
150
+ makeComponent({ component: 'Input', totalViolations: 1 }),
151
+ ];
152
+ const score = calculateA11yScore(makeSummary({
153
+ totalComponents: 2,
154
+ components,
155
+ }));
156
+ expect(score.aaaPercent).toBe(0);
157
+ });
158
+
159
+ it('aaPercent >= aaaPercent', () => {
160
+ const components = [
161
+ makeComponent({ component: 'Button', totalViolations: 1, totalCritical: 0, totalSerious: 0 }),
162
+ makeComponent({ component: 'Input', totalViolations: 0, totalCritical: 0, totalSerious: 0 }),
163
+ ];
164
+ const score = calculateA11yScore(makeSummary({
165
+ totalComponents: 2,
166
+ components,
167
+ }));
168
+ expect(score.aaPercent).toBeGreaterThanOrEqual(score.aaaPercent);
169
+ });
170
+ });
171
+ });
172
+
173
+ describe('formatGitHub', () => {
174
+ describe('badges', () => {
175
+ it('passed=true → "passing" + brightgreen', () => {
176
+ const output = formatGitHub(makeSummary({ passed: true }));
177
+ expect(output).toContain('passing');
178
+ expect(output).toContain('brightgreen');
179
+ });
180
+
181
+ it('passed=false → "failing" + red', () => {
182
+ const output = formatGitHub(makeSummary({
183
+ passed: false,
184
+ totalCritical: 1,
185
+ components: [makeComponent({ component: 'Button', totalCritical: 1 })],
186
+ }));
187
+ expect(output).toContain('failing');
188
+ expect(output).toContain('-red)');
189
+ });
190
+ });
191
+
192
+ describe('content', () => {
193
+ it('includes markdown table header', () => {
194
+ const output = formatGitHub(makeSummary({
195
+ components: [makeComponent()],
196
+ }));
197
+ expect(output).toContain('| Component |');
198
+ expect(output).toContain('| Variants |');
199
+ });
200
+
201
+ it('includes component names in table rows', () => {
202
+ const components = [
203
+ makeComponent({ component: 'Button' }),
204
+ makeComponent({ component: 'Dialog' }),
205
+ ];
206
+ const output = formatGitHub(makeSummary({
207
+ totalComponents: 2,
208
+ components,
209
+ }));
210
+ expect(output).toContain('| Button |');
211
+ expect(output).toContain('| Dialog |');
212
+ });
213
+
214
+ it('includes blocking count when critical + serious > 0', () => {
215
+ const output = formatGitHub(makeSummary({
216
+ passed: false,
217
+ totalCritical: 2,
218
+ totalSerious: 3,
219
+ components: [makeComponent({ component: 'Button', totalCritical: 2, totalSerious: 3 })],
220
+ }));
221
+ expect(output).toContain('**Blocking:** 5');
222
+ });
223
+
224
+ it('no blocking line when 0 critical + serious', () => {
225
+ const output = formatGitHub(makeSummary({
226
+ totalCritical: 0,
227
+ totalSerious: 0,
228
+ totalModerate: 2,
229
+ components: [makeComponent({ component: 'Button', totalViolations: 2 })],
230
+ }));
231
+ expect(output).not.toContain('**Blocking:**');
232
+ });
233
+ });
234
+
235
+ describe('score', () => {
236
+ it('uses pre-calculated score when present', () => {
237
+ const preCalc: A11yScore = { score: 42, aaPercent: 50, aaaPercent: 25 };
238
+ const output = formatGitHub(makeSummary({ score: preCalc }));
239
+ expect(output).toContain('42/100');
240
+ expect(output).toContain('50%');
241
+ expect(output).toContain('25%');
242
+ });
243
+
244
+ it('calculates score when missing', () => {
245
+ const summary = makeSummary({ totalMinor: 5 });
246
+ delete summary.score;
247
+ const output = formatGitHub(summary);
248
+ // 100 - 5 = 95
249
+ expect(output).toContain('95/100');
250
+ });
251
+ });
252
+
253
+ describe('violations', () => {
254
+ it('summary line includes all 4 severity counts', () => {
255
+ const output = formatGitHub(makeSummary({
256
+ totalViolations: 10,
257
+ totalCritical: 1,
258
+ totalSerious: 2,
259
+ totalModerate: 3,
260
+ totalMinor: 4,
261
+ components: [makeComponent({ component: 'Button', totalViolations: 10, totalCritical: 1, totalSerious: 2 })],
262
+ }));
263
+ expect(output).toContain('1 critical');
264
+ expect(output).toContain('2 serious');
265
+ expect(output).toContain('3 moderate');
266
+ expect(output).toContain('4 minor');
267
+ });
268
+
269
+ it('shows accessible components fraction', () => {
270
+ const output = formatGitHub(makeSummary({
271
+ totalComponents: 10,
272
+ accessibleComponents: 8,
273
+ accessiblePercent: 80,
274
+ }));
275
+ expect(output).toContain('8/10 components accessible');
276
+ });
277
+ });
278
+ });