@juspay/svelte-ui-components 2.133.0 → 2.133.1

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.
@@ -13,7 +13,8 @@
13
13
  indeterminateIcon,
14
14
  onclick,
15
15
  classes,
16
- ariaControls
16
+ ariaControls,
17
+ ariaLabel
17
18
  }: CheckboxProperties = $props();
18
19
 
19
20
  function handleClick(): void {
@@ -71,6 +72,7 @@
71
72
  aria-checked={indeterminate ? 'mixed' : checked}
72
73
  aria-disabled={disabled}
73
74
  aria-controls={ariaControls ?? null}
75
+ aria-label={text.length === 0 ? (ariaLabel ?? null) : null}
74
76
  onkeydown={handleKeyDown}
75
77
  data-pw={typeof testId === 'string' ? `${testId}-box` : null}
76
78
  testID={typeof testId === 'string' ? `${testId}-box` : null}
@@ -12,6 +12,11 @@ export type OptionalCheckboxProperties = {
12
12
  indeterminateIcon?: Snippet;
13
13
  classes?: string;
14
14
  ariaControls?: string;
15
+ /** Accessible name for the checkbox. Needed whenever the visible label sits
16
+ * outside this component (a table header cell, an icon-only row control),
17
+ * since name-from-content cannot reach it. Ignored when `text` is non-empty:
18
+ * a visible label must stay part of the accessible name (WCAG 2.5.3). */
19
+ ariaLabel?: string;
15
20
  };
16
21
  export type CheckboxEventProperties = {
17
22
  onclick?: (checked: boolean) => void;
@@ -26,6 +26,7 @@
26
26
  autoComplete = 'on',
27
27
  inputMode,
28
28
  name = '',
29
+ id,
29
30
  testId = '',
30
31
  textTransformers = [],
31
32
  textViewPresentation = [],
@@ -60,6 +61,16 @@
60
61
  showCount = false
61
62
  }: InputProperties = $props();
62
63
 
64
+ /* `for` on a <label> resolves against an element's id, never its name. The label
65
+ was emitted with for={name} while the field itself carried only name={name},
66
+ so the association never completed for any caller. Derive the id from name so
67
+ every existing caller that already passes label + name becomes correctly
68
+ labelled with no change on their part; an explicit id wins, for callers who
69
+ need uniqueness across repeated rows or who set no name. */
70
+ const uid = $props.id();
71
+ const effectiveId = $derived(id || (name !== '' ? `${name}-${uid}` : uid));
72
+ const hasVisibleLabel = $derived(typeof label === 'string' && label !== '' && !actionInput);
73
+
63
74
  export function focus() {
64
75
  try {
65
76
  inputElement?.focus();
@@ -268,8 +279,8 @@
268
279
  </script>
269
280
 
270
281
  <div class="input-container {classes ?? ''}" class:input-error={showError && !actionInput}>
271
- {#if typeof label === 'string' && label !== '' && !actionInput}
272
- <label class="label" for={name}>
282
+ {#if hasVisibleLabel}
283
+ <label class="label" for={effectiveId}>
273
284
  {label}{#if mandatory}<span class="input-mandatory-asterisk" aria-hidden="true">*</span>{/if}
274
285
  </label>
275
286
  {/if}
@@ -277,9 +288,10 @@
277
288
  {#snippet fieldElement()}
278
289
  {#if useTextArea}
279
290
  <textarea
291
+ id={effectiveId}
280
292
  {value}
281
293
  {placeholder}
282
- aria-label={ariaLabel}
294
+ aria-label={hasVisibleLabel ? null : (ariaLabel ?? null)}
283
295
  autocomplete={autoComplete}
284
296
  inputmode={inputMode}
285
297
  {name}
@@ -311,10 +323,11 @@
311
323
  ></textarea>
312
324
  {:else}
313
325
  <input
326
+ id={effectiveId}
314
327
  type={dataType}
315
328
  {value}
316
329
  {placeholder}
317
- aria-label={ariaLabel}
330
+ aria-label={hasVisibleLabel ? null : (ariaLabel ?? null)}
318
331
  autocomplete={autoComplete}
319
332
  inputmode={inputMode}
320
333
  {name}
@@ -65,6 +65,12 @@ export type OptionalInputProperties = {
65
65
  */
66
66
  inputMode?: HTMLInputAttributes['inputmode'];
67
67
  name?: string;
68
+ /** Field id, applied to the rendered `<input>`/`<textarea>` so the visible
69
+ * `<label>` can reference it. Wins over the auto-derived id. Omitting it is
70
+ * safe: the fallback appends a per-instance suffix from `$props.id()`, so
71
+ * fields that share a `name` (radio groups, repeated rows) still get unique
72
+ * ids. Note this names the FIELD, not the wrapper `<div>`. */
73
+ id?: string;
68
74
  textTransformers?: TextTransformer[];
69
75
  textViewPresentation?: TextTransformer[];
70
76
  testId?: string;
@@ -18,6 +18,7 @@
18
18
  selectedValue = null,
19
19
  role: menuRole = 'menu',
20
20
  ariaLabel: menuAriaLabel,
21
+ triggerAriaLabel,
21
22
  id: menuId,
22
23
  placement = 'bottom-left',
23
24
  usePortal = false
@@ -410,6 +411,7 @@
410
411
  tabindex="0"
411
412
  aria-haspopup="menu"
412
413
  aria-expanded={open}
414
+ aria-label={triggerAriaLabel ?? null}
413
415
  >
414
416
  {#if typeof trigger === 'function'}
415
417
  {@render trigger({
@@ -59,6 +59,11 @@ export type OptionalMenuProperties = {
59
59
  selectedValue?: string | null;
60
60
  role?: 'menu' | 'listbox';
61
61
  ariaLabel?: string;
62
+ /** Accessible name for the TRIGGER — the element that actually takes focus.
63
+ * `ariaLabel` names the dropdown, which is portaled to <body>, so it cannot
64
+ * name the control the user tabs to. Applies only when `interactiveTrigger`
65
+ * is false; when true the snippet's own control owns its name. */
66
+ triggerAriaLabel?: string;
62
67
  id?: string;
63
68
  /** Dropdown anchoring relative to the trigger. Defaults to `'bottom-left'`,
64
69
  * which preserves the existing behavior (including the `--menu-dropdown-top`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/svelte-ui-components",
3
- "version": "2.133.0",
3
+ "version": "2.133.1",
4
4
  "description": "A themeable Svelte 5 UI component library with CSS custom property driven styling",
5
5
  "keywords": [
6
6
  "svelte",