intelliwaketssveltekitv25 1.0.95 → 1.0.98

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.
@@ -0,0 +1,484 @@
1
+ # ListGroupItems Testing Guide
2
+
3
+ A guide for testing ListGroupItems components in downstream applications using Playwright.
4
+
5
+ ## Quick Start
6
+
7
+ ListGroupItems automatically adds data attributes to help you locate items in tests:
8
+
9
+ ```typescript
10
+ // Select a list item by its key
11
+ await page.locator('[data-listgroupitem-key="myValue:MyTitle:st:bv:rt:sect"]').click()
12
+
13
+ // Select a link within a list item
14
+ await page.locator('[data-listgroupitem-key-a="myValue:MyTitle:st:bv:rt:sect"]').click()
15
+
16
+ // Access a badge within an item
17
+ await page.locator('[data-listgroupitem-key="myValue:MyTitle:st:bv:rt:sect"] .listGroupBadgeValue')
18
+ ```
19
+
20
+ **Pro Tip:** Use the exported `ListGroupItemKeyCalc` function to generate keys that match the component:
21
+
22
+ ```typescript
23
+ import { ListGroupItemKeyCalc } from 'intelliwaketssveltekitv25'
24
+
25
+ const item = { value: 'myValue', title: 'MyTitle' }
26
+ const key = ListGroupItemKeyCalc(item)
27
+ await page.locator(`[data-listgroupitem-key="${key}"]`).click()
28
+ ```
29
+
30
+ ## Core Concepts
31
+
32
+ ### Data Attributes
33
+
34
+ ListGroupItems provides two data attributes for targeting elements:
35
+
36
+ | Attribute | Location | Purpose |
37
+ |-----------|----------|---------|
38
+ | `data-listgroupitem-key` | `<li>` element (class `listGroupItem`) | Identifies the entire list item container |
39
+ | `data-listgroupitem-key-a` | `<a>` element (when item has href) | Identifies the clickable link within the item |
40
+
41
+ ### Badge Selection
42
+
43
+ Badges within list items use the `listGroupBadgeValue` class and can be located relative to their parent item:
44
+
45
+ ```typescript
46
+ // Find badge value
47
+ const badge = page.locator('[data-listgroupitem-key="user:123:..."] .listGroupBadgeValue')
48
+ await expect(badge).toHaveText('5')
49
+ ```
50
+
51
+ ---
52
+
53
+ ## Common Testing Patterns
54
+
55
+ ### Clicking List Items
56
+
57
+ ```typescript
58
+ // Click an item with a link (href)
59
+ await page.locator('[data-listgroupitem-key-a="settings:Settings:st:bv:rt:sect"]').click()
60
+
61
+ // Click an item with a custom click handler (no href)
62
+ await page.locator('[data-listgroupitem-key="action:ActionItem:st:bv:rt:sect"]').click()
63
+ ```
64
+
65
+ ### Verifying Item State
66
+
67
+ ```typescript
68
+ // Check if item is selected
69
+ const item = page.locator('[data-listgroupitem-key="dashboard:Dashboard:st:bv:rt:sect"]')
70
+ await expect(item).toHaveClass(/selected/)
71
+
72
+ // Check if item is disabled
73
+ await expect(item).toHaveClass(/disabled/)
74
+
75
+ // Verify badge value
76
+ const badge = item.locator('.listGroupBadgeValue')
77
+ await expect(badge).toHaveText('12')
78
+ ```
79
+
80
+ ### Testing Hierarchical Items
81
+
82
+ ```typescript
83
+ // Parent item
84
+ const parent = page.locator('[data-listgroupitem-key="folder:Folder:st:bv:rt:sect"]')
85
+
86
+ // Expand/collapse toggle (when subsExist is true)
87
+ await parent.locator('div[role="button"]').first().click()
88
+
89
+ // Child items appear in nested ListGroupItems components
90
+ const child = page.locator('[data-listgroupitem-key="subfolder:SubFolder:st:bv:rt:sect"]')
91
+ await expect(child).toBeVisible()
92
+ ```
93
+
94
+ ### Testing Sections
95
+
96
+ ```typescript
97
+ // Section headers have class 'listGroupHeader'
98
+ const section = page.locator('.listGroupHeader').filter({ hasText: 'My Section' })
99
+ await section.click() // Collapses/expands the section
100
+
101
+ // Items within collapsed sections are hidden
102
+ const item = page.locator('[data-listgroupitem-key="item:SectionItem:st:bv:rt:sect"]')
103
+ await expect(item).toBeHidden()
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Understanding Key Generation
109
+
110
+ ### Automatic Key Format
111
+
112
+ When a list item doesn't have a custom `key` property, ListGroupItems generates one from available properties:
113
+
114
+ ```
115
+ {value}:{title}:{sub_title}:{badgeValue}:{rightText}:{section}
116
+ ```
117
+
118
+ **Default values when properties are missing:**
119
+ - `value` → `'v'`
120
+ - `title` → `paneName` → `'t'`
121
+ - `sub_title` → `'st'`
122
+ - `badgeValue` → `'bv'`
123
+ - `rightText` → `'rt'`
124
+ - `section` → `'sect'`
125
+
126
+ **Example:**
127
+ ```typescript
128
+ {
129
+ value: 'user123',
130
+ title: 'John Doe',
131
+ sub_title: 'Admin',
132
+ badgeValue: '5',
133
+ rightText: 'Active',
134
+ section: 'Users'
135
+ }
136
+ // Generates: "user123:John Doe:Admin:5:Active:Users"
137
+ ```
138
+
139
+ ### Custom Keys
140
+
141
+ For more predictable testing, provide a custom `key` property:
142
+
143
+ ```typescript
144
+ const items: TListGroupItem[] = [
145
+ {
146
+ key: 'user-john-doe', // Custom key for testing
147
+ title: 'John Doe',
148
+ value: 'user123',
149
+ // ... other properties
150
+ }
151
+ ]
152
+ ```
153
+
154
+ Then in your test:
155
+ ```typescript
156
+ await page.locator('[data-listgroupitem-key="user-john-doe"]').click()
157
+ ```
158
+
159
+ ### Using ListGroupItemKeyCalc in Tests
160
+
161
+ The library exports the `ListGroupItemKeyCalc` function so your tests can generate the exact same keys as the component. This ensures consistency and prevents key mismatches:
162
+
163
+ ```typescript
164
+ import { test, expect } from '@playwright/test'
165
+ import { ListGroupItemKeyCalc } from 'intelliwaketssveltekitv25'
166
+ import type { TListGroupItem } from 'intelliwaketssveltekitv25'
167
+
168
+ test('navigate to user settings', async ({ page }) => {
169
+ // Define your item (this could come from your app's data)
170
+ const settingsItem: TListGroupItem = {
171
+ value: 'settings',
172
+ title: 'User Settings',
173
+ section: 'Configuration'
174
+ }
175
+
176
+ // Generate the key using the same function the component uses
177
+ const key = ListGroupItemKeyCalc(settingsItem)
178
+ // key = "settings:User Settings:st:bv:rt:Configuration"
179
+
180
+ // Use the key to locate and click the item
181
+ await page.locator(`[data-listgroupitem-key="${key}"]`).click()
182
+
183
+ // Verify navigation
184
+ await expect(page).toHaveURL(/.*settings/)
185
+ })
186
+ ```
187
+
188
+ **Benefits:**
189
+ - **No manual key construction** - Avoid errors from typos or format mismatches
190
+ - **Type safety** - TypeScript ensures your item has valid properties
191
+ - **Consistency** - Same function used by component and tests
192
+ - **Easy refactoring** - If key format changes, only update one function
193
+
194
+ **Advanced Pattern - Test Fixtures:**
195
+
196
+ Create reusable test fixtures with pre-calculated keys:
197
+
198
+ ```typescript
199
+ // test-fixtures.ts
200
+ import { ListGroupItemKeyCalc, type TListGroupItem } from 'intelliwaketssveltekitv25'
201
+
202
+ export const testItems = {
203
+ dashboard: {
204
+ value: 'dashboard',
205
+ title: 'Dashboard',
206
+ section: 'Main'
207
+ },
208
+ settings: {
209
+ value: 'settings',
210
+ title: 'Settings',
211
+ section: 'Configuration'
212
+ }
213
+ } as const satisfies Record<string, TListGroupItem>
214
+
215
+ // Pre-calculate keys
216
+ export const testKeys = Object.fromEntries(
217
+ Object.entries(testItems).map(([name, item]) => [name, ListGroupItemKeyCalc(item)])
218
+ ) as Record<keyof typeof testItems, string>
219
+
220
+ // Usage in tests:
221
+ // await page.locator(`[data-listgroupitem-key="${testKeys.dashboard}"]`).click()
222
+ ```
223
+
224
+ ---
225
+
226
+ ## Advanced Testing Scenarios
227
+
228
+ ### Working with Dynamic Content
229
+
230
+ When titles contain HTML or special characters:
231
+
232
+ ```typescript
233
+ // ListGroupItems converts title to string for key generation
234
+ // HTML tags and entities are preserved in the key
235
+ const item = {
236
+ title: 'User <strong>Admin</strong>', // HTML in title
237
+ value: 'admin'
238
+ }
239
+ // Generated key: "admin:User <strong>Admin</strong>:st:bv:rt:sect"
240
+
241
+ // For reliable testing, use custom keys or match on value only
242
+ await page.locator('[data-listgroupitem-key^="admin:"]').click()
243
+ ```
244
+
245
+ **Best Practices:**
246
+ 1. **Preferred:** Use custom `key` property for items with dynamic or HTML content
247
+ 2. **Recommended:** Use `ListGroupItemKeyCalc` function to generate keys programmatically
248
+ 3. **Fallback:** Use partial matching with `^=` or `*=` selectors
249
+
250
+ ### Testing Icons and Images
251
+
252
+ ```typescript
253
+ // Items with icons (via faProps or icon property)
254
+ const item = page.locator('[data-listgroupitem-key="settings:Settings:st:bv:rt:sect"]')
255
+
256
+ // Icon is inside the item
257
+ const icon = item.locator('svg') // FontAwesome icons render as SVG
258
+ await expect(icon).toBeVisible()
259
+
260
+ // Images
261
+ const img = item.locator('img')
262
+ await expect(img).toHaveAttribute('alt', 'Settings')
263
+ ```
264
+
265
+ ### Testing Right Text and Badges
266
+
267
+ ```typescript
268
+ const item = page.locator('[data-listgroupitem-key="task:MyTask:st:bv:rt:sect"]')
269
+
270
+ // Right text appears in a div with inline-block float-right
271
+ const rightText = item.locator('div.inline-block.float-right')
272
+ await expect(rightText).toHaveText('Due Soon')
273
+
274
+ // Badges have specific styling and data-color-dark attribute
275
+ const badge = item.locator('.listGroupBadgeValue')
276
+ await expect(badge).toHaveAttribute('data-color-dark', 'primary')
277
+ await expect(badge).toHaveText('3')
278
+ ```
279
+
280
+ ### Testing Ellipsis and Wrap Behavior
281
+
282
+ ```typescript
283
+ // When ellipses prop is true, title attribute contains full text
284
+ const item = page.locator('[data-listgroupitem-key="long:Very Long Title...:st:bv:rt:sect"]')
285
+ const titleDiv = item.locator('div.overflow-hidden').first()
286
+ await expect(titleDiv).toHaveAttribute('title', 'Very Long Title That Was Truncated')
287
+ ```
288
+
289
+ ---
290
+
291
+ ## Component Props Affecting Testing
292
+
293
+ ### Key Props
294
+
295
+ | Prop | Type | Impact on Testing |
296
+ |------|------|-------------------|
297
+ | `listItems` | `TListGroupItem[]` | Source of all items and their keys |
298
+ | `subsExist` | `boolean` | Adds collapse/expand controls to items with children |
299
+ | `useValueInHref` | `boolean` | Appends `:value` to href paths, affecting navigation |
300
+ | `active` | `TFindIsActive` | Filters which items are visible (`true` shows non-hidden, `false` shows hidden, `null` shows all) |
301
+ | `collapsedValues` | `string[]` | Controls which parent items are collapsed (children hidden) |
302
+ | `collapsedSections` | `string[]` | Controls which sections are collapsed (items hidden) |
303
+ | `parentSection` | `string \| null` | Filters section headers (section won't show if it matches parent) |
304
+
305
+ ### Testing with Different Props
306
+
307
+ ```typescript
308
+ // Test item visibility based on 'active' prop
309
+ // When active={true}, only items with hidden !== true are shown
310
+ const hiddenItem = page.locator('[data-listgroupitem-key="archived:Archived:st:bv:rt:sect"]')
311
+ await expect(hiddenItem).toBeHidden()
312
+
313
+ // When active={null}, all items are shown
314
+ await expect(hiddenItem).toBeVisible()
315
+ ```
316
+
317
+ ---
318
+
319
+ ## CSS Classes for Testing
320
+
321
+ ListGroupItems uses several CSS classes that can aid in test selection:
322
+
323
+ | Class | Element | Purpose |
324
+ |-------|---------|---------|
325
+ | `listGroup` | `<ul>` | Root container |
326
+ | `listGroupItem` | `<li>` | Individual list item |
327
+ | `listGroupHeader` | `<li>` | Section header (clickable to collapse) |
328
+ | `listGroupBadgeValue` | `<div>` | Badge value indicator |
329
+ | `listEmpty` | `<li>` | Shown when list is empty |
330
+ | `selected` | `<li>` | Item is currently selected/active |
331
+ | `disabled` | `<li>` | Item is disabled |
332
+ | `list_group_indicator` | `<li>` | Animated selection indicator |
333
+
334
+ ### Example: Testing Selection States
335
+
336
+ ```typescript
337
+ // Wait for selection indicator to appear
338
+ await page.locator('.list_group_indicator').waitFor({ state: 'visible' })
339
+
340
+ // Verify correct item is selected
341
+ const selectedItem = page.locator('.listGroupItem.selected')
342
+ await expect(selectedItem).toHaveAttribute('data-listgroupitem-key', 'expected:key:st:bv:rt:sect')
343
+ ```
344
+
345
+ ---
346
+
347
+ ## Troubleshooting
348
+
349
+ ### Key Not Found
350
+
351
+ **Problem:** `locator('[data-listgroupitem-key="..."]')` doesn't find the element.
352
+
353
+ **Solutions:**
354
+ 1. Check if item is filtered by `active` prop
355
+ 2. Check if item is in a collapsed section (`collapsedSections`)
356
+ 3. Check if parent item is collapsed (`collapsedValues`)
357
+ 4. Verify key format matches generated key (check console/inspector)
358
+ 5. Use partial match: `locator('[data-listgroupitem-key^="value:"]')`
359
+
360
+ ### Link Not Clickable
361
+
362
+ **Problem:** `data-listgroupitem-key-a` attribute not found.
363
+
364
+ **Explanation:** This attribute only exists when:
365
+ - Item has `pathFromItem` (generated from `href` or `title`)
366
+ - Item does NOT have `linkClick` handler
367
+
368
+ **Solution:** If item has `linkClick`, use `data-listgroupitem-key` on the `<li>` instead.
369
+
370
+ ### Item Hidden But Should Be Visible
371
+
372
+ **Problem:** Item exists in DOM but is not visible.
373
+
374
+ **Check:**
375
+ 1. Is parent item collapsed? Look for `collapsed` property in parent
376
+ 2. Is section collapsed? Check `collapsedSections` array
377
+ 3. Is item filtered by `active` prop? Check `hidden` property on item
378
+ 4. Is item in a nested ListGroupItems? Parent's `topValue` determines which items show
379
+
380
+ ---
381
+
382
+ ## Complete Example
383
+
384
+ ```typescript
385
+ import { test, expect } from '@playwright/test'
386
+ import { ListGroupItemKeyCalc, type TListGroupItem } from 'intelliwaketssveltekitv25'
387
+
388
+ test('navigate and interact with list group', async ({ page }) => {
389
+ await page.goto('/dashboard')
390
+
391
+ // Wait for list to load
392
+ await page.locator('.listGroup').waitFor()
393
+
394
+ // Click a section header to expand it
395
+ await page.locator('.listGroupHeader').filter({ hasText: 'Settings' }).click()
396
+
397
+ // Define the item we want to interact with
398
+ const profileItem: TListGroupItem = {
399
+ value: 'profile',
400
+ title: 'Profile Settings',
401
+ section: 'sect'
402
+ }
403
+ const profileKey = ListGroupItemKeyCalc(profileItem)
404
+
405
+ // Click a list item using generated key
406
+ await page.locator(`[data-listgroupitem-key-a="${profileKey}"]`).click()
407
+
408
+ // Verify navigation occurred
409
+ await expect(page).toHaveURL(/.*profile/)
410
+
411
+ // Verify item is selected
412
+ const selectedItem = page.locator(`[data-listgroupitem-key="${profileKey}"]`)
413
+ await expect(selectedItem).toHaveClass(/selected/)
414
+
415
+ // Check badge value
416
+ const badge = selectedItem.locator('.listGroupBadgeValue')
417
+ await expect(badge).toHaveText('3')
418
+
419
+ // Expand an item with children using generated key
420
+ const usersItem: TListGroupItem = {
421
+ value: 'users',
422
+ title: 'Users',
423
+ section: 'sect'
424
+ }
425
+ const usersKey = ListGroupItemKeyCalc(usersItem)
426
+ const parentItem = page.locator(`[data-listgroupitem-key="${usersKey}"]`)
427
+ await parentItem.locator('div[role="button"]').first().click()
428
+
429
+ // Child items now visible
430
+ const adminUsersItem: TListGroupItem = {
431
+ value: 'users-admin',
432
+ title: 'Admin Users',
433
+ section: 'sect'
434
+ }
435
+ const adminUsersKey = ListGroupItemKeyCalc(adminUsersItem)
436
+ const childItem = page.locator(`[data-listgroupitem-key="${adminUsersKey}"]`)
437
+ await expect(childItem).toBeVisible()
438
+ })
439
+ ```
440
+
441
+ ---
442
+
443
+ ## Reference: TListGroupItem Properties
444
+
445
+ Key properties that affect testing:
446
+
447
+ ```typescript
448
+ type TListGroupItem = {
449
+ key?: string // Custom key (recommended for testing)
450
+ value?: string // Used in auto-generated key and for hierarchy
451
+ title?: string | Snippet // Primary display text, used in key
452
+ sub_title?: string | Snippet // Secondary text, used in key
453
+ section?: string // Section header, used in key
454
+ badgeValue?: string | number // Badge content, used in key
455
+ rightText?: string // Right-aligned text, used in key
456
+
457
+ href?: string // Custom navigation path
458
+ linkClick?: () => void // Custom click handler (overrides href)
459
+
460
+ parent_value?: string // Parent item value (for hierarchy)
461
+ subs?: TListGroupItem[] // Child items
462
+
463
+ selected?: boolean // Item is selected
464
+ disabled?: boolean // Item is disabled
465
+ hidden?: boolean // Item is hidden (respects 'active' prop)
466
+ collapsed?: boolean // Item's children are collapsed
467
+
468
+ // Visual properties
469
+ faProps?: IFAProps // FontAwesome icon props
470
+ icon?: IconDefinition // FontAwesome icon
471
+ imageHref?: string // Image URL
472
+ bigIcon?: boolean // Larger icon size
473
+ badgeClass?: string // Additional badge CSS classes
474
+ badgeColor?: string // Badge color theme
475
+ itemClass?: string // Additional item CSS classes
476
+
477
+ // Other
478
+ paneName?: string // Fallback for title in key generation
479
+ hover_title?: string // Tooltip text
480
+ noLinkReplace?: boolean // Affects DisplayHTML link processing
481
+ strikeThrough?: boolean // Strike-through styling
482
+ dataSvelteKitPreloadData?: string // SvelteKit preload behavior
483
+ }
484
+ ```