@moontra/moonui 6.17.0 → 6.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +189 -108
- package/dist/index.js +114 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +114 -28
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -5
- package/src/__tests__/cdn-iife-removed.test.ts +103 -0
- package/src/components/ui/__tests__/dark-token-contrast.test.ts +162 -0
- package/src/components/ui/__tests__/data-table-basic.test.tsx +109 -0
- package/src/components/ui/__tests__/file-upload.issue-378.test.tsx +226 -0
- package/src/components/ui/__tests__/progress-secondary-contrast.test.ts +231 -0
- package/src/components/ui/__tests__/progress.test.tsx +4 -1
- package/src/components/ui/__tests__/responsive-color-invariance.test.ts +92 -0
- package/src/components/ui/__tests__/slider-secondary-contrast.test.ts +375 -0
- package/src/components/ui/data-table-basic.tsx +59 -8
- package/src/components/ui/file-upload.tsx +51 -16
- package/src/components/ui/progress.tsx +14 -1
- package/src/components/ui/slider.tsx +26 -2
- package/src/styles/tokens.css +30 -3
- package/dist/chunk-VXDFZPDA.global.js +0 -33
- package/dist/chunk-VXDFZPDA.global.js.map +0 -1
- package/dist/hooks/index.global.js +0 -33
- package/dist/hooks/index.global.js.map +0 -1
- package/dist/index.global.js +0 -980
- package/dist/index.global.js.map +0 -1
- package/dist/lib/theme.global.js +0 -54
- package/dist/lib/theme.global.js.map +0 -1
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #372 — Progress `variant="secondary"` dolgu↔track kontrast değişmezi.
|
|
3
|
+
*
|
|
4
|
+
* BULGU (#318'in ardılı): #318 `indicatorVariant ?? variant` düşüşünü kurunca
|
|
5
|
+
* `variant="secondary"` dolguya İLK KEZ ulaştı — ve ulaşan değer görünmez çıktı.
|
|
6
|
+
* Dolgu `bg-secondary`, track `bg-secondary/20`: ikisi de AYNI yüzey token'ına
|
|
7
|
+
* basıyor. `--secondary` light'ta rgb(235,239,245), dark'ta rgb(2,8,23) — %20
|
|
8
|
+
* alfa track ile tam dolgu iki temada da ~1.0 oranında, bar hiç görünmüyordu.
|
|
9
|
+
* axe bunu yakalamaz: `color-contrast` yalnız METİN öğelerini ölçer.
|
|
10
|
+
*
|
|
11
|
+
* DÜZELTME: dolgu `bg-[rgb(var(--secondary-500))]` (tokens.css slate skalası).
|
|
12
|
+
* `bg-secondary-foreground` BİLEREK seçilmedi — light'ta --primary ile aynı
|
|
13
|
+
* hue/sat'ta ΔL=%2, dark'ta birebir --primary değeri: varyant primary klonuna
|
|
14
|
+
* dönerdi. Track `bg-secondary/20` KALDI (issue #372 seçenek 1; track↔zemin
|
|
15
|
+
* ayrışması ayrı token-ekseni bulgusudur, kapsam dışı).
|
|
16
|
+
*
|
|
17
|
+
* Kabul kriteri (issue #372 §3): dolgu↔track kontrastı light ve dark temada
|
|
18
|
+
* ≥3:1 (WCAG 1.4.11 non-text eşiği). Buradaki testler hem bu eşiği hem de
|
|
19
|
+
* "500 iki temada da geçen TEK kademe" seçim gerekçesini kilitler.
|
|
20
|
+
*
|
|
21
|
+
* Hesap yöntemi primary-subtle-token.test.ts ile birebir aynıdır (WCAG 2.x
|
|
22
|
+
* bağıl parlaklık); oradaki "hesaplayıcı doğru mu?" çapası burada da var.
|
|
23
|
+
* NOT: #375'in dark-token-contrast.test.ts dosyasıyla bilerek AYRI dosya —
|
|
24
|
+
* o PR --destructive/--muted/--muted-foreground'u değiştirir, --secondary'ye
|
|
25
|
+
* ve --secondary-500 skalasına dokunmaz (diff'i teyit edildi, çakışma yok).
|
|
26
|
+
*/
|
|
27
|
+
import * as React from 'react'
|
|
28
|
+
import { render, screen } from '@testing-library/react'
|
|
29
|
+
import '@testing-library/jest-dom'
|
|
30
|
+
import * as fs from 'fs'
|
|
31
|
+
import * as path from 'path'
|
|
32
|
+
import { Progress } from '../progress'
|
|
33
|
+
|
|
34
|
+
const KOK = path.resolve(__dirname, '../../..')
|
|
35
|
+
const tokens = fs.readFileSync(path.join(KOK, 'styles/tokens.css'), 'utf8')
|
|
36
|
+
|
|
37
|
+
/** "H S% L%" biçimindeki token değerini oku (light = :root, dark = .dark bloğu) */
|
|
38
|
+
function tokenOku(ad: string, blok: 'light' | 'dark'): string | null {
|
|
39
|
+
const kaynak =
|
|
40
|
+
blok === 'dark'
|
|
41
|
+
? tokens.slice(tokens.indexOf('.dark'))
|
|
42
|
+
: tokens.slice(0, tokens.indexOf('.dark'))
|
|
43
|
+
return kaynak.match(new RegExp(`--${ad}:\\s*([^;]+);`))?.[1].trim() ?? null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function hslToRgb(h: number, s: number, l: number): [number, number, number] {
|
|
47
|
+
h /= 360
|
|
48
|
+
s /= 100
|
|
49
|
+
l /= 100
|
|
50
|
+
const f = (n: number) => {
|
|
51
|
+
const k = (n + h * 12) % 12
|
|
52
|
+
const a = s * Math.min(l, 1 - l)
|
|
53
|
+
return l - a * Math.max(-1, Math.min(k - 3, Math.min(9 - k, 1)))
|
|
54
|
+
}
|
|
55
|
+
return [Math.round(255 * f(0)), Math.round(255 * f(8)), Math.round(255 * f(4))]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const hslTokenRgb = (v: string): [number, number, number] => {
|
|
59
|
+
const [h, s, l] = v.split(/\s+/).map((x) => parseFloat(x))
|
|
60
|
+
return hslToRgb(h, s, l)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Skala token'ları ("--secondary-500: 100 116 139") HSL DEĞİL, RGB üçlüsüdür
|
|
65
|
+
* (slate-500 = rgb(100,116,139) ile birebir; component'te bu yüzden
|
|
66
|
+
* `bg-[rgb(var(--secondary-500))]` kompozisyonu kullanılır, hsl() değil).
|
|
67
|
+
*/
|
|
68
|
+
const rgbTokenRgb = (v: string): [number, number, number] => {
|
|
69
|
+
const p = v.split(/\s+/).map((x) => parseFloat(x))
|
|
70
|
+
return [p[0], p[1], p[2]]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function bagilParlaklik([r, g, b]: [number, number, number]): number {
|
|
74
|
+
const c = (v: number) => {
|
|
75
|
+
const x = v / 255
|
|
76
|
+
return x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4)
|
|
77
|
+
}
|
|
78
|
+
return 0.2126 * c(r) + 0.7152 * c(g) + 0.0722 * c(b)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function rgbKontrast(a: [number, number, number], b: [number, number, number]): number {
|
|
82
|
+
const la = bagilParlaklik(a)
|
|
83
|
+
const lb = bagilParlaklik(b)
|
|
84
|
+
const [hi, lo] = la > lb ? [la, lb] : [lb, la]
|
|
85
|
+
return (hi + 0.05) / (lo + 0.05)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** `bg-secondary/20` gibi alfa katmanının zemin üstündeki bileşke rengi */
|
|
89
|
+
function alfaBileske(
|
|
90
|
+
ust: [number, number, number],
|
|
91
|
+
alfa: number,
|
|
92
|
+
zemin: [number, number, number]
|
|
93
|
+
): [number, number, number] {
|
|
94
|
+
return [
|
|
95
|
+
Math.round(alfa * ust[0] + (1 - alfa) * zemin[0]),
|
|
96
|
+
Math.round(alfa * ust[1] + (1 - alfa) * zemin[1]),
|
|
97
|
+
Math.round(alfa * ust[2] + (1 - alfa) * zemin[2]),
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Docs kabuğunda ölçülen GERÇEK koyu kart zemini (#111827 = rgb(17,24,39)) —
|
|
103
|
+
* #284/#368 konvansiyonu: kontrast, --background'ın yanında bu gerçek yüzeye
|
|
104
|
+
* göre de ölçülür (tokens.css --input notu).
|
|
105
|
+
*/
|
|
106
|
+
const KART_GERCEK_DARK: [number, number, number] = [17, 24, 39]
|
|
107
|
+
|
|
108
|
+
/** Tema başına dolgu (secondary-500) ve track (secondary@%20 ∘ zemin) renkleri */
|
|
109
|
+
function temaRenkleri(blok: 'light' | 'dark') {
|
|
110
|
+
const dolgu = rgbTokenRgb(tokenOku('secondary-500', 'light')!) // skala yalnız :root'ta — iki temada aynı
|
|
111
|
+
const secondary = hslTokenRgb(tokenOku('secondary', blok)!)
|
|
112
|
+
const background = hslTokenRgb(tokenOku('background', blok)!)
|
|
113
|
+
const card = hslTokenRgb(tokenOku('card', blok)!)
|
|
114
|
+
const zeminler: Array<[string, [number, number, number]]> = [
|
|
115
|
+
['--background', background],
|
|
116
|
+
['--card', card],
|
|
117
|
+
]
|
|
118
|
+
if (blok === 'dark') zeminler.push(['gerçek kart #111827', KART_GERCEK_DARK])
|
|
119
|
+
return { dolgu, secondary, zeminler }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
describe('#372 progress secondary dolgu↔track kontrastı', () => {
|
|
123
|
+
it('hesaplayıcı DOĞRU — bilinen bir çift belgelenen oranı veriyor', () => {
|
|
124
|
+
// Sessiz-yeşil koruması: `--info-subtle` çifti tokens.css'te 9.52:1 diye
|
|
125
|
+
// belgelenmiş; hesaplayıcı bozulursa bu çapa kırmızı olur.
|
|
126
|
+
const oran = rgbKontrast(
|
|
127
|
+
hslTokenRgb(tokenOku('info-subtle', 'light')!),
|
|
128
|
+
hslTokenRgb(tokenOku('info-subtle-foreground', 'light')!)
|
|
129
|
+
)
|
|
130
|
+
expect(oran).toBeGreaterThan(9.3)
|
|
131
|
+
expect(oran).toBeLessThan(9.7)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('--secondary-500 tanımlı ve RGB üçlüsü olarak ayrıştırılabiliyor', () => {
|
|
135
|
+
const ham = tokenOku('secondary-500', 'light')
|
|
136
|
+
expect(ham).toBeTruthy()
|
|
137
|
+
const rgb = rgbTokenRgb(ham!)
|
|
138
|
+
rgb.forEach((kanal) => {
|
|
139
|
+
expect(Number.isFinite(kanal)).toBe(true)
|
|
140
|
+
expect(kanal).toBeGreaterThanOrEqual(0)
|
|
141
|
+
expect(kanal).toBeLessThanOrEqual(255)
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
describe('kaynak sözleşmesi: dolgu ile track FARKLI token’lara basıyor', () => {
|
|
146
|
+
const kaynak = fs.readFileSync(path.join(KOK, 'components/ui/progress.tsx'), 'utf8')
|
|
147
|
+
|
|
148
|
+
it('track secondary sınıfı `bg-secondary/20` (issue seçenek 1 — değişmedi)', () => {
|
|
149
|
+
expect(kaynak).toMatch(/secondary:\s*"bg-secondary\/20"/)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('dolgu secondary sınıfı `bg-[rgb(var(--secondary-500))]` (artık --secondary DEĞİL)', () => {
|
|
153
|
+
expect(kaynak).toMatch(/secondary:\s*"bg-\[rgb\(var\(--secondary-500\)\)\]"/)
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
describe('render sözleşmesi: <Progress variant="secondary" />', () => {
|
|
158
|
+
it('track ve dolgu sınıfları DOM’a farklı token’larla iniyor', () => {
|
|
159
|
+
render(React.createElement(Progress, { variant: 'secondary', value: 50 }))
|
|
160
|
+
const track = screen.getByRole('progressbar')
|
|
161
|
+
expect(track).toHaveClass('bg-secondary/20')
|
|
162
|
+
const dolgu = track.firstElementChild as HTMLElement
|
|
163
|
+
expect(dolgu).not.toBeNull()
|
|
164
|
+
expect(dolgu.className).toContain('bg-[rgb(var(--secondary-500))]')
|
|
165
|
+
expect(dolgu.className).not.toMatch(/(?:^|\s)bg-secondary(?:\s|$)/)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('#318 düşüş mantığı KORUNUYOR — indicatorVariant hâlâ variant’ı ezer', () => {
|
|
169
|
+
render(React.createElement(Progress, { variant: 'secondary', indicatorVariant: 'success', value: 50 }))
|
|
170
|
+
const dolgu = screen.getByRole('progressbar').firstElementChild as HTMLElement
|
|
171
|
+
expect(dolgu.className).toContain('bg-success')
|
|
172
|
+
expect(dolgu.className).not.toContain('bg-[rgb(var(--secondary-500))]')
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
describe.each(['light', 'dark'] as const)('%s tema — kabul kriteri ≥3:1 (WCAG 1.4.11)', (blok) => {
|
|
177
|
+
it('YENİ dolgu (secondary-500), track’in her olası zemindeki bileşkesine ≥3:1', () => {
|
|
178
|
+
// Ölçülen değerler: light 4.63 (--background=--card=beyaz) · dark 4.23
|
|
179
|
+
// (--background=--card) / 3.83 (gerçek kart #111827).
|
|
180
|
+
const { dolgu, secondary, zeminler } = temaRenkleri(blok)
|
|
181
|
+
for (const [ad, zemin] of zeminler) {
|
|
182
|
+
const track = alfaBileske(secondary, 0.2, zemin)
|
|
183
|
+
const oran = rgbKontrast(dolgu, track)
|
|
184
|
+
expect({ zemin: ad, oran }).toEqual({ zemin: ad, oran: expect.any(Number) })
|
|
185
|
+
expect(oran).toBeGreaterThanOrEqual(3.0)
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('ESKİ dolgu (bg-secondary) aynı ölçümde başarısızdı — bug’ın belgesi', () => {
|
|
190
|
+
// Biri dolguyu `bg-secondary`ye geri döndürürse bu test ona "eski değer
|
|
191
|
+
// zaten görünmüyordu" diye değil, üstteki kriter testi kırmızıyla söyler;
|
|
192
|
+
// burası ise düzeltmenin GEREKLİLİĞİNİ sabitler (light 1.02, dark ~1.0-1.5).
|
|
193
|
+
const { secondary, zeminler } = temaRenkleri(blok)
|
|
194
|
+
for (const [, zemin] of zeminler) {
|
|
195
|
+
const track = alfaBileske(secondary, 0.2, zemin)
|
|
196
|
+
expect(rgbKontrast(secondary, track)).toBeLessThan(3.0)
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
describe('seçim gerekçesi: 500, iki temada birden geçen TEK skala kademesi', () => {
|
|
202
|
+
// Dolgu her iki temada AYNI renk olduğundan parlaklığı [~0.107, ~0.289]
|
|
203
|
+
// bandında kalmalı: daha açık kademe light track'e (near-white), daha koyu
|
|
204
|
+
// kademe dark track'e (near-black) yetişemiyor. Komşu kademeler bunu kanıtlar.
|
|
205
|
+
it('secondary-400 light temada eşiğin ALTINDA kalır (~2.50)', () => {
|
|
206
|
+
const k400 = rgbTokenRgb(tokenOku('secondary-400', 'light')!)
|
|
207
|
+
const { secondary, zeminler } = temaRenkleri('light')
|
|
208
|
+
const track = alfaBileske(secondary, 0.2, zeminler[0][1])
|
|
209
|
+
expect(rgbKontrast(k400, track)).toBeLessThan(3.0)
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('secondary-600 dark temada eşiğin ALTINDA kalır (~2.66)', () => {
|
|
213
|
+
const k600 = rgbTokenRgb(tokenOku('secondary-600', 'light')!)
|
|
214
|
+
const { secondary, zeminler } = temaRenkleri('dark')
|
|
215
|
+
const track = alfaBileske(secondary, 0.2, zeminler[0][1])
|
|
216
|
+
expect(rgbKontrast(k600, track)).toBeLessThan(3.0)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('secondary-foreground kontrastı GEÇERDİ ama primary klonu üretirdi — kilit', () => {
|
|
220
|
+
// Bu test "neden secondary-foreground değil?" kararının dayanağını sabitler:
|
|
221
|
+
// light'ta --secondary-foreground ile --primary aynı hue/sat'ta ΔL=%2
|
|
222
|
+
// (fill↔fill 1.07), dark'ta birebir aynı değer. Varyant kimliği kaybolurdu.
|
|
223
|
+
const lightOran = rgbKontrast(
|
|
224
|
+
hslTokenRgb(tokenOku('secondary-foreground', 'light')!),
|
|
225
|
+
hslTokenRgb(tokenOku('primary', 'light')!)
|
|
226
|
+
)
|
|
227
|
+
expect(lightOran).toBeLessThan(1.3) // ayırt edilemez — near-identical
|
|
228
|
+
expect(tokenOku('secondary-foreground', 'dark')).toBe(tokenOku('primary', 'dark'))
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
})
|
|
@@ -218,7 +218,10 @@ describe('Progress Component', () => {
|
|
|
218
218
|
render(<Progress indicatorVariant="secondary" />)
|
|
219
219
|
const progress = screen.getByRole('progressbar')
|
|
220
220
|
const indicator = progress.querySelector('div')
|
|
221
|
-
|
|
221
|
+
// #372: dolgu artık `bg-secondary` DEĞİL — o yüzey token'ı track'in
|
|
222
|
+
// (`bg-secondary/20`) aynısıydı ve bar iki temada da görünmüyordu.
|
|
223
|
+
// Gerekçe + kontrast hesabı: progress-secondary-contrast.test.ts
|
|
224
|
+
expect(indicator).toHaveClass('bg-[rgb(var(--secondary-500))]')
|
|
222
225
|
})
|
|
223
226
|
|
|
224
227
|
it('renders success indicator variant correctly', () => {
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #354 — kontrast ölçümü viewport'tan BAĞIMSIZ olmalı.
|
|
3
|
+
*
|
|
4
|
+
* Issue'nun kabul kriteri "sabit viewport"ta ölçüm istiyordu. Ama mevcut tarayıcı
|
|
5
|
+
* araçlarıyla viewport DEĞİŞTİRİLEMEDİ: `resize_window` "başarılı" dönüyor,
|
|
6
|
+
* `innerWidth` 2751'de kalıyor, `outerWidth` 0 raporlanıyor.
|
|
7
|
+
*
|
|
8
|
+
* Bu yüzden risk ÖLÇÜLMEK yerine ORTADAN KALDIRILDI: eğer hiçbir kırılma noktası
|
|
9
|
+
* renk değiştirmiyorsa, contrast sonucu viewport'tan bağımsızdır ve tek bir
|
|
10
|
+
* genişlikte ölçmek yeterlidir.
|
|
11
|
+
*
|
|
12
|
+
* Tarama sonucu (2026-08-04): iki pakette 351 dosyada 199 responsive varyant
|
|
13
|
+
* (free 17 + pro 182); renk ailesinden olanların tamamı boyut/hiza değerleri.
|
|
14
|
+
* **Sıfır renk varyantı.**
|
|
15
|
+
*
|
|
16
|
+
* Bu test o değişmezi kilitler: biri `md:text-primary` gibi bir sınıf eklerse
|
|
17
|
+
* kırmızı olur ve "artık viewport başına ölçmek gerekiyor" uyarısı verir.
|
|
18
|
+
*/
|
|
19
|
+
import * as fs from 'fs'
|
|
20
|
+
import * as path from 'path'
|
|
21
|
+
|
|
22
|
+
const KOKLER = [
|
|
23
|
+
path.resolve(__dirname, '../../..'), // packages/moonui/src
|
|
24
|
+
path.resolve(__dirname, '../../../../../moonui-pro/src'), // packages/moonui-pro/src
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
/** Renk ailesinden yardımcı adları — boyut/hiza/spacing DEĞİL. */
|
|
28
|
+
const RENK_ONEKLERI = ['text', 'bg', 'border', 'from', 'via', 'to', 'ring', 'divide',
|
|
29
|
+
'placeholder', 'decoration', 'caret', 'accent', 'outline', 'shadow']
|
|
30
|
+
|
|
31
|
+
/** `text-*` ailesinde renk OLMAYAN değerler (boyut, hiza, taşma). */
|
|
32
|
+
const RENK_OLMAYAN_TEXT = new Set([
|
|
33
|
+
'xs','sm','base','lg','xl','2xl','3xl','4xl','5xl','6xl','7xl','8xl','9xl',
|
|
34
|
+
'left','center','right','justify','start','end',
|
|
35
|
+
'wrap','nowrap','balance','pretty','clip','ellipsis',
|
|
36
|
+
])
|
|
37
|
+
|
|
38
|
+
function dosyalar(kok: string): string[] {
|
|
39
|
+
const cikti: string[] = []
|
|
40
|
+
if (!fs.existsSync(kok)) return cikti
|
|
41
|
+
;(function walk(dir: string) {
|
|
42
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
43
|
+
const p = path.join(dir, e.name)
|
|
44
|
+
if (e.isDirectory()) {
|
|
45
|
+
if (!/node_modules|__tests__|dist/.test(p)) walk(p)
|
|
46
|
+
} else if (/\.(tsx?|css)$/.test(e.name)) cikti.push(p)
|
|
47
|
+
}
|
|
48
|
+
})(kok)
|
|
49
|
+
return cikti
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const tumDosyalar = KOKLER.flatMap(dosyalar)
|
|
53
|
+
const RESPONSIVE = /\b(sm|md|lg|xl|2xl):([a-z][a-z0-9-]*)(?:-([a-z0-9[\]#/.%-]+))?/g
|
|
54
|
+
|
|
55
|
+
describe('#354 responsive renk değişmezi', () => {
|
|
56
|
+
it('tarayıcı GERÇEKTEN responsive varyant buluyor (sessiz-yeşil koruması)', () => {
|
|
57
|
+
// Bu koruma olmadan, regex/yol bozulduğunda test "0 renk varyantı" ile YEŞİL
|
|
58
|
+
// geçerdi ve viewport riski sessizce açık kalırdı.
|
|
59
|
+
//
|
|
60
|
+
// Eşik, gerçek sayıya (2026-08-04'te 199) YAPIŞIK olmamalı: ölü dosya silmek
|
|
61
|
+
// gibi meşru işler sayıyı birkaç birim düşürür. İlk sürüm `> 200` idi ve o
|
|
62
|
+
// günkü değer 202'ydi; #365'te ölü `dashboard/demo.tsx` (3 varyant) silinince
|
|
63
|
+
// kırmızıya döndü — regex bozulmadan. Amaç "tarayıcı ölü mü" sorusunu
|
|
64
|
+
// yanıtlamak olduğundan büyüklük mertebesi yeterli.
|
|
65
|
+
let toplam = 0
|
|
66
|
+
for (const f of tumDosyalar) {
|
|
67
|
+
toplam += [...fs.readFileSync(f, 'utf8').matchAll(RESPONSIVE)].length
|
|
68
|
+
}
|
|
69
|
+
expect(toplam).toBeGreaterThan(100)
|
|
70
|
+
expect(tumDosyalar.length).toBeGreaterThan(100)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('hiçbir kırılma noktası RENK değiştirmiyor', () => {
|
|
74
|
+
const ihlaller: string[] = []
|
|
75
|
+
for (const f of tumDosyalar) {
|
|
76
|
+
const src = fs.readFileSync(f, 'utf8')
|
|
77
|
+
for (const m of src.matchAll(RESPONSIVE)) {
|
|
78
|
+
const [tam, , yardimci, deger] = m
|
|
79
|
+
if (!RENK_ONEKLERI.includes(yardimci)) continue
|
|
80
|
+
if (!deger) continue
|
|
81
|
+
// `text-` ailesinde boyut/hiza değerleri renk değildir
|
|
82
|
+
if (yardimci === 'text' && RENK_OLMAYAN_TEXT.has(deger)) continue
|
|
83
|
+
// köşeli parantezli keyfi değerler: renk mi boyut mu? `#`/`rgb` varsa renk.
|
|
84
|
+
if (deger.startsWith('[') && !/#|rgb|hsl/.test(deger)) continue
|
|
85
|
+
ihlaller.push(`${path.basename(f)} → ${tam}`)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Boş olmalı: aksi halde contrast ölçümü artık viewport'a bağımlıdır ve
|
|
89
|
+
// #354'ün "tek genişlikte ölçmek yeterli" gerekçesi düşer.
|
|
90
|
+
expect(ihlaller).toEqual([])
|
|
91
|
+
})
|
|
92
|
+
})
|