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,383 @@
1
+ import React from 'react'
2
+ import { List } from './list'
3
+
4
+ // Pruebas unitarias para el componente List mejorado
5
+ describe('List Component', () => {
6
+ // Mock de los componentes dependientes
7
+ const mockIcon = jest.fn()
8
+ const mockText = jest.fn()
9
+ const mockCircularProgress = jest.fn()
10
+ const mockTextField = jest.fn()
11
+
12
+ beforeEach(() => {
13
+ jest.clearAllMocks()
14
+
15
+ // Mock de componentes
16
+ jest.doMock('./icon', () => ({ Icon: mockIcon }))
17
+ jest.doMock('./text', () => ({ Text: mockText }))
18
+ jest.doMock('./progress', () => ({ CircularProgress: mockCircularProgress }))
19
+ jest.doMock('./textfield', () => ({ TextField: mockTextField }))
20
+
21
+ // Mock de console.warn
22
+ jest.spyOn(console, 'warn').mockImplementation(() => {})
23
+ })
24
+
25
+ afterEach(() => {
26
+ console.warn.mockRestore()
27
+ })
28
+
29
+ test('component exports correctly', () => {
30
+ expect(List).toBeDefined()
31
+ expect(typeof List).toBe('function')
32
+ })
33
+
34
+ test('component has correct PropTypes', () => {
35
+ expect(List.propTypes).toBeDefined()
36
+ expect(List.propTypes.items).toBeDefined()
37
+ expect(List.propTypes.selected).toBeDefined()
38
+ expect(List.propTypes.onSelect).toBeDefined()
39
+ expect(List.propTypes.groupBy).toBeDefined()
40
+ expect(List.propTypes.loading).toBeDefined()
41
+ expect(List.propTypes.searchable).toBeDefined()
42
+ expect(List.propTypes.sortable).toBeDefined()
43
+ expect(List.propTypes.multiSelect).toBeDefined()
44
+ expect(List.propTypes.dense).toBeDefined()
45
+ expect(List.propTypes.disabled).toBeDefined()
46
+ expect(List.propTypes.animated).toBeDefined()
47
+ })
48
+
49
+ test('component has correct defaultProps', () => {
50
+ expect(List.defaultProps).toBeDefined()
51
+ expect(List.defaultProps.items).toEqual([])
52
+ expect(List.defaultProps.loading).toBe(false)
53
+ expect(List.defaultProps.empty).toBe(false)
54
+ expect(List.defaultProps.searchable).toBe(false)
55
+ expect(List.defaultProps.sortable).toBe(false)
56
+ expect(List.defaultProps.multiSelect).toBe(false)
57
+ expect(List.defaultProps.dense).toBe(false)
58
+ expect(List.defaultProps.disabled).toBe(false)
59
+ expect(List.defaultProps.animated).toBe(true)
60
+ expect(List.defaultProps.role).toBe('list')
61
+ })
62
+
63
+ test('warns when items is not an array', () => {
64
+ const validateItems = (items) => {
65
+ if (!Array.isArray(items)) {
66
+ console.warn('List component: items prop must be an array')
67
+ }
68
+ }
69
+
70
+ validateItems('not an array')
71
+ expect(console.warn).toHaveBeenCalledWith('List component: items prop must be an array')
72
+
73
+ console.warn.mockClear()
74
+ validateItems([])
75
+ expect(console.warn).not.toHaveBeenCalled()
76
+ })
77
+
78
+ test('handles selection correctly', () => {
79
+ const mockOnSelect = jest.fn()
80
+
81
+ const handleSelect = (id, event, multiSelect, selected, onSelect, onMultiSelect) => {
82
+ if (multiSelect && event?.ctrlKey) {
83
+ const currentSelected = Array.isArray(selected) ? selected : [selected].filter(Boolean)
84
+ const newSelected = currentSelected.includes(id)
85
+ ? currentSelected.filter(s => s !== id)
86
+ : [...currentSelected, id]
87
+
88
+ if (onMultiSelect) {
89
+ onMultiSelect(newSelected)
90
+ } else if (onSelect) {
91
+ onSelect(newSelected)
92
+ }
93
+ } else {
94
+ if (onSelect) onSelect(id)
95
+ }
96
+ }
97
+
98
+ // Single select
99
+ handleSelect('item1', {}, false, null, mockOnSelect, null)
100
+ expect(mockOnSelect).toHaveBeenCalledWith('item1')
101
+
102
+ mockOnSelect.mockClear()
103
+
104
+ // Multi-select with Ctrl
105
+ const mockOnMultiSelect = jest.fn()
106
+ handleSelect('item1', { ctrlKey: true }, true, [], mockOnSelect, mockOnMultiSelect)
107
+ expect(mockOnMultiSelect).toHaveBeenCalledWith(['item1'])
108
+ })
109
+
110
+ test('filters items by search correctly', () => {
111
+ const items = [
112
+ { id: 1, line1: 'Apple', line2: 'Fruit' },
113
+ { id: 2, line1: 'Banana', line2: 'Yellow fruit' },
114
+ { id: 3, line1: 'Carrot', line2: 'Vegetable' }
115
+ ]
116
+
117
+ const filterItems = (items, searchTerm, searchBy) => {
118
+ if (!searchTerm.trim()) return items
119
+
120
+ return items.filter(item => {
121
+ const searchText = searchBy
122
+ .map(field => item[field] || '')
123
+ .join(' ')
124
+ .toLowerCase()
125
+ return searchText.includes(searchTerm.toLowerCase())
126
+ })
127
+ }
128
+
129
+ expect(filterItems(items, '', ['line1', 'line2'])).toEqual(items)
130
+ expect(filterItems(items, 'apple', ['line1', 'line2'])).toEqual([items[0]])
131
+ expect(filterItems(items, 'fruit', ['line1', 'line2'])).toEqual([items[0], items[1]])
132
+ expect(filterItems(items, 'yellow', ['line1', 'line2'])).toEqual([items[1]])
133
+ expect(filterItems(items, 'xyz', ['line1', 'line2'])).toEqual([])
134
+ })
135
+
136
+ test('sorts items correctly', () => {
137
+ const items = [
138
+ { id: 1, name: 'Charlie', age: 30 },
139
+ { id: 2, name: 'Alice', age: 25 },
140
+ { id: 3, name: 'Bob', age: 35 }
141
+ ]
142
+
143
+ const sortItems = (items, sortConfig) => {
144
+ if (!sortConfig.key) return items
145
+
146
+ return [...items].sort((a, b) => {
147
+ const aValue = a[sortConfig.key] || ''
148
+ const bValue = b[sortConfig.key] || ''
149
+
150
+ if (sortConfig.direction === 'asc') {
151
+ return aValue.toString().localeCompare(bValue.toString())
152
+ } else {
153
+ return bValue.toString().localeCompare(aValue.toString())
154
+ }
155
+ })
156
+ }
157
+
158
+ // Sort by name ascending
159
+ const sortedAsc = sortItems(items, { key: 'name', direction: 'asc' })
160
+ expect(sortedAsc[0].name).toBe('Alice')
161
+ expect(sortedAsc[1].name).toBe('Bob')
162
+ expect(sortedAsc[2].name).toBe('Charlie')
163
+
164
+ // Sort by name descending
165
+ const sortedDesc = sortItems(items, { key: 'name', direction: 'desc' })
166
+ expect(sortedDesc[0].name).toBe('Charlie')
167
+ expect(sortedDesc[1].name).toBe('Bob')
168
+ expect(sortedDesc[2].name).toBe('Alice')
169
+ })
170
+
171
+ test('handles sort configuration correctly', () => {
172
+ const handleSort = (key, currentConfig) => {
173
+ const direction = currentConfig.key === key && currentConfig.direction === 'asc' ? 'desc' : 'asc'
174
+ return { key, direction }
175
+ }
176
+
177
+ // First click on a column
178
+ expect(handleSort('name', { key: null, direction: 'asc' }))
179
+ .toEqual({ key: 'name', direction: 'asc' })
180
+
181
+ // Second click on same column
182
+ expect(handleSort('name', { key: 'name', direction: 'asc' }))
183
+ .toEqual({ key: 'name', direction: 'desc' })
184
+
185
+ // Third click on same column
186
+ expect(handleSort('name', { key: 'name', direction: 'desc' }))
187
+ .toEqual({ key: 'name', direction: 'asc' })
188
+
189
+ // Click on different column
190
+ expect(handleSort('age', { key: 'name', direction: 'desc' }))
191
+ .toEqual({ key: 'age', direction: 'asc' })
192
+ })
193
+
194
+ test('generates CSS classes correctly', () => {
195
+ const generateClasses = (dense, disabled, animated, loading, className) => {
196
+ return [
197
+ 'list',
198
+ dense && 'list--dense',
199
+ disabled && 'list--disabled',
200
+ animated && 'list--animated',
201
+ loading && 'list--loading',
202
+ className
203
+ ].filter(Boolean).join(' ')
204
+ }
205
+
206
+ expect(generateClasses(false, false, false, false, ''))
207
+ .toBe('list')
208
+
209
+ expect(generateClasses(true, false, true, false, 'custom'))
210
+ .toBe('list list--dense list--animated custom')
211
+
212
+ expect(generateClasses(false, true, false, true, ''))
213
+ .toBe('list list--disabled list--loading')
214
+ })
215
+
216
+ test('generates accessibility attributes correctly', () => {
217
+ const generateAriaAttributes = (ariaLabel, disabled, loading, role) => {
218
+ return {
219
+ 'aria-label': ariaLabel || 'List',
220
+ 'aria-disabled': disabled,
221
+ 'aria-busy': loading,
222
+ role: role
223
+ }
224
+ }
225
+
226
+ const result1 = generateAriaAttributes(null, false, false, 'list')
227
+ expect(result1['aria-label']).toBe('List')
228
+ expect(result1['aria-disabled']).toBe(false)
229
+ expect(result1['aria-busy']).toBe(false)
230
+ expect(result1.role).toBe('list')
231
+
232
+ const result2 = generateAriaAttributes('Custom List', true, true, 'listbox')
233
+ expect(result2['aria-label']).toBe('Custom List')
234
+ expect(result2['aria-disabled']).toBe(true)
235
+ expect(result2['aria-busy']).toBe(true)
236
+ expect(result2.role).toBe('listbox')
237
+ })
238
+
239
+ test('groups items correctly', () => {
240
+ const items = [
241
+ { id: 1, name: 'Apple', category: 'Fruit' },
242
+ { id: 2, name: 'Banana', category: 'Fruit' },
243
+ { id: 3, name: 'Carrot', category: 'Vegetable' },
244
+ { id: 4, name: 'Broccoli', category: 'Vegetable' }
245
+ ]
246
+
247
+ const groupItems = (items, groupBy) => {
248
+ return items.reduce((groups, item) => {
249
+ let group = groups.find(g => g.name === item[groupBy])
250
+ if (!group) {
251
+ group = { name: item[groupBy], items: [] }
252
+ groups.push(group)
253
+ }
254
+ group.items.push(item)
255
+ return groups
256
+ }, [])
257
+ }
258
+
259
+ const grouped = groupItems(items, 'category')
260
+ expect(grouped).toHaveLength(2)
261
+ expect(grouped[0].name).toBe('Fruit')
262
+ expect(grouped[0].items).toHaveLength(2)
263
+ expect(grouped[1].name).toBe('Vegetable')
264
+ expect(grouped[1].items).toHaveLength(2)
265
+ })
266
+
267
+ test('handles group collapse/expand correctly', () => {
268
+ const handleGroupToggle = (groupName, collapsedGroups) => {
269
+ const next = new Set(collapsedGroups)
270
+ if (next.has(groupName)) {
271
+ next.delete(groupName)
272
+ } else {
273
+ next.add(groupName)
274
+ }
275
+ return next
276
+ }
277
+
278
+ const initial = new Set()
279
+ const afterCollapse = handleGroupToggle('group1', initial)
280
+ expect(afterCollapse.has('group1')).toBe(true)
281
+
282
+ const afterExpand = handleGroupToggle('group1', afterCollapse)
283
+ expect(afterExpand.has('group1')).toBe(false)
284
+ })
285
+ })
286
+
287
+ // ListItem tests
288
+ describe('ListItem Component', () => {
289
+ test('determines selection state correctly', () => {
290
+ const isSelected = (id, selected) => {
291
+ return Array.isArray(selected) ? selected.includes(id) : selected === id
292
+ }
293
+
294
+ expect(isSelected('item1', 'item1')).toBe(true)
295
+ expect(isSelected('item1', 'item2')).toBe(false)
296
+ expect(isSelected('item1', ['item1', 'item2'])).toBe(true)
297
+ expect(isSelected('item1', ['item2', 'item3'])).toBe(false)
298
+ expect(isSelected('item1', [])).toBe(false)
299
+ })
300
+
301
+ test('handles keyboard navigation correctly', () => {
302
+ const mockOnSelect = jest.fn()
303
+
304
+ const handleKeyDown = (event, disabled, onSelect, id) => {
305
+ if (disabled) return false
306
+
307
+ switch (event.key) {
308
+ case 'Enter':
309
+ case ' ':
310
+ if (onSelect) onSelect(id, event)
311
+ return true
312
+ default:
313
+ return false
314
+ }
315
+ }
316
+
317
+ // Test Enter key
318
+ expect(handleKeyDown({ key: 'Enter' }, false, mockOnSelect, 'item1')).toBe(true)
319
+ expect(mockOnSelect).toHaveBeenCalledWith('item1', { key: 'Enter' })
320
+
321
+ mockOnSelect.mockClear()
322
+
323
+ // Test Space key
324
+ expect(handleKeyDown({ key: ' ' }, false, mockOnSelect, 'item1')).toBe(true)
325
+ expect(mockOnSelect).toHaveBeenCalledWith('item1', { key: ' ' })
326
+
327
+ mockOnSelect.mockClear()
328
+
329
+ // Test disabled state
330
+ expect(handleKeyDown({ key: 'Enter' }, true, mockOnSelect, 'item1')).toBe(false)
331
+ expect(mockOnSelect).not.toHaveBeenCalled()
332
+
333
+ // Test other keys
334
+ expect(handleKeyDown({ key: 'Tab' }, false, mockOnSelect, 'item1')).toBe(false)
335
+ })
336
+
337
+ test('generates item CSS classes correctly', () => {
338
+ const generateItemClasses = (isSelected, isDisabled, dense, animated, multiSelect) => {
339
+ return [
340
+ 'list__item',
341
+ isSelected && 'list__item--selected',
342
+ isDisabled && 'list__item--disabled',
343
+ dense && 'list__item--dense',
344
+ animated && 'list__item--animated',
345
+ multiSelect && 'list__item--multi-select'
346
+ ].filter(Boolean).join(' ')
347
+ }
348
+
349
+ expect(generateItemClasses(false, false, false, false, false))
350
+ .toBe('list__item')
351
+
352
+ expect(generateItemClasses(true, false, true, true, true))
353
+ .toBe('list__item list__item--selected list__item--dense list__item--animated list__item--multi-select')
354
+
355
+ expect(generateItemClasses(false, true, false, false, false))
356
+ .toBe('list__item list__item--disabled')
357
+ })
358
+
359
+ test('generates item accessibility attributes correctly', () => {
360
+ const generateItemAriaAttributes = (isSelected, isDisabled, line1) => {
361
+ return {
362
+ 'aria-selected': isSelected,
363
+ 'aria-disabled': isDisabled,
364
+ 'aria-label': typeof line1 === 'string' ? line1 : 'List item',
365
+ role: 'option',
366
+ tabIndex: isDisabled ? -1 : 0
367
+ }
368
+ }
369
+
370
+ const result1 = generateItemAriaAttributes(true, false, 'Test Item')
371
+ expect(result1['aria-selected']).toBe(true)
372
+ expect(result1['aria-disabled']).toBe(false)
373
+ expect(result1['aria-label']).toBe('Test Item')
374
+ expect(result1.role).toBe('option')
375
+ expect(result1.tabIndex).toBe(0)
376
+
377
+ const result2 = generateItemAriaAttributes(false, true, { type: 'span', children: 'Complex' })
378
+ expect(result2['aria-selected']).toBe(false)
379
+ expect(result2['aria-disabled']).toBe(true)
380
+ expect(result2['aria-label']).toBe('List item')
381
+ expect(result2.tabIndex).toBe(-1)
382
+ })
383
+ })