@moontra/moonui 6.15.0 → 6.16.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/dist/hooks/index.global.js +1 -6
- package/dist/hooks/index.global.js.map +1 -1
- package/dist/index.global.js +9 -9
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +35 -30
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +35 -30
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/ui/__tests__/select-trigger-name.test.tsx +65 -0
- package/src/components/ui/select.tsx +22 -3
package/package.json
CHANGED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #340 — `SelectTrigger` erişilebilir ad taşır.
|
|
3
|
+
*
|
|
4
|
+
* BULGUNUN İNCELİĞİ: docs'ta hiçbir tetikleyici etiketsiz değildi — hepsinde
|
|
5
|
+
* `<SelectValue placeholder="Select a theme" />` vardı ve metin ekranda GÖRÜNÜYORDU.
|
|
6
|
+
* Yine de axe 10 `button-name` ihlali sayıyordu. Sebebi axe'ın açıklaması verdi:
|
|
7
|
+
*
|
|
8
|
+
* "Element does not have inner text that is visible to screen readers"
|
|
9
|
+
*
|
|
10
|
+
* `SelectPrimitive.Trigger` `role="combobox"` render eder ve ARIA'da bu rol
|
|
11
|
+
* **"name from author"**dur — erişilebilir ad İÇERİKTEN GELMEZ. Yani görünür metin
|
|
12
|
+
* gözle okunur ama ekran okuyucuya hiç ad taşımaz.
|
|
13
|
+
*
|
|
14
|
+
* Sonuç: `aria-label` verilmeyen HER MoonUI Select adsızdı. Bu bir docs örneği
|
|
15
|
+
* kusuru değil, component yüzeyiydi.
|
|
16
|
+
*/
|
|
17
|
+
import * as React from 'react'
|
|
18
|
+
import { render, screen } from '@testing-library/react'
|
|
19
|
+
import '@testing-library/jest-dom'
|
|
20
|
+
|
|
21
|
+
import { Select, SelectTrigger, SelectValue } from '../select'
|
|
22
|
+
|
|
23
|
+
const kur = (props: React.ComponentProps<typeof SelectTrigger> = {}) =>
|
|
24
|
+
render(
|
|
25
|
+
<Select>
|
|
26
|
+
<SelectTrigger {...props}>
|
|
27
|
+
<SelectValue placeholder="Select a theme" />
|
|
28
|
+
</SelectTrigger>
|
|
29
|
+
</Select>
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
describe('#340 SelectTrigger erişilebilir ad', () => {
|
|
33
|
+
it('ad verilmezse GÖRÜNÜR içerik ada bağlanır', () => {
|
|
34
|
+
kur()
|
|
35
|
+
// `role="combobox"` içerikten ad almadığı için component `aria-labelledby`
|
|
36
|
+
// ile açıkça bağlamak zorunda.
|
|
37
|
+
expect(screen.getByRole('combobox')).toHaveAccessibleName('Select a theme')
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('tüketicinin `aria-label`i KAZANIR', () => {
|
|
41
|
+
kur({ 'aria-label': 'Theme selector' })
|
|
42
|
+
expect(screen.getByRole('combobox')).toHaveAccessibleName('Theme selector')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('tüketicinin `aria-labelledby`si KORUNUR (ezilmez)', () => {
|
|
46
|
+
render(
|
|
47
|
+
<>
|
|
48
|
+
<span id="dis-etiket">Appearance</span>
|
|
49
|
+
<Select>
|
|
50
|
+
<SelectTrigger aria-labelledby="dis-etiket">
|
|
51
|
+
<SelectValue placeholder="Select a theme" />
|
|
52
|
+
</SelectTrigger>
|
|
53
|
+
</Select>
|
|
54
|
+
</>
|
|
55
|
+
)
|
|
56
|
+
expect(screen.getByRole('combobox')).toHaveAccessibleName('Appearance')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('rol gerçekten `combobox` — bu testin varsayımı ayakta', () => {
|
|
60
|
+
// Radix rolü değiştirirse (ör. `button`) ad-içerikten-gelir kuralı geçerli olur
|
|
61
|
+
// ve bu bekçinin gerekçesi düşer. O zaman bilinçli gözden geçirilmeli.
|
|
62
|
+
kur()
|
|
63
|
+
expect(screen.getByRole('combobox')).toBeInTheDocument()
|
|
64
|
+
})
|
|
65
|
+
})
|
|
@@ -100,7 +100,24 @@ export interface SelectTriggerProps extends React.ComponentPropsWithoutRef<typeo
|
|
|
100
100
|
const SelectTrigger = React.forwardRef<
|
|
101
101
|
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
|
102
102
|
SelectTriggerProps
|
|
103
|
-
>(({ className, children, variant, size, error, success, loading, leftIcon, rightIcon, ...props }, ref) =>
|
|
103
|
+
>(({ className, children, variant, size, error, success, loading, leftIcon, rightIcon, ...props }, ref) => {
|
|
104
|
+
/**
|
|
105
|
+
* #340: `SelectPrimitive.Trigger` `role="combobox"` render eder. ARIA'da bu rol
|
|
106
|
+
* **"name from author"**dur — yani erişilebilir ad İÇERİKTEN GELMEZ. Tetikleyicinin
|
|
107
|
+
* içindeki görünür metin ("Select a theme") gözle okunur ama ekran okuyucuya HİÇ
|
|
108
|
+
* ad taşımaz; axe bunu `button-name` (critical) sayar.
|
|
109
|
+
*
|
|
110
|
+
* Sonuç: `aria-label` verilmeyen HER MoonUI Select adsızdı — docs örneği değil,
|
|
111
|
+
* component yüzeyi. Bu yüzden tetikleyicinin görünür içeriğine bir id verilip
|
|
112
|
+
* `aria-labelledby` ile ona bağlanıyor: ad, kullanıcının GÖRDÜĞÜ metin olur.
|
|
113
|
+
*
|
|
114
|
+
* Tüketici kendi `aria-label`/`aria-labelledby`sini verdiyse ona dokunulmaz.
|
|
115
|
+
*/
|
|
116
|
+
const icerikId = React.useId()
|
|
117
|
+
const adVerilmis =
|
|
118
|
+
props["aria-label"] != null || props["aria-labelledby"] != null
|
|
119
|
+
|
|
120
|
+
return (
|
|
104
121
|
<SelectPrimitive.Trigger
|
|
105
122
|
ref={ref}
|
|
106
123
|
className={cn(
|
|
@@ -119,8 +136,9 @@ const SelectTrigger = React.forwardRef<
|
|
|
119
136
|
data-error={error ? true : undefined}
|
|
120
137
|
data-success={success ? true : undefined}
|
|
121
138
|
data-loading={loading ? true : undefined}
|
|
139
|
+
aria-labelledby={adVerilmis ? props["aria-labelledby"] : icerikId}
|
|
122
140
|
>
|
|
123
|
-
<div className="flex items-center flex-1 gap-2">
|
|
141
|
+
<div id={icerikId} className="flex items-center flex-1 gap-2">
|
|
124
142
|
{leftIcon && (
|
|
125
143
|
<span className="flex items-center justify-center text-muted-foreground">
|
|
126
144
|
{leftIcon}
|
|
@@ -143,7 +161,8 @@ const SelectTrigger = React.forwardRef<
|
|
|
143
161
|
</SelectPrimitive.Icon>
|
|
144
162
|
)}
|
|
145
163
|
</SelectPrimitive.Trigger>
|
|
146
|
-
)
|
|
164
|
+
)
|
|
165
|
+
})
|
|
147
166
|
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
|
148
167
|
|
|
149
168
|
/**
|