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.
Files changed (122) hide show
  1. package/ACCORDION_EVALUATION.md +583 -0
  2. package/CHECKBOX_EVALUATION.md +273 -0
  3. package/CHIP_EVALUATION.md +542 -0
  4. package/COLOR_EVALUATION.md +524 -0
  5. package/COMPONENTS_EVALUATION.md +477 -0
  6. package/FORM_EVALUATION.md +459 -0
  7. package/HEADER_EVALUATION.md +436 -0
  8. package/ICON_EVALUATION.md +254 -0
  9. package/LIST_EVALUATION.md +574 -0
  10. package/PROGRESS_EVALUATION.md +450 -0
  11. package/RADIO_EVALUATION.md +439 -0
  12. package/RADIO_VISUAL_FIX.md +183 -0
  13. package/SECTION_IMPROVEMENTS.md +153 -0
  14. package/SWITCH_EVALUATION.md +335 -0
  15. package/SWITCH_VISUAL_FIX.md +232 -0
  16. package/TAB_EVALUATION.md +626 -0
  17. package/TEXTFIELD_EVALUATION.md +747 -0
  18. package/TOOLTIP_FIX.md +157 -0
  19. package/TREE_EVALUATION.md +708 -0
  20. package/dist/index.cjs +10893 -1969
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.css +7768 -1096
  23. package/dist/index.css.map +1 -1
  24. package/dist/index.modern.js +10921 -2005
  25. package/dist/index.modern.js.map +1 -1
  26. package/dist/index.umd.js +10893 -1969
  27. package/dist/index.umd.js.map +1 -1
  28. package/jest.config.js +24 -0
  29. package/package.json +10 -1
  30. package/src/html/accordion.css +208 -4
  31. package/src/html/accordion.example.js +390 -0
  32. package/src/html/accordion.js +284 -28
  33. package/src/html/accordion.unit.test.js +334 -0
  34. package/src/html/button.css +157 -16
  35. package/src/html/button.example.js +374 -0
  36. package/src/html/button.js +240 -60
  37. package/src/html/button.test.js +422 -0
  38. package/src/html/checkbox.css +74 -2
  39. package/src/html/checkbox.example.js +316 -0
  40. package/src/html/checkbox.js +113 -26
  41. package/src/html/checkbox.test.js +285 -0
  42. package/src/html/chip.css +230 -19
  43. package/src/html/chip.example.js +355 -0
  44. package/src/html/chip.js +321 -25
  45. package/src/html/chip.test.js +425 -0
  46. package/src/html/color.css +435 -6
  47. package/src/html/color.example.js +527 -0
  48. package/src/html/color.js +458 -9
  49. package/src/html/color.test.js +362 -4
  50. package/src/html/components.example.js +492 -0
  51. package/src/html/components_enhanced.test.js +581 -0
  52. package/src/html/form.css +70 -3
  53. package/src/html/form.example.js +385 -0
  54. package/src/html/form.js +232 -34
  55. package/src/html/form.test.js +369 -0
  56. package/src/html/header2.css +264 -0
  57. package/src/html/header2.example.js +411 -0
  58. package/src/html/header2.js +203 -0
  59. package/src/html/header2.test.js +377 -0
  60. package/src/html/icon.css +20 -2
  61. package/src/html/icon.example.js +268 -0
  62. package/src/html/icon.js +86 -16
  63. package/src/html/icon.test.js +231 -0
  64. package/src/html/index.js +4 -1
  65. package/src/html/list.css +393 -1
  66. package/src/html/list.example.js +404 -0
  67. package/src/html/list.js +583 -40
  68. package/src/html/list.test.js +383 -0
  69. package/src/html/progress.css +707 -17
  70. package/src/html/progress.example.js +424 -0
  71. package/src/html/progress.js +906 -9
  72. package/src/html/progress.test.js +313 -0
  73. package/src/html/property.css +399 -0
  74. package/src/html/property.example.js +553 -0
  75. package/src/html/property.js +393 -15
  76. package/src/html/property.test.js +351 -2
  77. package/src/html/radio-visual-test.js +289 -0
  78. package/src/html/radio.css +137 -11
  79. package/src/html/radio.example.js +389 -0
  80. package/src/html/radio.js +234 -10
  81. package/src/html/radio.test.js +318 -0
  82. package/src/html/section.example.js +99 -0
  83. package/src/html/section.js +40 -3
  84. package/src/html/section.test.js +131 -0
  85. package/src/html/selector.css +329 -3
  86. package/src/html/selector.js +369 -23
  87. package/src/html/switch-debug.js +197 -0
  88. package/src/html/switch-test-visual.js +294 -0
  89. package/src/html/switch.css +200 -0
  90. package/src/html/switch.example.js +461 -0
  91. package/src/html/switch.js +283 -23
  92. package/src/html/switch.test.js +355 -0
  93. package/src/html/tab.css +289 -0
  94. package/src/html/tab.example.js +446 -0
  95. package/src/html/tab.js +387 -22
  96. package/src/html/tab_enhanced.js +378 -0
  97. package/src/html/tab_enhanced.test.js +504 -0
  98. package/src/html/table2.css +576 -0
  99. package/src/html/table2.example.js +703 -0
  100. package/src/html/table2.js +1252 -0
  101. package/src/html/table2.migration.md +328 -0
  102. package/src/html/table2.test.js +582 -0
  103. package/src/html/text.css +375 -0
  104. package/src/html/text.js +311 -20
  105. package/src/html/textfield2.css +841 -0
  106. package/src/html/textfield2.example.js +1370 -0
  107. package/src/html/textfield2.js +1143 -0
  108. package/src/html/textfield2.test.js +950 -0
  109. package/src/html/thumbnail.css +289 -2
  110. package/src/html/thumbnail.js +214 -9
  111. package/src/html/tokenfield.css +449 -1
  112. package/src/html/tokenfield.example.js +503 -0
  113. package/src/html/tokenfield.js +561 -56
  114. package/src/html/tokenfield.test.js +423 -0
  115. package/src/html/tooltip-positioning-demo.js +187 -0
  116. package/src/html/tooltip.css +25 -2
  117. package/src/html/tree.css +240 -10
  118. package/src/html/tree.example.js +475 -0
  119. package/src/html/tree.js +714 -28
  120. package/src/html/tree_enhanced.test.js +495 -0
  121. package/table2.test.js +454 -0
  122. 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 = ({ id, icon, size = "normal", tooltip, clickable = false, disabled = false, action, eventPropagation = false, className }) => {
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
- return tooltip ? (
20
- <Tooltip {...tooltip}>
21
- <i id={id} className={`icon ${size} ${style} ${className} material-icons`} onClick={click} >
22
- {icon}
23
- </i>
24
- </Tooltip>
25
- ) : (
26
- <i id={id} className={`icon ${size} ${style} ${className} material-icons`} onClick={click}>
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
- const IconTest = (props) => {
33
- return (
34
- <div>
35
- <Icon icon="add" tooltip={{ text: 'add', top: '2rem', left: '2rem'}}/>
36
- </div>
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'