@juspay/svelte-ui-components 2.96.2 → 2.97.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/Animations/ModalAnimation.svelte +26 -5
  2. package/dist/Animations/ModalAnimation.svelte.d.ts +1 -0
  3. package/dist/Animations/OverlayAnimation.svelte +3 -2
  4. package/dist/Animations/OverlayAnimation.svelte.d.ts +1 -0
  5. package/dist/BarChart/BarChart.svelte +4 -0
  6. package/dist/BrandLoader/BrandLoader.svelte +2 -2
  7. package/dist/BrandLoader/properties.d.ts +1 -0
  8. package/dist/Checkbox/Checkbox.svelte +2 -0
  9. package/dist/DateRangePicker/DateRangePicker.svelte +9 -1
  10. package/dist/DeltaIndicator/properties.d.ts +1 -1
  11. package/dist/DualAxisBarChart/DualAxisBarChart.svelte +1 -0
  12. package/dist/FunnelChart/FunnelChart.svelte +1 -0
  13. package/dist/GridItem/GridItem.svelte +10 -2
  14. package/dist/GridItem/properties.d.ts +1 -0
  15. package/dist/Icon/Icon.svelte +9 -2
  16. package/dist/Icon/properties.d.ts +1 -0
  17. package/dist/IconStack/IconStack.svelte +2 -2
  18. package/dist/IconStack/properties.d.ts +1 -0
  19. package/dist/Input/Input.svelte +4 -1
  20. package/dist/InputButton/InputButton.svelte +3 -2
  21. package/dist/InputButton/properties.d.ts +1 -0
  22. package/dist/LineChart/LineChart.svelte +9 -1
  23. package/dist/LoadingDots/LoadingDots.svelte +6 -1
  24. package/dist/ProportionBar/ProportionBar.svelte +10 -2
  25. package/dist/Select/Select.svelte +13 -1
  26. package/dist/Sheet/Sheet.svelte +1 -0
  27. package/dist/StatCard/StatCard.svelte +45 -10
  28. package/dist/Status/Status.svelte +3 -2
  29. package/dist/Status/properties.d.ts +1 -0
  30. package/dist/Table/BuiltinCell.svelte +27 -8
  31. package/dist/Table/Table.svelte +15 -1
  32. package/dist/Table/properties.d.ts +1 -3
  33. package/dist/Toolbar/Toolbar.svelte +1 -1
  34. package/dist/Tooltip/Tooltip.svelte +5 -0
  35. package/dist/_chart/Axis.svelte +4 -1
  36. package/dist/_chart/ChartTooltip.svelte +3 -1
  37. package/dist/_chart/Legend.svelte +1 -0
  38. package/package.json +1 -1
@@ -9,9 +9,16 @@
9
9
  align?: ModalAlign;
10
10
  transitionType?: ModalTransition;
11
11
  children?: Snippet;
12
+ testId?: string;
12
13
  };
13
14
 
14
- let { enable = true, align = 'bottom', transitionType = 'ALL', children }: Props = $props();
15
+ let {
16
+ enable = true,
17
+ align = 'bottom',
18
+ transitionType = 'ALL',
19
+ children,
20
+ testId
21
+ }: Props = $props();
15
22
 
16
23
  let flyAnimationProperties = $derived.by(() => {
17
24
  const base = { x: 0, y: 0, duration: 380 };
@@ -34,19 +41,33 @@
34
41
 
35
42
  {#if enable}
36
43
  {#if useFlyAnimation && useOutTransition}
37
- <div in:fly|global={flyAnimationProperties} out:fly|global={flyAnimationProperties}>
44
+ <div
45
+ in:fly|global={flyAnimationProperties}
46
+ out:fly|global={flyAnimationProperties}
47
+ data-pw={typeof testId === 'string' ? testId : null}
48
+ >
38
49
  {@render children?.()}
39
50
  </div>
40
51
  {:else if useFlyAnimation}
41
- <div in:fly|global={flyAnimationProperties}>
52
+ <div
53
+ in:fly|global={flyAnimationProperties}
54
+ data-pw={typeof testId === 'string' ? testId : null}
55
+ >
42
56
  {@render children?.()}
43
57
  </div>
44
58
  {:else if useOutTransition}
45
- <div in:fade|global={fadeAnimationProperties} out:fade|global={fadeAnimationProperties}>
59
+ <div
60
+ in:fade|global={fadeAnimationProperties}
61
+ out:fade|global={fadeAnimationProperties}
62
+ data-pw={typeof testId === 'string' ? testId : null}
63
+ >
46
64
  {@render children?.()}
47
65
  </div>
48
66
  {:else}
49
- <div in:fade|global={fadeAnimationProperties}>
67
+ <div
68
+ in:fade|global={fadeAnimationProperties}
69
+ data-pw={typeof testId === 'string' ? testId : null}
70
+ >
50
71
  {@render children?.()}
51
72
  </div>
52
73
  {/if}
@@ -6,6 +6,7 @@ type Props = {
6
6
  align?: ModalAlign;
7
7
  transitionType?: ModalTransition;
8
8
  children?: Snippet;
9
+ testId?: string;
9
10
  };
10
11
  declare const ModalAnimation: import("svelte").Component<Props, {}, "">;
11
12
  type ModalAnimation = ReturnType<typeof ModalAnimation>;
@@ -4,11 +4,12 @@
4
4
 
5
5
  type Props = {
6
6
  children?: Snippet;
7
+ testId?: string;
7
8
  };
8
9
 
9
- let { children }: Props = $props();
10
+ let { children, testId }: Props = $props();
10
11
  </script>
11
12
 
12
- <div out:fade={{ duration: 350 }}>
13
+ <div out:fade={{ duration: 350 }} data-pw={typeof testId === 'string' ? testId : null}>
13
14
  {@render children?.()}
14
15
  </div>
@@ -1,6 +1,7 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  type Props = {
3
3
  children?: Snippet;
4
+ testId?: string;
4
5
  };
5
6
  declare const OverlayAnimation: import("svelte").Component<Props, {}, "">;
6
7
  type OverlayAnimation = ReturnType<typeof OverlayAnimation>;
@@ -884,6 +884,7 @@
884
884
  effectiveHighlightedIndex !== bar.pi)}
885
885
  d={stackedBarPath(bar)}
886
886
  fill={barFillAttr(bar)}
887
+ data-pw={`bar-${i}`}
887
888
  tabindex="0"
888
889
  role="button"
889
890
  aria-label="{bar.dataPoint.label}: {getDisplayValue(bar)}"
@@ -912,6 +913,7 @@
912
913
  rx={barRadius}
913
914
  ry={barRadius}
914
915
  fill={barFillAttr(bar)}
916
+ data-pw={`bar-${i}`}
915
917
  tabindex="0"
916
918
  role="button"
917
919
  aria-label="{bar.dataPoint.label}: {getDisplayValue(bar)}"
@@ -930,10 +932,12 @@
930
932
  <text
931
933
  class="bar-value"
932
934
  class:bar-value-inside={bl.p.placement === 'inside'}
935
+ data-inside={bl.p.placement === 'inside' ? 'true' : 'false'}
933
936
  x={bl.p.x}
934
937
  y={bl.p.y}
935
938
  text-anchor={bl.p.textAnchor}
936
939
  dominant-baseline={bl.p.dominantBaseline}
940
+ data-pw={`bar-value-${i}`}
937
941
  style={bl.p.placement === 'inside'
938
942
  ? `fill: ${getContrastColor(bl.bar.color)}; stroke: ${contrastOutline(bl.bar.color)};`
939
943
  : ''}>{bl.text}</text
@@ -1,10 +1,10 @@
1
1
  <script lang="ts">
2
2
  import type { BrandLoaderProperties } from './properties';
3
3
 
4
- let { brandLogoURL, brandText, subText, classes }: BrandLoaderProperties = $props();
4
+ let { brandLogoURL, brandText, subText, classes, testId }: BrandLoaderProperties = $props();
5
5
  </script>
6
6
 
7
- <div class="background {classes ?? ''}">
7
+ <div class="background {classes ?? ''}" data-pw={typeof testId === 'string' ? testId : null}>
8
8
  <div class="loader">
9
9
  <img src={brandLogoURL} alt="" />
10
10
  <div class="text">{brandText}</div>
@@ -3,4 +3,5 @@ export type BrandLoaderProperties = {
3
3
  brandText: string;
4
4
  subText?: string | null;
5
5
  classes?: string;
6
+ testId?: string;
6
7
  };
@@ -58,6 +58,7 @@
58
58
  tabindex={-1}
59
59
  aria-hidden="true"
60
60
  onclick={(e: MouseEvent) => e.stopPropagation()}
61
+ data-pw={typeof testId === 'string' ? `${testId}-native-input` : null}
61
62
  />
62
63
  <span
63
64
  class="box"
@@ -69,6 +70,7 @@
69
70
  aria-disabled={disabled}
70
71
  aria-controls={ariaControls ?? null}
71
72
  onkeydown={handleKeyDown}
73
+ data-pw={typeof testId === 'string' ? `${testId}-box` : null}
72
74
  >
73
75
  {#if checked && !indeterminate}
74
76
  {#if typeof checkedIcon === 'function'}
@@ -556,6 +556,7 @@
556
556
  onclick={togglePicker}
557
557
  ariaLabel={isOpen ? 'Close date picker' : 'Open date picker'}
558
558
  classes="drp-trigger {isOpen ? 'drp-trigger-open' : ''}"
559
+ {...typeof testId === 'string' ? { testId: `${testId}-trigger` } : {}}
559
560
  >
560
561
  {#if typeof triggerSnippet === 'function'}
561
562
  {@render triggerSnippet(triggerLabel)}
@@ -652,6 +653,7 @@
652
653
  role="dialog"
653
654
  aria-label="Date range picker"
654
655
  aria-modal="true"
656
+ data-pw={typeof testId === 'string' ? `${testId}-panel` : null}
655
657
  >
656
658
  <div class="drp-panel-inner">
657
659
  <!-- Preset sidebar -->
@@ -676,11 +678,17 @@
676
678
  role="option"
677
679
  aria-selected={isPresetActive(preset)}
678
680
  onclick={() => handlePreset(preset)}
681
+ data-pw={typeof testId === 'string' ? `${testId}-preset-${preset.label}` : null}
679
682
  >
680
683
  {preset.label}
681
684
  {#if presetCheckmark && isPresetActive(preset)}
682
685
  <!-- eslint-disable-next-line svelte/no-at-html-tags -->
683
- <span class="drp-preset-check" aria-hidden="true">{@html checkmarkSvg}</span>
686
+ <span
687
+ class="drp-preset-check"
688
+ aria-hidden="true"
689
+ data-pw={typeof testId === 'string' ? `${testId}-preset-check` : null}
690
+ >{@html checkmarkSvg}</span
691
+ >
684
692
  {/if}
685
693
  </button>
686
694
  {/each}
@@ -20,7 +20,7 @@ export type OptionalDeltaIndicatorProperties = {
20
20
  /** Absolute values at or below this are treated as `neutral` (default 0). */
21
21
  neutralThreshold?: number;
22
22
  /** Test selector applied as the `data-pw` attribute. */
23
- testId?: string;
23
+ testId?: string | null;
24
24
  /** Additional CSS classes for theming. */
25
25
  classes?: string;
26
26
  };
@@ -567,6 +567,7 @@
567
567
  width={hr.width}
568
568
  height={dims.innerHeight}
569
569
  fill="transparent"
570
+ data-pw={`hover-target-${hr.catIdx}`}
570
571
  data-category-index={hr.catIdx}
571
572
  tabindex="0"
572
573
  role="button"
@@ -332,6 +332,7 @@
332
332
  class:funnel-bar-dimmed={hoveredIndex !== null && hoveredIndex !== index}
333
333
  x={bx}
334
334
  y={by}
335
+ data-pw={`funnel-bar-${index}`}
335
336
  width={stageColumnWidth}
336
337
  height={bh}
337
338
  fill={color}
@@ -8,7 +8,8 @@
8
8
  showLoader = $bindable(false),
9
9
  onclick,
10
10
  onkeydown,
11
- classes
11
+ classes,
12
+ testId
12
13
  }: GridItemProperties = $props();
13
14
 
14
15
  function handleClick(event: MouseEvent) {
@@ -17,7 +18,14 @@
17
18
  }
18
19
  </script>
19
20
 
20
- <div class="container {classes ?? ''}" onclick={handleClick} {onkeydown} role="button" tabindex="0">
21
+ <div
22
+ class="container {classes ?? ''}"
23
+ onclick={handleClick}
24
+ {onkeydown}
25
+ role="button"
26
+ tabindex="0"
27
+ data-pw={typeof testId === 'string' ? testId : null}
28
+ >
21
29
  <div class="grid-header">
22
30
  <img src={headerIcon} alt="" class="grid-item-header-icon" />
23
31
  </div>
@@ -7,6 +7,7 @@ export type OptionalGridItemProperties = {
7
7
  headerIcon?: string | null;
8
8
  showLoader?: boolean;
9
9
  classes?: string;
10
+ testId?: string;
10
11
  };
11
12
  export type GridItemEventProperties = {
12
13
  onclick?: (event: MouseEvent) => void;
@@ -2,10 +2,17 @@
2
2
  import Img from '../Img/Img.svelte';
3
3
  import type { IconProperties } from './properties';
4
4
 
5
- let { icon, svg, text, onclick, onkeydown, classes }: IconProperties = $props();
5
+ let { icon, svg, text, onclick, onkeydown, classes, testId }: IconProperties = $props();
6
6
  </script>
7
7
 
8
- <div class="icon-container {classes ?? ''}" {onclick} {onkeydown} role="button" tabindex="0">
8
+ <div
9
+ class="icon-container {classes ?? ''}"
10
+ {onclick}
11
+ {onkeydown}
12
+ role="button"
13
+ tabindex="0"
14
+ data-pw={typeof testId === 'string' ? testId : null}
15
+ >
9
16
  {#if typeof svg === 'string' && svg.length > 0}
10
17
  <!-- eslint-disable svelte/no-at-html-tags -->
11
18
  <span class="icon-svg">{@html svg}</span>
@@ -4,6 +4,7 @@ export type OptionalIconProperties = {
4
4
  svg?: string;
5
5
  text?: string | null;
6
6
  classes?: string;
7
+ testId?: string;
7
8
  };
8
9
  export type IconEventProperties = {
9
10
  onclick?: (event: MouseEvent) => void;
@@ -1,10 +1,10 @@
1
1
  <script lang="ts">
2
2
  import type { IconStackProperties } from './properties';
3
3
 
4
- let { icons, classes }: IconStackProperties = $props();
4
+ let { icons, classes, testId }: IconStackProperties = $props();
5
5
  </script>
6
6
 
7
- <div class="stack-container {classes ?? ''}">
7
+ <div class="stack-container {classes ?? ''}" data-pw={typeof testId === 'string' ? testId : null}>
8
8
  {#each icons as icon, i (i)}
9
9
  <div class="stack-icon" style:z-index={icons.length - i}>
10
10
  {#if icon.type === 'image'}
@@ -5,4 +5,5 @@ export type IconStackItem = {
5
5
  export type IconStackProperties = {
6
6
  icons: IconStackItem[];
7
7
  classes?: string;
8
+ testId?: string;
8
9
  };
@@ -359,7 +359,10 @@
359
359
  {/if}
360
360
 
361
361
  {#if onErrorMessage !== '' && showError && !actionInput}
362
- <div class="error-message">
362
+ <div
363
+ class="error-message"
364
+ data-pw={typeof testId === 'string' && testId.length > 0 ? `${testId}-error-message` : null}
365
+ >
363
366
  {onErrorMessage}
364
367
  </div>
365
368
  {/if}
@@ -19,7 +19,8 @@
19
19
  classes,
20
20
  mandatory,
21
21
  size,
22
- error
22
+ error,
23
+ testId
23
24
  }: InputButtonProperties = $props();
24
25
 
25
26
  let validationState = $state<ValidationState>('InProgress');
@@ -61,7 +62,7 @@
61
62
  }
62
63
  </script>
63
64
 
64
- <div class="container {classes ?? ''}">
65
+ <div class="container {classes ?? ''}" data-pw={typeof testId === 'string' ? testId : null}>
65
66
  {#if inputProperties.label && inputProperties.label !== ''}
66
67
  <label class="label" for={inputProperties.name}>
67
68
  {inputProperties.label}{#if mandatory}<span class="mandatory-marker" aria-label="required">
@@ -33,6 +33,7 @@ export type OptionalInputButtonProperties = {
33
33
  * internal validation messages surface normally.
34
34
  */
35
35
  error?: string;
36
+ testId?: string;
36
37
  };
37
38
  export type InputButtonEventProperties = {
38
39
  inputEventProperties?: InputEventProperties;
@@ -584,7 +584,14 @@
584
584
  {/if}
585
585
 
586
586
  {#each haloPoints as hp, i (i)}
587
- <circle class="dot-halo" cx={hp.x} cy={hp.y} r={dotRadius + 6} fill={hp.color} />
587
+ <circle
588
+ class="dot-halo"
589
+ cx={hp.x}
590
+ cy={hp.y}
591
+ r={dotRadius + 6}
592
+ fill={hp.color}
593
+ data-pw={`dot-halo-${i}`}
594
+ />
588
595
  {/each}
589
596
 
590
597
  {#each lines as line, si (si)}
@@ -672,6 +679,7 @@
672
679
  width={dims.innerWidth}
673
680
  height={dims.innerHeight}
674
681
  fill="transparent"
682
+ data-pw="hover-overlay"
675
683
  onpointermove={handleOverlayMove}
676
684
  onpointerleave={handleLeave}
677
685
  onclick={handleClick}
@@ -13,7 +13,12 @@
13
13
  data-pw={typeof testId === 'string' ? testId : null}
14
14
  >
15
15
  {#each { length: count } as _, i (i)}
16
- <span class="dot" class:pulse={animation === 'pulse'} style="--i: {i}"></span>
16
+ <span
17
+ class="dot"
18
+ class:pulse={animation === 'pulse'}
19
+ style="--i: {i}"
20
+ data-pw={typeof testId === 'string' ? `${testId}-dot-${i}` : null}
21
+ ></span>
17
22
  {/each}
18
23
  </span>
19
24
 
@@ -109,6 +109,7 @@
109
109
  role={showLegend ? null : 'img'}
110
110
  aria-hidden={showLegend ? 'true' : null}
111
111
  aria-label={showLegend ? null : ariaSummary}
112
+ data-pw={typeof testId === 'string' ? `${testId}-svg` : null}
112
113
  >
113
114
  {#each rectSegments as rectSegment, rectIndex (rectIndex)}
114
115
  <rect
@@ -130,9 +131,16 @@
130
131
  </div>
131
132
 
132
133
  {#if showLegend}
133
- <ul class="proportion-bar-legend" aria-label="Segment breakdown">
134
+ <ul
135
+ class="proportion-bar-legend"
136
+ aria-label="Segment breakdown"
137
+ data-pw={typeof testId === 'string' ? `${testId}-legend` : null}
138
+ >
134
139
  {#each computedSegments as computedSegment, legendIndex (legendIndex)}
135
- <li class="proportion-bar-legend-item">
140
+ <li
141
+ class="proportion-bar-legend-item"
142
+ data-pw={typeof testId === 'string' ? `${testId}-legend-item-${legendIndex}` : null}
143
+ >
136
144
  <span
137
145
  class="proportion-bar-swatch"
138
146
  style="background: {computedSegment.color};"
@@ -427,6 +427,7 @@
427
427
  {disabled}
428
428
  autocomplete="off"
429
429
  tabindex={disabled ? -1 : 0}
430
+ data-pw={typeof testId === 'string' ? `${testId}-search` : null}
430
431
  />
431
432
  {/if}
432
433
  {:else}
@@ -451,6 +452,7 @@
451
452
  {disabled}
452
453
  autocomplete="off"
453
454
  tabindex={disabled ? -1 : 0}
455
+ data-pw={typeof testId === 'string' ? `${testId}-search` : null}
454
456
  />
455
457
  {:else if value.length === 0}
456
458
  <span class="select-placeholder">{placeholder}</span>
@@ -468,6 +470,7 @@
468
470
  {disabled}
469
471
  autocomplete="off"
470
472
  tabindex={disabled ? -1 : 0}
473
+ data-pw={typeof testId === 'string' ? `${testId}-search` : null}
471
474
  />
472
475
  {:else}
473
476
  <span class={displayText.length > 0 ? 'select-value' : 'select-placeholder'}>
@@ -524,12 +527,17 @@
524
527
  class:checked={allFilteredSelected}
525
528
  class:indeterminate={selectAllIndeterminate}
526
529
  aria-hidden="true"
530
+ data-checked={allFilteredSelected ? 'true' : 'false'}
531
+ data-pw={typeof testId === 'string' ? `${testId}-select-all-indicator` : null}
527
532
  >
528
533
  {#if allFilteredSelected}
529
534
  <!-- eslint-disable-next-line svelte/no-at-html-tags -->
530
535
  <span class="select-option-check">{@html checkmarkSvg}</span>
531
536
  {:else if selectAllIndeterminate}
532
- <span class="select-option-dash"></span>
537
+ <span
538
+ class="select-option-dash"
539
+ data-pw={typeof testId === 'string' ? `${testId}-select-all-dash` : null}
540
+ ></span>
533
541
  {/if}
534
542
  </span>
535
543
  {/if}
@@ -567,6 +575,10 @@
567
575
  class="select-option-indicator"
568
576
  class:checked={value.includes(row.item.id)}
569
577
  aria-hidden="true"
578
+ data-checked={value.includes(row.item.id) ? 'true' : 'false'}
579
+ data-pw={typeof testId === 'string'
580
+ ? `${testId}-option-indicator-${row.item.id}`
581
+ : null}
570
582
  >
571
583
  {#if value.includes(row.item.id)}
572
584
  <!-- eslint-disable-next-line svelte/no-at-html-tags -->
@@ -145,6 +145,7 @@
145
145
  aria-modal="true"
146
146
  aria-label={title ?? 'Sheet'}
147
147
  tabindex="-1"
148
+ data-pw={typeof testId === 'string' ? `${testId}-panel` : null}
148
149
  transition:fly|global={flyParams}
149
150
  onintroend={handleIntroEnd}
150
151
  onoutroend={handleOutroEnd}
@@ -91,7 +91,7 @@
91
91
  onkeydown={isInteractive ? handleKeydown : null}
92
92
  >
93
93
  {#if hasHeaderContent}
94
- <div class="statcard-header">
94
+ <div class="statcard-header" data-pw={typeof testId === 'string' ? `${testId}-header` : null}>
95
95
  <div class="statcard-header-left">
96
96
  {#if hasTitle}
97
97
  {#if tooltip}
@@ -112,7 +112,12 @@
112
112
  </span>
113
113
  </Tooltip>
114
114
  {:else}
115
- <div class="statcard-title">{title}</div>
115
+ <div
116
+ class="statcard-title"
117
+ data-pw={typeof testId === 'string' ? `${testId}-title` : null}
118
+ >
119
+ {title}
120
+ </div>
116
121
  {/if}
117
122
  {/if}
118
123
  {#if checkbox}
@@ -137,10 +142,17 @@
137
142
  {/if}
138
143
 
139
144
  {#if hasRows && rows}
140
- <div class="statcard-rows" class:statcard-rows-horizontal={rowsDirection === 'row'}>
145
+ <div
146
+ class="statcard-rows"
147
+ class:statcard-rows-horizontal={rowsDirection === 'row'}
148
+ data-pw={typeof testId === 'string' ? `${testId}-rows` : null}
149
+ >
141
150
  {#each rows as row, rowIndex (rowIndex)}
142
151
  {#if rowIndex > 0}
143
- <div class="statcard-row-divider"></div>
152
+ <div
153
+ class="statcard-row-divider"
154
+ data-pw={typeof testId === 'string' ? `${testId}-row-divider-${rowIndex}` : null}
155
+ ></div>
144
156
  {/if}
145
157
  <div class="statcard-row" data-pw={typeof row.testId === 'string' ? row.testId : null}>
146
158
  {#if typeof row.heading === 'string' && row.heading.length > 0}
@@ -159,9 +171,18 @@
159
171
  </div>
160
172
  {/if}
161
173
  <div class="statcard-row-value-line">
162
- <div class="statcard-value">{row.value}</div>
174
+ <div
175
+ class="statcard-value"
176
+ data-pw={typeof testId === 'string' ? `${testId}-value-${rowIndex}` : null}
177
+ >
178
+ {row.value}
179
+ </div>
163
180
  {#if typeof row.change === 'number'}
164
- <DeltaIndicator value={row.change} invertColors={row.invertChangeColors ?? false} />
181
+ <DeltaIndicator
182
+ value={row.change}
183
+ invertColors={row.invertChangeColors ?? false}
184
+ testId={testId ? `${testId}-delta-${rowIndex}` : null}
185
+ />
165
186
  {/if}
166
187
  {#if typeof row.additionalContent === 'string' && row.additionalContent.length > 0}
167
188
  <div class="statcard-row-additional">{row.additionalContent}</div>
@@ -173,7 +194,12 @@
173
194
  {/if}
174
195
  <div class="statcard-breakdown-grid">
175
196
  {#each row.breakdown as breakdownItem, breakdownIndex (breakdownIndex)}
176
- <div class="statcard-breakdown-item">
197
+ <div
198
+ class="statcard-breakdown-item"
199
+ data-pw={typeof testId === 'string'
200
+ ? `${testId}-breakdown-item-${rowIndex}-${breakdownIndex}`
201
+ : null}
202
+ >
177
203
  <div class="statcard-breakdown-label">{breakdownItem.label}</div>
178
204
  <div class="statcard-breakdown-value">{breakdownItem.value}</div>
179
205
  {#if typeof breakdownItem.change === 'number'}
@@ -192,9 +218,13 @@
192
218
  {:else}
193
219
  <div class="statcard-value-row">
194
220
  {#if typeof valueSnippet === 'function'}
195
- <div class="statcard-value">{@render valueSnippet()}</div>
221
+ <div class="statcard-value" data-pw={typeof testId === 'string' ? `${testId}-value` : null}>
222
+ {@render valueSnippet()}
223
+ </div>
196
224
  {:else if typeof value === 'string' && value.length > 0}
197
- <div class="statcard-value">{value}</div>
225
+ <div class="statcard-value" data-pw={typeof testId === 'string' ? `${testId}-value` : null}>
226
+ {value}
227
+ </div>
198
228
  {/if}
199
229
 
200
230
  {#if hasDelta}
@@ -210,7 +240,12 @@
210
240
  {/if}
211
241
 
212
242
  {#if typeof subtitle === 'string' && subtitle.length > 0}
213
- <div class="statcard-subtitle">{subtitle}</div>
243
+ <div
244
+ class="statcard-subtitle"
245
+ data-pw={typeof testId === 'string' ? `${testId}-subtitle` : null}
246
+ >
247
+ {subtitle}
248
+ </div>
214
249
  {/if}
215
250
 
216
251
  {#if children}
@@ -10,11 +10,12 @@
10
10
  buttonProperties,
11
11
  classes,
12
12
  onbuttonClick,
13
- icon
13
+ icon,
14
+ testId
14
15
  }: StatusProperties = $props();
15
16
  </script>
16
17
 
17
- <div class="background {classes ?? ''}">
18
+ <div class="background {classes ?? ''}" data-pw={typeof testId === 'string' ? testId : null}>
18
19
  <div class="order-status">
19
20
  <div class="status-image">
20
21
  {#if icon}
@@ -12,6 +12,7 @@ export type StatusProperties = StatusEventProperties & {
12
12
  * priority over `statusIcon` when provided.
13
13
  */
14
14
  icon?: Snippet;
15
+ testId?: string;
15
16
  };
16
17
  export type StatusEventProperties = {
17
18
  onbuttonClick?: () => void;
@@ -141,7 +141,10 @@
141
141
  : []}
142
142
  <div class="builtin-icon-label {alignmentClass}">
143
143
  {#each icons as iconSrc, iconIndex (`${iconIndex}-${iconSrc}`)}
144
- <span class="builtin-icon-label-icon">
144
+ <span
145
+ class="builtin-icon-label-icon"
146
+ data-pw={column.testId ? `${column.testId}-icon-${iconIndex}` : null}
147
+ >
145
148
  <Img src={String(iconSrc)} alt="" fallback="" />
146
149
  </span>
147
150
  {/each}
@@ -151,7 +154,7 @@
151
154
  {@const data = asJsonObject(value) ?? {}}
152
155
  <div class="builtin-image-two-line {alignmentClass}">
153
156
  {#if typeof data.imageUrl === 'string' && data.imageUrl}
154
- <span class="builtin-thumb">
157
+ <span class="builtin-thumb" data-pw={column.testId ? `${column.testId}-thumb` : null}>
155
158
  <Img
156
159
  src={data.imageUrl}
157
160
  alt={typeof data.text1 === 'string' ? data.text1 : ''}
@@ -159,7 +162,10 @@
159
162
  />
160
163
  </span>
161
164
  {:else}
162
- <span class="builtin-thumb builtin-thumb-placeholder"></span>
165
+ <span
166
+ class="builtin-thumb builtin-thumb-placeholder"
167
+ data-pw={column.testId ? `${column.testId}-thumb-placeholder` : null}
168
+ ></span>
163
169
  {/if}
164
170
  <div class="builtin-two-line {alignmentClass}">
165
171
  <span class="builtin-primary-text">{typeof data.text1 === 'string' ? data.text1 : '-'}</span>
@@ -171,8 +177,12 @@
171
177
  {@const tags = asTagArrayItems(value)}
172
178
  {#if tags}
173
179
  <div class="builtin-tag-array {alignmentClass}">
174
- {#each tags as tag (tag.text)}
175
- <Pill text={tag.text} classes={tag.classes ?? ''} />
180
+ {#each tags as tag, tagIndex (`${tagIndex}-${tag.text}`)}
181
+ <Pill
182
+ text={tag.text}
183
+ classes={tag.classes ?? ''}
184
+ testId={tag.testId ?? (column.testId && `${column.testId}-tag-${tagIndex}`)}
185
+ />
176
186
  {/each}
177
187
  </div>
178
188
  {:else}
@@ -206,13 +216,19 @@
206
216
  {/if}
207
217
  {#if typeof compare.trendPercent === 'number'}
208
218
  {#if compare.trendPercent > 0}
209
- <span class="builtin-trend builtin-trend-up">
219
+ <span
220
+ class="builtin-trend builtin-trend-up"
221
+ data-pw={column.testId ? `${column.testId}-trend-up` : null}
222
+ >
210
223
  <!-- eslint-disable svelte/no-at-html-tags -->
211
224
  <span class="builtin-trend-icon">{@html trendUpSvg}</span>
212
225
  {compare.trendPercent}%
213
226
  </span>
214
227
  {:else if compare.trendPercent < 0}
215
- <span class="builtin-trend builtin-trend-down">
228
+ <span
229
+ class="builtin-trend builtin-trend-down"
230
+ data-pw={column.testId ? `${column.testId}-trend-down` : null}
231
+ >
216
232
  <!-- eslint-disable svelte/no-at-html-tags -->
217
233
  <span class="builtin-trend-icon">{@html trendDownSvg}</span>
218
234
  {compare.trendPercent}%
@@ -480,7 +496,10 @@
480
496
  </Button>
481
497
  </span>
482
498
  {#if copied}
483
- <span class="builtin-link-copied">Copied</span>
499
+ <span
500
+ class="builtin-link-copied"
501
+ data-pw={column.testId ? `${column.testId}-link-copied` : null}>Copied</span
502
+ >
484
503
  {/if}
485
504
  {/if}
486
505
  </div>
@@ -630,6 +630,7 @@
630
630
  ? 'mixed'
631
631
  : headerCheckboxState === 'all'}
632
632
  aria-label="Select all rows"
633
+ data-pw={typeof testId === 'string' ? `${testId}-select-all` : null}
633
634
  {...checkboxSelection?.getRowAttributes
634
635
  ? checkboxSelection.getRowAttributes('__header__', -1)
635
636
  : {}}
@@ -817,6 +818,9 @@
817
818
  aria-checked={rowSelected}
818
819
  aria-disabled={rowDisabled}
819
820
  aria-label={`Select row ${rowId || 'non-selectable'}`}
821
+ data-pw={typeof testId === 'string'
822
+ ? `${testId}-row-checkbox-${rowId}`
823
+ : null}
820
824
  {...checkboxSelection?.getRowAttributes
821
825
  ? checkboxSelection.getRowAttributes(rowId, rowIndex)
822
826
  : {}}
@@ -859,6 +863,9 @@
859
863
  <div
860
864
  class={isContentScrollable ? 'scrollable-content' : ''}
861
865
  class:table-cell-clamp={keyedColumn?.maxWidth && isScalarCell}
866
+ data-pw={keyedColumn?.testId
867
+ ? `${keyedColumn.testId}-cell-${rowIndex}`
868
+ : null}
862
869
  >
863
870
  {#if keyedColumn && typeof keyedColumn.cell === 'function' && keyedRow}
864
871
  {@render keyedColumn.cell(keyedRow, rowIndex, originalIndex)}
@@ -894,7 +901,14 @@
894
901
  more than one page (or the server reports more chunks); a
895
902
  single-page table shows no footer. -->
896
903
  <div class="table-footer table-paginator" data-pw={pagination.testId ?? null}>
897
- <span class="table-paginator-range">{paginationRangeText}</span>
904
+ <span
905
+ class="table-paginator-range"
906
+ data-pw={typeof testId === 'string'
907
+ ? `${testId}-paginator-range`
908
+ : pagination?.testId
909
+ ? `${pagination.testId}-range`
910
+ : null}>{paginationRangeText}</span
911
+ >
898
912
  <span class="table-paginator-controls">
899
913
  {#if pageSizeOptions.length > 0}
900
914
  <span class="table-paginator-size">
@@ -52,6 +52,7 @@ export type TableImageTwoLineTextCellData = {
52
52
  export type TableTagArrayCellItem = {
53
53
  text: string;
54
54
  classes?: string;
55
+ testId?: string;
55
56
  };
56
57
  /** Cell shape for `type: 'avatar-stack'` — initials chips with overflow. */
57
58
  export type TableAvatarStackCellData = {
@@ -243,9 +244,6 @@ export type TableColumn = {
243
244
  /**
244
245
  * Horizontal alignment for this column's header and body cells. When unset,
245
246
  * cells follow the table-wide `--table-text-align` (left by default).
246
- * Built-in flex cells (compare, two-line-text, text-tag, icon-label,
247
- * image-two-line-text, tag-array, avatar-stack) mirror it into their flex
248
- * alignment so stacked/inline content follows the aligned edge too.
249
247
  */
250
248
  align?: 'left' | 'center' | 'right';
251
249
  /**
@@ -18,7 +18,7 @@
18
18
  </script>
19
19
 
20
20
  <div class="toolbar {classes ?? ''}" data-pw={testId}>
21
- <div class="content">
21
+ <div class="content" data-pw={typeof testId === 'string' ? `${testId}-content` : null}>
22
22
  {#if typeof leftContent === 'function'}
23
23
  {@render leftContent()}
24
24
  {:else if showBackButton && typeof backIcon === 'string' && backIcon.length > 0}
@@ -234,6 +234,10 @@
234
234
  }
235
235
  const bubble = document.createElement('div');
236
236
  bubble.setAttribute('role', 'tooltip');
237
+ bubble.setAttribute(
238
+ 'data-pw',
239
+ typeof testId === 'string' ? `${testId}-bubble` : 'tooltip-bubble'
240
+ );
237
241
 
238
242
  const coords = computePortalCoords(rect, pos);
239
243
  bubble.style.cssText = [
@@ -369,6 +373,7 @@
369
373
  class="tooltip-bubble {displayPosition}"
370
374
  role="tooltip"
371
375
  style:--tooltip-shift="{bubbleShift}px"
376
+ data-pw={typeof testId === 'string' ? `${testId}-bubble` : 'tooltip-bubble'}
372
377
  >
373
378
  <div class="tooltip-arrow"></div>
374
379
  {#if typeof content === 'function'}
@@ -44,7 +44,7 @@
44
44
  const TICK_SIZE = 6;
45
45
  </script>
46
46
 
47
- <g class="axis axis-{orientation} {classes ?? ''}">
47
+ <g class="axis axis-{orientation} {classes ?? ''}" data-pw={`axis-${orientation}`}>
48
48
  {#if isHorizontal}
49
49
  <line class="axis-line" x1={scale.range[0]} x2={scale.range[1]} y1={0} y2={0} />
50
50
  {#each tickValues as tick, i (i)}
@@ -58,6 +58,7 @@
58
58
  transform="translate(0, {TICK_SIZE + 4}) rotate(-45)"
59
59
  text-anchor="end"
60
60
  dominant-baseline="auto"
61
+ data-pw={`tick-label-${i}`}
61
62
  >
62
63
  {format(tick)}
63
64
  </text>
@@ -67,6 +68,7 @@
67
68
  y={orientation === 'bottom' ? TICK_SIZE + 4 : -(TICK_SIZE + 4)}
68
69
  text-anchor="middle"
69
70
  dominant-baseline={orientation === 'bottom' ? 'hanging' : 'auto'}
71
+ data-pw={`tick-label-${i}`}
70
72
  >
71
73
  {format(tick)}
72
74
  </text>
@@ -102,6 +104,7 @@
102
104
  x={orientation === 'left' ? -(TICK_SIZE + 4) : TICK_SIZE + 4}
103
105
  text-anchor={orientation === 'left' ? 'end' : 'start'}
104
106
  dominant-baseline="middle"
107
+ data-pw={`tick-label-${i}`}
105
108
  >
106
109
  {format(tick)}
107
110
  </text>
@@ -86,7 +86,7 @@
86
86
  <div class="tooltip-title">{tooltipData.title}</div>
87
87
  {/if}
88
88
  {#each tooltipData.items as item, i (i)}
89
- <div class="tooltip-item">
89
+ <div class="tooltip-item" data-pw={`tooltip-item-${i}`}>
90
90
  {#if item.color}
91
91
  <span class="tooltip-swatch" style="background: {item.color}"></span>
92
92
  {/if}
@@ -106,6 +106,7 @@
106
106
  class="chart-tooltip portal {unstyled ? 'unstyled' : ''} {classes ?? ''}"
107
107
  style="left: {pos.left}px; top: {pos.top}px;"
108
108
  use:portalToBody
109
+ data-pw="chart-tooltip"
109
110
  >
110
111
  {@render inner(data)}
111
112
  </div>
@@ -116,6 +117,7 @@
116
117
  bind:clientHeight={tooltipHeight}
117
118
  class="chart-tooltip {unstyled ? 'unstyled' : ''} {classes ?? ''}"
118
119
  style="left: {pos.left}px; top: {pos.top}px;"
120
+ data-pw="chart-tooltip"
119
121
  >
120
122
  {@render inner(data)}
121
123
  </div>
@@ -17,6 +17,7 @@
17
17
  class:legend-hidden={item.hidden}
18
18
  aria-pressed={!item.hidden}
19
19
  onclick={() => onToggle(i)}
20
+ data-pw={`legend-toggle-${i}`}
20
21
  >
21
22
  <span class="legend-swatch" style="background: {item.color}"></span>
22
23
  <span class="legend-label">{item.label}</span>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/svelte-ui-components",
3
- "version": "2.96.2",
3
+ "version": "2.97.0",
4
4
  "description": "A themeable Svelte 5 UI component library with CSS custom property driven styling",
5
5
  "keywords": [
6
6
  "svelte",