ywana-core8 0.1.75 → 0.1.77
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/ACCORDION_EVALUATION.md +583 -0
- package/CHECKBOX_EVALUATION.md +273 -0
- package/CHIP_EVALUATION.md +542 -0
- package/COLOR_EVALUATION.md +524 -0
- package/COMPONENTS_EVALUATION.md +477 -0
- package/FORM_EVALUATION.md +459 -0
- package/HEADER_EVALUATION.md +436 -0
- package/ICON_EVALUATION.md +254 -0
- package/LIST_EVALUATION.md +574 -0
- package/PROGRESS_EVALUATION.md +450 -0
- package/RADIO_EVALUATION.md +439 -0
- package/RADIO_VISUAL_FIX.md +183 -0
- package/SECTION_IMPROVEMENTS.md +153 -0
- package/SWITCH_EVALUATION.md +335 -0
- package/SWITCH_VISUAL_FIX.md +232 -0
- package/TAB_EVALUATION.md +626 -0
- package/TEXTFIELD_EVALUATION.md +747 -0
- package/TOOLTIP_FIX.md +157 -0
- package/TREE_EVALUATION.md +708 -0
- package/dist/index.cjs +10893 -1969
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +7768 -1096
- package/dist/index.css.map +1 -1
- package/dist/index.modern.js +10921 -2005
- package/dist/index.modern.js.map +1 -1
- package/dist/index.umd.js +10893 -1969
- package/dist/index.umd.js.map +1 -1
- package/jest.config.js +24 -0
- package/package.json +10 -1
- package/src/html/accordion.css +208 -4
- package/src/html/accordion.example.js +390 -0
- package/src/html/accordion.js +284 -28
- package/src/html/accordion.unit.test.js +334 -0
- package/src/html/button.css +157 -16
- package/src/html/button.example.js +374 -0
- package/src/html/button.js +240 -60
- package/src/html/button.test.js +422 -0
- package/src/html/checkbox.css +74 -2
- package/src/html/checkbox.example.js +316 -0
- package/src/html/checkbox.js +113 -26
- package/src/html/checkbox.test.js +285 -0
- package/src/html/chip.css +230 -19
- package/src/html/chip.example.js +355 -0
- package/src/html/chip.js +321 -25
- package/src/html/chip.test.js +425 -0
- package/src/html/color.css +435 -6
- package/src/html/color.example.js +527 -0
- package/src/html/color.js +458 -9
- package/src/html/color.test.js +362 -4
- package/src/html/components.example.js +492 -0
- package/src/html/components_enhanced.test.js +581 -0
- package/src/html/form.css +70 -3
- package/src/html/form.example.js +385 -0
- package/src/html/form.js +232 -34
- package/src/html/form.test.js +369 -0
- package/src/html/header2.css +264 -0
- package/src/html/header2.example.js +411 -0
- package/src/html/header2.js +203 -0
- package/src/html/header2.test.js +377 -0
- package/src/html/icon.css +20 -2
- package/src/html/icon.example.js +268 -0
- package/src/html/icon.js +86 -16
- package/src/html/icon.test.js +231 -0
- package/src/html/index.js +4 -1
- package/src/html/list.css +393 -1
- package/src/html/list.example.js +404 -0
- package/src/html/list.js +583 -40
- package/src/html/list.test.js +383 -0
- package/src/html/progress.css +707 -17
- package/src/html/progress.example.js +424 -0
- package/src/html/progress.js +906 -9
- package/src/html/progress.test.js +313 -0
- package/src/html/property.css +399 -0
- package/src/html/property.example.js +553 -0
- package/src/html/property.js +393 -15
- package/src/html/property.test.js +351 -2
- package/src/html/radio-visual-test.js +289 -0
- package/src/html/radio.css +137 -11
- package/src/html/radio.example.js +389 -0
- package/src/html/radio.js +234 -10
- package/src/html/radio.test.js +318 -0
- package/src/html/section.example.js +99 -0
- package/src/html/section.js +40 -3
- package/src/html/section.test.js +131 -0
- package/src/html/selector.css +329 -3
- package/src/html/selector.js +369 -23
- package/src/html/switch-debug.js +197 -0
- package/src/html/switch-test-visual.js +294 -0
- package/src/html/switch.css +200 -0
- package/src/html/switch.example.js +461 -0
- package/src/html/switch.js +283 -23
- package/src/html/switch.test.js +355 -0
- package/src/html/tab.css +289 -0
- package/src/html/tab.example.js +446 -0
- package/src/html/tab.js +387 -22
- package/src/html/tab_enhanced.js +378 -0
- package/src/html/tab_enhanced.test.js +504 -0
- package/src/html/table2.css +576 -0
- package/src/html/table2.example.js +703 -0
- package/src/html/table2.js +1252 -0
- package/src/html/table2.migration.md +328 -0
- package/src/html/table2.test.js +582 -0
- package/src/html/text.css +375 -0
- package/src/html/text.js +311 -20
- package/src/html/textfield2.css +841 -0
- package/src/html/textfield2.example.js +1370 -0
- package/src/html/textfield2.js +1143 -0
- package/src/html/textfield2.test.js +950 -0
- package/src/html/thumbnail.css +289 -2
- package/src/html/thumbnail.js +214 -9
- package/src/html/tokenfield.css +449 -1
- package/src/html/tokenfield.example.js +503 -0
- package/src/html/tokenfield.js +561 -56
- package/src/html/tokenfield.test.js +423 -0
- package/src/html/tooltip-positioning-demo.js +187 -0
- package/src/html/tooltip.css +25 -2
- package/src/html/tree.css +240 -10
- package/src/html/tree.example.js +475 -0
- package/src/html/tree.js +714 -28
- package/src/html/tree_enhanced.test.js +495 -0
- package/table2.test.js +454 -0
- package/src/html/button.tsx +0 -38
@@ -0,0 +1,268 @@
|
|
1
|
+
import React, { useState } from 'react'
|
2
|
+
import { Icon } from './icon'
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Ejemplos de uso del componente Icon mejorado
|
6
|
+
*/
|
7
|
+
export const IconExamples = () => {
|
8
|
+
const [clickCount, setClickCount] = useState(0)
|
9
|
+
const [isDisabled, setIsDisabled] = useState(false)
|
10
|
+
|
11
|
+
const handleClick = () => {
|
12
|
+
setClickCount(prev => prev + 1)
|
13
|
+
}
|
14
|
+
|
15
|
+
const toggleDisabled = () => {
|
16
|
+
setIsDisabled(prev => !prev)
|
17
|
+
}
|
18
|
+
|
19
|
+
return (
|
20
|
+
<div style={{ padding: '2rem', maxWidth: '800px' }}>
|
21
|
+
<h1>Ejemplos del Componente Icon Mejorado</h1>
|
22
|
+
|
23
|
+
<div style={{
|
24
|
+
background: '#f8f9fa',
|
25
|
+
padding: '1rem',
|
26
|
+
borderRadius: '8px',
|
27
|
+
marginBottom: '2rem',
|
28
|
+
border: '1px solid #e9ecef'
|
29
|
+
}}>
|
30
|
+
<h3>✅ Mejoras Implementadas:</h3>
|
31
|
+
<ul>
|
32
|
+
<li>🛡️ <strong>Manejo seguro de className</strong> - No falla con valores undefined</li>
|
33
|
+
<li>♿ <strong>Accesibilidad mejorada</strong> - Atributos ARIA y soporte de teclado</li>
|
34
|
+
<li>📝 <strong>PropTypes y documentación</strong> - Validación y documentación completa</li>
|
35
|
+
<li>🎯 <strong>Validación de props</strong> - Advertencias para props requeridos</li>
|
36
|
+
<li>🎨 <strong>CSS mejorado</strong> - Cursor consistente y estados de focus</li>
|
37
|
+
<li>⌨️ <strong>Soporte de teclado</strong> - Enter y Espacio para activar</li>
|
38
|
+
<li>🧪 <strong>Pruebas unitarias</strong> - 11 pruebas que cubren toda la funcionalidad</li>
|
39
|
+
</ul>
|
40
|
+
</div>
|
41
|
+
|
42
|
+
{/* Iconos básicos */}
|
43
|
+
<section style={{ marginBottom: '2rem' }}>
|
44
|
+
<h3>Iconos Básicos</h3>
|
45
|
+
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
|
46
|
+
<Icon icon="home" />
|
47
|
+
<Icon icon="settings" />
|
48
|
+
<Icon icon="favorite" />
|
49
|
+
<Icon icon="info" />
|
50
|
+
</div>
|
51
|
+
<p><em>Iconos simples sin interacción</em></p>
|
52
|
+
</section>
|
53
|
+
|
54
|
+
{/* Diferentes tamaños */}
|
55
|
+
<section style={{ marginBottom: '2rem' }}>
|
56
|
+
<h3>Diferentes Tamaños</h3>
|
57
|
+
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
|
58
|
+
<Icon icon="star" size="small" />
|
59
|
+
<Icon icon="star" size="normal" />
|
60
|
+
<Icon icon="star" size="large" />
|
61
|
+
</div>
|
62
|
+
<p><em>Tamaños: small, normal, large</em></p>
|
63
|
+
</section>
|
64
|
+
|
65
|
+
{/* Iconos clickables */}
|
66
|
+
<section style={{ marginBottom: '2rem' }}>
|
67
|
+
<h3>Iconos Clickables</h3>
|
68
|
+
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
|
69
|
+
<Icon
|
70
|
+
icon="thumb_up"
|
71
|
+
clickable
|
72
|
+
action={handleClick}
|
73
|
+
tooltip={{
|
74
|
+
text: 'Hacer clic para incrementar',
|
75
|
+
top: '-3rem',
|
76
|
+
left: '-2rem'
|
77
|
+
}}
|
78
|
+
/>
|
79
|
+
<Icon
|
80
|
+
icon="refresh"
|
81
|
+
clickable
|
82
|
+
action={() => setClickCount(0)}
|
83
|
+
tooltip={{
|
84
|
+
text: 'Resetear contador',
|
85
|
+
top: '-3rem',
|
86
|
+
left: '-2rem'
|
87
|
+
}}
|
88
|
+
/>
|
89
|
+
<span>Clics: {clickCount}</span>
|
90
|
+
</div>
|
91
|
+
<p><em>Prueba hacer clic en los iconos o usar Enter/Espacio cuando tengan focus</em></p>
|
92
|
+
</section>
|
93
|
+
|
94
|
+
{/* Estados disabled */}
|
95
|
+
<section style={{ marginBottom: '2rem' }}>
|
96
|
+
<h3>Estados Disabled</h3>
|
97
|
+
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
|
98
|
+
<Icon
|
99
|
+
icon="toggle_on"
|
100
|
+
clickable
|
101
|
+
action={toggleDisabled}
|
102
|
+
tooltip={{
|
103
|
+
text: 'Alternar estado disabled',
|
104
|
+
top: '-3rem',
|
105
|
+
left: '-3rem'
|
106
|
+
}}
|
107
|
+
/>
|
108
|
+
<Icon
|
109
|
+
icon="delete"
|
110
|
+
clickable
|
111
|
+
disabled={isDisabled}
|
112
|
+
action={() => alert('¡Eliminado!')}
|
113
|
+
tooltip={{
|
114
|
+
text: isDisabled ? 'Icono deshabilitado' : 'Eliminar elemento',
|
115
|
+
top: '-3rem',
|
116
|
+
left: '-2rem'
|
117
|
+
}}
|
118
|
+
/>
|
119
|
+
<span>Estado: {isDisabled ? 'Disabled' : 'Enabled'}</span>
|
120
|
+
</div>
|
121
|
+
<p><em>El primer icono alterna el estado del segundo</em></p>
|
122
|
+
</section>
|
123
|
+
|
124
|
+
{/* Iconos con tooltips */}
|
125
|
+
<section style={{ marginBottom: '2rem' }}>
|
126
|
+
<h3>Iconos con Tooltips</h3>
|
127
|
+
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
|
128
|
+
<Icon
|
129
|
+
icon="help"
|
130
|
+
clickable
|
131
|
+
tooltip={{
|
132
|
+
text: 'Ayuda y soporte',
|
133
|
+
top: '-3rem',
|
134
|
+
left: '-2rem'
|
135
|
+
}}
|
136
|
+
action={() => alert('¡Ayuda!')}
|
137
|
+
/>
|
138
|
+
<Icon
|
139
|
+
icon="download"
|
140
|
+
clickable
|
141
|
+
tooltip={{
|
142
|
+
text: 'Descargar archivo',
|
143
|
+
top: '-3rem',
|
144
|
+
left: '-2rem'
|
145
|
+
}}
|
146
|
+
action={() => alert('¡Descargando!')}
|
147
|
+
/>
|
148
|
+
<Icon
|
149
|
+
icon="share"
|
150
|
+
clickable
|
151
|
+
tooltip={{
|
152
|
+
text: 'Compartir contenido',
|
153
|
+
top: '-3rem',
|
154
|
+
left: '-2rem'
|
155
|
+
}}
|
156
|
+
action={() => alert('¡Compartiendo!')}
|
157
|
+
/>
|
158
|
+
</div>
|
159
|
+
<p><em>Tooltips posicionados para no interferir con los clics</em></p>
|
160
|
+
</section>
|
161
|
+
|
162
|
+
{/* Accesibilidad */}
|
163
|
+
<section style={{ marginBottom: '2rem' }}>
|
164
|
+
<h3>Accesibilidad Mejorada</h3>
|
165
|
+
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
|
166
|
+
<Icon
|
167
|
+
icon="accessibility"
|
168
|
+
clickable
|
169
|
+
ariaLabel="Configuración de accesibilidad"
|
170
|
+
action={() => alert('Configuración de accesibilidad')}
|
171
|
+
/>
|
172
|
+
<Icon
|
173
|
+
icon="visibility"
|
174
|
+
clickable
|
175
|
+
ariaLabel="Alternar visibilidad"
|
176
|
+
action={() => alert('Visibilidad alternada')}
|
177
|
+
/>
|
178
|
+
<Icon
|
179
|
+
icon="hearing"
|
180
|
+
clickable
|
181
|
+
ariaLabel="Configuración de audio"
|
182
|
+
action={() => alert('Configuración de audio')}
|
183
|
+
/>
|
184
|
+
</div>
|
185
|
+
<p><em>Iconos con etiquetas ARIA personalizadas y soporte completo de teclado</em></p>
|
186
|
+
</section>
|
187
|
+
|
188
|
+
{/* Clases personalizadas */}
|
189
|
+
<section style={{ marginBottom: '2rem' }}>
|
190
|
+
<h3>Clases CSS Personalizadas</h3>
|
191
|
+
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
|
192
|
+
<Icon
|
193
|
+
icon="palette"
|
194
|
+
className="custom-icon-style"
|
195
|
+
style={{ color: '#e91e63' }}
|
196
|
+
/>
|
197
|
+
<Icon
|
198
|
+
icon="brush"
|
199
|
+
className="another-custom-class"
|
200
|
+
style={{ color: '#2196f3' }}
|
201
|
+
/>
|
202
|
+
<Icon
|
203
|
+
icon="color_lens"
|
204
|
+
className={undefined} // Manejo seguro de undefined
|
205
|
+
style={{ color: '#4caf50' }}
|
206
|
+
/>
|
207
|
+
</div>
|
208
|
+
<p><em>Manejo seguro de className, incluso con valores undefined</em></p>
|
209
|
+
</section>
|
210
|
+
|
211
|
+
{/* Demostración de validación */}
|
212
|
+
<section style={{ marginBottom: '2rem' }}>
|
213
|
+
<h3>Validación de Props</h3>
|
214
|
+
<div style={{
|
215
|
+
background: '#fff3cd',
|
216
|
+
padding: '1rem',
|
217
|
+
borderRadius: '4px',
|
218
|
+
border: '1px solid #ffeaa7'
|
219
|
+
}}>
|
220
|
+
<p><strong>Prueba en la consola del navegador:</strong></p>
|
221
|
+
<p>El componente ahora valida que la prop <code>icon</code> sea requerida y muestra advertencias apropiadas.</p>
|
222
|
+
<p>También valida tipos de props en desarrollo para detectar errores temprano.</p>
|
223
|
+
</div>
|
224
|
+
</section>
|
225
|
+
|
226
|
+
{/* Comparación antes/después */}
|
227
|
+
<section>
|
228
|
+
<h3>Comparación: Antes vs Después</h3>
|
229
|
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
|
230
|
+
<div style={{
|
231
|
+
background: '#ffebee',
|
232
|
+
padding: '1rem',
|
233
|
+
borderRadius: '4px',
|
234
|
+
border: '1px solid #ffcdd2'
|
235
|
+
}}>
|
236
|
+
<h4>❌ Antes</h4>
|
237
|
+
<ul>
|
238
|
+
<li>className undefined causaba errores</li>
|
239
|
+
<li>Sin validación de props</li>
|
240
|
+
<li>Sin accesibilidad</li>
|
241
|
+
<li>Sin soporte de teclado</li>
|
242
|
+
<li>Cursor inconsistente</li>
|
243
|
+
<li>Sin documentación</li>
|
244
|
+
</ul>
|
245
|
+
</div>
|
246
|
+
<div style={{
|
247
|
+
background: '#e8f5e8',
|
248
|
+
padding: '1rem',
|
249
|
+
borderRadius: '4px',
|
250
|
+
border: '1px solid #c8e6c9'
|
251
|
+
}}>
|
252
|
+
<h4>✅ Después</h4>
|
253
|
+
<ul>
|
254
|
+
<li>Manejo seguro de todas las props</li>
|
255
|
+
<li>PropTypes completos</li>
|
256
|
+
<li>Atributos ARIA apropiados</li>
|
257
|
+
<li>Soporte completo de teclado</li>
|
258
|
+
<li>Estados visuales consistentes</li>
|
259
|
+
<li>Documentación y ejemplos</li>
|
260
|
+
</ul>
|
261
|
+
</div>
|
262
|
+
</div>
|
263
|
+
</section>
|
264
|
+
</div>
|
265
|
+
)
|
266
|
+
}
|
267
|
+
|
268
|
+
export default IconExamples
|
package/src/html/icon.js
CHANGED
@@ -1,11 +1,23 @@
|
|
1
1
|
import React from 'react'
|
2
|
+
import PropTypes from 'prop-types'
|
2
3
|
import './icon.css'
|
3
4
|
import { Tooltip } from './tooltip';
|
4
5
|
|
5
6
|
/**
|
6
|
-
* Icon
|
7
|
+
* Icon component for displaying Material Icons with various states and interactions
|
7
8
|
*/
|
8
|
-
export const Icon = ({
|
9
|
+
export const Icon = ({
|
10
|
+
id,
|
11
|
+
icon,
|
12
|
+
size = "normal",
|
13
|
+
tooltip,
|
14
|
+
clickable = false,
|
15
|
+
disabled = false,
|
16
|
+
action,
|
17
|
+
eventPropagation = false,
|
18
|
+
className,
|
19
|
+
ariaLabel
|
20
|
+
}) => {
|
9
21
|
|
10
22
|
function click (event) {
|
11
23
|
if (!eventPropagation) {
|
@@ -15,24 +27,82 @@ export const Icon = ({ id, icon, size = "normal", tooltip, clickable = false, di
|
|
15
27
|
if (action && !disabled) action(event)
|
16
28
|
}
|
17
29
|
|
30
|
+
// Validate icon prop
|
31
|
+
if (!icon) {
|
32
|
+
console.warn('Icon component: icon prop is required')
|
33
|
+
return null
|
34
|
+
}
|
35
|
+
|
18
36
|
const style = disabled ? "disabled" : clickable ? "clickable" : ""
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
37
|
+
const safeClassName = className || ''
|
38
|
+
|
39
|
+
// Accessibility attributes
|
40
|
+
const ariaAttributes = {
|
41
|
+
role: clickable ? 'button' : 'img',
|
42
|
+
'aria-label': ariaLabel || (clickable ? `${icon} button` : icon),
|
43
|
+
'aria-disabled': disabled,
|
44
|
+
tabIndex: clickable && !disabled ? 0 : -1
|
45
|
+
}
|
46
|
+
|
47
|
+
// Handle keyboard events for accessibility
|
48
|
+
const handleKeyDown = (event) => {
|
49
|
+
if (clickable && !disabled && (event.key === 'Enter' || event.key === ' ')) {
|
50
|
+
event.preventDefault()
|
51
|
+
click(event)
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
const iconElement = (
|
56
|
+
<i
|
57
|
+
id={id}
|
58
|
+
className={`icon ${size} ${style} ${safeClassName} material-icons`}
|
59
|
+
onClick={click}
|
60
|
+
onKeyDown={handleKeyDown}
|
61
|
+
{...ariaAttributes}
|
62
|
+
>
|
27
63
|
{icon}
|
28
64
|
</i>
|
29
65
|
)
|
66
|
+
|
67
|
+
return tooltip ? (
|
68
|
+
<Tooltip {...tooltip}>
|
69
|
+
{iconElement}
|
70
|
+
</Tooltip>
|
71
|
+
) : iconElement
|
30
72
|
}
|
31
73
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
74
|
+
// PropTypes para validación y documentación
|
75
|
+
Icon.propTypes = {
|
76
|
+
/** Unique identifier for the icon element */
|
77
|
+
id: PropTypes.string,
|
78
|
+
/** Material Icon name (required) */
|
79
|
+
icon: PropTypes.string.isRequired,
|
80
|
+
/** Size variant of the icon */
|
81
|
+
size: PropTypes.oneOf(['small', 'normal', 'large']),
|
82
|
+
/** Tooltip configuration object */
|
83
|
+
tooltip: PropTypes.shape({
|
84
|
+
text: PropTypes.string.isRequired,
|
85
|
+
top: PropTypes.string,
|
86
|
+
left: PropTypes.string
|
87
|
+
}),
|
88
|
+
/** Whether the icon is clickable */
|
89
|
+
clickable: PropTypes.bool,
|
90
|
+
/** Whether the icon is disabled */
|
91
|
+
disabled: PropTypes.bool,
|
92
|
+
/** Click handler function */
|
93
|
+
action: PropTypes.func,
|
94
|
+
/** Whether to allow event propagation */
|
95
|
+
eventPropagation: PropTypes.bool,
|
96
|
+
/** Additional CSS classes */
|
97
|
+
className: PropTypes.string,
|
98
|
+
/** Accessibility label for screen readers */
|
99
|
+
ariaLabel: PropTypes.string
|
100
|
+
}
|
101
|
+
|
102
|
+
Icon.defaultProps = {
|
103
|
+
size: 'normal',
|
104
|
+
clickable: false,
|
105
|
+
disabled: false,
|
106
|
+
eventPropagation: false,
|
107
|
+
className: ''
|
38
108
|
}
|
@@ -0,0 +1,231 @@
|
|
1
|
+
import React from 'react'
|
2
|
+
import { Icon } from './icon'
|
3
|
+
|
4
|
+
// Pruebas unitarias para el componente Icon
|
5
|
+
describe('Icon Component', () => {
|
6
|
+
// Mock del componente Tooltip
|
7
|
+
const mockTooltip = jest.fn()
|
8
|
+
|
9
|
+
beforeEach(() => {
|
10
|
+
jest.clearAllMocks()
|
11
|
+
|
12
|
+
// Mock del componente Tooltip
|
13
|
+
jest.doMock('./tooltip', () => ({
|
14
|
+
Tooltip: mockTooltip
|
15
|
+
}))
|
16
|
+
|
17
|
+
// Mock de console.warn para las pruebas
|
18
|
+
jest.spyOn(console, 'warn').mockImplementation(() => {})
|
19
|
+
})
|
20
|
+
|
21
|
+
afterEach(() => {
|
22
|
+
console.warn.mockRestore()
|
23
|
+
})
|
24
|
+
|
25
|
+
test('component exports correctly', () => {
|
26
|
+
expect(Icon).toBeDefined()
|
27
|
+
expect(typeof Icon).toBe('function')
|
28
|
+
})
|
29
|
+
|
30
|
+
test('component has correct PropTypes', () => {
|
31
|
+
expect(Icon.propTypes).toBeDefined()
|
32
|
+
expect(Icon.propTypes.id).toBeDefined()
|
33
|
+
expect(Icon.propTypes.icon).toBeDefined()
|
34
|
+
expect(Icon.propTypes.size).toBeDefined()
|
35
|
+
expect(Icon.propTypes.tooltip).toBeDefined()
|
36
|
+
expect(Icon.propTypes.clickable).toBeDefined()
|
37
|
+
expect(Icon.propTypes.disabled).toBeDefined()
|
38
|
+
expect(Icon.propTypes.action).toBeDefined()
|
39
|
+
expect(Icon.propTypes.eventPropagation).toBeDefined()
|
40
|
+
expect(Icon.propTypes.className).toBeDefined()
|
41
|
+
expect(Icon.propTypes.ariaLabel).toBeDefined()
|
42
|
+
})
|
43
|
+
|
44
|
+
test('component has correct defaultProps', () => {
|
45
|
+
expect(Icon.defaultProps).toBeDefined()
|
46
|
+
expect(Icon.defaultProps.size).toBe('normal')
|
47
|
+
expect(Icon.defaultProps.clickable).toBe(false)
|
48
|
+
expect(Icon.defaultProps.disabled).toBe(false)
|
49
|
+
expect(Icon.defaultProps.eventPropagation).toBe(false)
|
50
|
+
expect(Icon.defaultProps.className).toBe('')
|
51
|
+
})
|
52
|
+
|
53
|
+
test('returns null and warns when icon prop is missing', () => {
|
54
|
+
const result = Icon({})
|
55
|
+
|
56
|
+
expect(result).toBeNull()
|
57
|
+
expect(console.warn).toHaveBeenCalledWith('Icon component: icon prop is required')
|
58
|
+
})
|
59
|
+
|
60
|
+
test('handles className safely', () => {
|
61
|
+
// Simular el manejo seguro de className
|
62
|
+
const testClassName = (className) => {
|
63
|
+
const safeClassName = className || ''
|
64
|
+
return `icon normal ${safeClassName} material-icons`
|
65
|
+
}
|
66
|
+
|
67
|
+
expect(testClassName('custom-class')).toBe('icon normal custom-class material-icons')
|
68
|
+
expect(testClassName(undefined)).toBe('icon normal material-icons')
|
69
|
+
expect(testClassName('')).toBe('icon normal material-icons')
|
70
|
+
expect(testClassName(null)).toBe('icon normal material-icons')
|
71
|
+
})
|
72
|
+
|
73
|
+
test('generates correct CSS classes for different states', () => {
|
74
|
+
// Simular la lógica de generación de clases
|
75
|
+
const generateClasses = (size, disabled, clickable, className) => {
|
76
|
+
const style = disabled ? "disabled" : clickable ? "clickable" : ""
|
77
|
+
const safeClassName = className || ''
|
78
|
+
return `icon ${size} ${style} ${safeClassName} material-icons`
|
79
|
+
}
|
80
|
+
|
81
|
+
expect(generateClasses('normal', false, false, '')).toBe('icon normal material-icons')
|
82
|
+
expect(generateClasses('small', false, true, '')).toBe('icon small clickable material-icons')
|
83
|
+
expect(generateClasses('large', true, false, '')).toBe('icon large disabled material-icons')
|
84
|
+
expect(generateClasses('normal', false, true, 'custom')).toBe('icon normal clickable custom material-icons')
|
85
|
+
})
|
86
|
+
|
87
|
+
test('generates correct accessibility attributes', () => {
|
88
|
+
// Simular la lógica de atributos de accesibilidad
|
89
|
+
const generateAriaAttributes = (clickable, disabled, icon, ariaLabel) => {
|
90
|
+
return {
|
91
|
+
role: clickable ? 'button' : 'img',
|
92
|
+
'aria-label': ariaLabel || (clickable ? `${icon} button` : icon),
|
93
|
+
'aria-disabled': disabled,
|
94
|
+
tabIndex: clickable && !disabled ? 0 : -1
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
// Icono no clickable
|
99
|
+
const nonClickable = generateAriaAttributes(false, false, 'home', null)
|
100
|
+
expect(nonClickable.role).toBe('img')
|
101
|
+
expect(nonClickable['aria-label']).toBe('home')
|
102
|
+
expect(nonClickable.tabIndex).toBe(-1)
|
103
|
+
|
104
|
+
// Icono clickable
|
105
|
+
const clickable = generateAriaAttributes(true, false, 'settings', null)
|
106
|
+
expect(clickable.role).toBe('button')
|
107
|
+
expect(clickable['aria-label']).toBe('settings button')
|
108
|
+
expect(clickable.tabIndex).toBe(0)
|
109
|
+
|
110
|
+
// Icono clickable pero disabled
|
111
|
+
const disabled = generateAriaAttributes(true, true, 'delete', null)
|
112
|
+
expect(disabled.role).toBe('button')
|
113
|
+
expect(disabled['aria-disabled']).toBe(true)
|
114
|
+
expect(disabled.tabIndex).toBe(-1)
|
115
|
+
|
116
|
+
// Con aria-label personalizado
|
117
|
+
const customLabel = generateAriaAttributes(true, false, 'star', 'Favorito')
|
118
|
+
expect(customLabel['aria-label']).toBe('Favorito')
|
119
|
+
})
|
120
|
+
|
121
|
+
test('click handler works correctly', () => {
|
122
|
+
const mockAction = jest.fn()
|
123
|
+
|
124
|
+
// Simular el comportamiento del click handler
|
125
|
+
const simulateClick = (eventPropagation, disabled, action) => {
|
126
|
+
const event = {
|
127
|
+
stopPropagation: jest.fn(),
|
128
|
+
preventDefault: jest.fn()
|
129
|
+
}
|
130
|
+
|
131
|
+
// Lógica del click handler
|
132
|
+
if (!eventPropagation) {
|
133
|
+
event.stopPropagation()
|
134
|
+
event.preventDefault()
|
135
|
+
}
|
136
|
+
if (action && !disabled) action(event)
|
137
|
+
|
138
|
+
return event
|
139
|
+
}
|
140
|
+
|
141
|
+
// Click normal
|
142
|
+
const event1 = simulateClick(false, false, mockAction)
|
143
|
+
expect(event1.stopPropagation).toHaveBeenCalled()
|
144
|
+
expect(event1.preventDefault).toHaveBeenCalled()
|
145
|
+
expect(mockAction).toHaveBeenCalledWith(event1)
|
146
|
+
|
147
|
+
mockAction.mockClear()
|
148
|
+
|
149
|
+
// Click con disabled
|
150
|
+
const event2 = simulateClick(false, true, mockAction)
|
151
|
+
expect(mockAction).not.toHaveBeenCalled()
|
152
|
+
|
153
|
+
// Click con event propagation
|
154
|
+
const event3 = simulateClick(true, false, mockAction)
|
155
|
+
expect(event3.stopPropagation).not.toHaveBeenCalled()
|
156
|
+
expect(event3.preventDefault).not.toHaveBeenCalled()
|
157
|
+
expect(mockAction).toHaveBeenCalledWith(event3)
|
158
|
+
})
|
159
|
+
|
160
|
+
test('keyboard event handling works correctly', () => {
|
161
|
+
const mockAction = jest.fn()
|
162
|
+
|
163
|
+
// Simular el manejo de eventos de teclado
|
164
|
+
const simulateKeyDown = (key, clickable, disabled, action) => {
|
165
|
+
const event = {
|
166
|
+
key,
|
167
|
+
preventDefault: jest.fn()
|
168
|
+
}
|
169
|
+
|
170
|
+
// Lógica del keyboard handler
|
171
|
+
if (clickable && !disabled && (event.key === 'Enter' || event.key === ' ')) {
|
172
|
+
event.preventDefault()
|
173
|
+
// Simular llamada a click
|
174
|
+
if (action) action(event)
|
175
|
+
}
|
176
|
+
|
177
|
+
return event
|
178
|
+
}
|
179
|
+
|
180
|
+
// Enter key
|
181
|
+
const enterEvent = simulateKeyDown('Enter', true, false, mockAction)
|
182
|
+
expect(enterEvent.preventDefault).toHaveBeenCalled()
|
183
|
+
expect(mockAction).toHaveBeenCalledWith(enterEvent)
|
184
|
+
|
185
|
+
mockAction.mockClear()
|
186
|
+
|
187
|
+
// Space key
|
188
|
+
const spaceEvent = simulateKeyDown(' ', true, false, mockAction)
|
189
|
+
expect(spaceEvent.preventDefault).toHaveBeenCalled()
|
190
|
+
expect(mockAction).toHaveBeenCalledWith(spaceEvent)
|
191
|
+
|
192
|
+
mockAction.mockClear()
|
193
|
+
|
194
|
+
// Other key (should not trigger)
|
195
|
+
const otherEvent = simulateKeyDown('a', true, false, mockAction)
|
196
|
+
expect(otherEvent.preventDefault).not.toHaveBeenCalled()
|
197
|
+
expect(mockAction).not.toHaveBeenCalled()
|
198
|
+
|
199
|
+
// Disabled icon (should not trigger)
|
200
|
+
const disabledEvent = simulateKeyDown('Enter', true, true, mockAction)
|
201
|
+
expect(disabledEvent.preventDefault).not.toHaveBeenCalled()
|
202
|
+
expect(mockAction).not.toHaveBeenCalled()
|
203
|
+
})
|
204
|
+
|
205
|
+
test('size variants are handled correctly', () => {
|
206
|
+
const validSizes = ['small', 'normal', 'large']
|
207
|
+
|
208
|
+
validSizes.forEach(size => {
|
209
|
+
const classes = `icon ${size} material-icons`
|
210
|
+
expect(classes).toContain(size)
|
211
|
+
})
|
212
|
+
})
|
213
|
+
|
214
|
+
test('tooltip integration works correctly', () => {
|
215
|
+
// Verificar que el componente puede manejar tooltips
|
216
|
+
const tooltipConfig = {
|
217
|
+
text: 'Test tooltip',
|
218
|
+
top: '-2rem',
|
219
|
+
left: '-1rem'
|
220
|
+
}
|
221
|
+
|
222
|
+
// Simular la lógica de tooltip
|
223
|
+
const hasTooltip = !!tooltipConfig
|
224
|
+
expect(hasTooltip).toBe(true)
|
225
|
+
|
226
|
+
// Verificar que la configuración es válida
|
227
|
+
expect(tooltipConfig.text).toBe('Test tooltip')
|
228
|
+
expect(tooltipConfig.top).toBe('-2rem')
|
229
|
+
expect(tooltipConfig.left).toBe('-1rem')
|
230
|
+
})
|
231
|
+
})
|
package/src/html/index.js
CHANGED
@@ -4,17 +4,20 @@ export { CheckBox } from './checkbox'
|
|
4
4
|
export { Chips, Chip } from './chip'
|
5
5
|
export { Form } from './form'
|
6
6
|
export { Header } from './header'
|
7
|
+
export { Header2 } from './header2'
|
7
8
|
export { Icon } from './icon'
|
8
9
|
export { List } from './list'
|
9
10
|
export { MenuIcon, Menu, MenuItem, MenuSeparator } from './menu'
|
10
11
|
export { CircularProgress, LinearProgress } from './progress'
|
11
12
|
export { Property } from './property'
|
12
|
-
export { RadioButton } from './radio'
|
13
|
+
export { RadioButton, RadioGroup } from './radio'
|
13
14
|
export { Section } from './section'
|
14
15
|
export { Tabs, Tab, Stack } from './tab'
|
15
16
|
export { DataTable } from './table'
|
17
|
+
export { DataTable2 } from './table2'
|
16
18
|
export { Text, TEXTFORMATS } from './text'
|
17
19
|
export { TextField, DropDown, TextArea, DateRange, PasswordField } from './textfield'
|
20
|
+
export { TextField2, TextArea2, PasswordField2, DropDown2, DateRange2 } from './textfield2'
|
18
21
|
export { TokenField } from './tokenfield'
|
19
22
|
export { Tree, TreeNode, TreeItem } from './tree'
|
20
23
|
export { Switch, Switch2 } from './switch'
|