@warp-ds/elements 2.8.1-next.2 → 2.8.1-next.3

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 (40) hide show
  1. package/dist/custom-elements.json +183 -2
  2. package/dist/index.d.ts +21 -2
  3. package/dist/packages/attention/attention.js +6 -6
  4. package/dist/packages/attention/attention.js.map +2 -2
  5. package/dist/packages/box/box.js +3 -3
  6. package/dist/packages/box/box.js.map +2 -2
  7. package/dist/packages/breadcrumbs/breadcrumbs.stories.js +11 -20
  8. package/dist/packages/button/button.js +2 -2
  9. package/dist/packages/button/button.js.map +2 -2
  10. package/dist/packages/checkbox-group/checkbox-group.js +1 -1
  11. package/dist/packages/checkbox-group/checkbox-group.js.map +2 -2
  12. package/dist/packages/datepicker/datepicker.stories.js +2 -4
  13. package/dist/packages/icon/icon.react.stories.js +1 -1
  14. package/dist/packages/icon/icon.stories.js +1 -1
  15. package/dist/packages/link/link.js +1 -1
  16. package/dist/packages/link/link.js.map +2 -2
  17. package/dist/packages/modal/modal.js +4 -3
  18. package/dist/packages/modal/modal.js.map +3 -3
  19. package/dist/packages/modal-header/modal-header.js +1 -1
  20. package/dist/packages/modal-header/modal-header.js.map +2 -2
  21. package/dist/packages/pill/pill.js +1 -1
  22. package/dist/packages/pill/pill.js.map +2 -2
  23. package/dist/packages/select/select.a11y.test.d.ts +1 -0
  24. package/dist/packages/select/select.a11y.test.js +124 -0
  25. package/dist/packages/select/select.d.ts +7 -4
  26. package/dist/packages/select/select.js +21 -21
  27. package/dist/packages/select/select.js.map +3 -3
  28. package/dist/packages/select/select.react.stories.d.ts +1 -1
  29. package/dist/packages/select/select.test.js +168 -0
  30. package/dist/packages/slider/slider.stories.js +14 -36
  31. package/dist/packages/slider-thumb/slider-thumb.js +1 -1
  32. package/dist/packages/slider-thumb/slider-thumb.js.map +2 -2
  33. package/dist/packages/tab/tab.js +1 -1
  34. package/dist/packages/tab/tab.js.map +2 -2
  35. package/dist/packages/textarea/textarea.js +9 -9
  36. package/dist/packages/textarea/textarea.js.map +2 -2
  37. package/dist/packages/textfield/textfield.js +1 -1
  38. package/dist/packages/textfield/textfield.js.map +2 -2
  39. package/dist/web-types.json +131 -44
  40. package/package.json +7 -7
@@ -3,7 +3,7 @@ import React from 'react';
3
3
  import { Select } from './react';
4
4
  declare const _default: {
5
5
  title: string;
6
- render(args: Omit<React.HTMLAttributes<import("./select").WarpSelect>, "name" | "form" | "label" | "onchange" | "autoFocus" | "onChange" | "render" | "renderOptions" | "connectedCallback" | "disconnectedCallback" | "renderRoot" | "isUpdatePending" | "hasUpdated" | "addController" | "removeController" | "attributeChangedCallback" | "requestUpdate" | "updateComplete" | "disabled" | "value" | "resetFormControl" | "validationTarget" | "internals" | "showError" | "validationMessage" | "validity" | "validationComplete" | "checkValidity" | "formResetCallback" | "valueChangedCallback" | "validityCallback" | "validationMessageCallback" | "setValue" | "shouldFormValueUpdate" | "invalid" | "optional" | "helpText" | "readOnly" | "readonly" | "always" | "hint" | "_options" | "_setValue" | "handleKeyDown"> & {
6
+ render(args: Omit<React.HTMLAttributes<import("./select").WarpSelect>, "name" | "form" | "label" | "onchange" | "autoFocus" | "onChange" | "render" | "renderOptions" | "connectedCallback" | "disconnectedCallback" | "renderRoot" | "isUpdatePending" | "hasUpdated" | "addController" | "removeController" | "attributeChangedCallback" | "requestUpdate" | "updateComplete" | "disabled" | "value" | "updated" | "firstUpdated" | "resetFormControl" | "validationTarget" | "internals" | "showError" | "validationMessage" | "validity" | "validationComplete" | "checkValidity" | "formResetCallback" | "valueChangedCallback" | "validityCallback" | "validationMessageCallback" | "setValue" | "shouldFormValueUpdate" | "invalid" | "optional" | "helpText" | "readOnly" | "readonly" | "willUpdate" | "formStateRestoreCallback" | "always" | "hint" | "_options" | "_setValue" | "handleKeyDown"> & {
7
7
  onChange?: (e: Event) => void;
8
8
  onchange?: (e: Event) => void;
9
9
  } & Partial<Omit<import("./select").WarpSelect, keyof HTMLElement>> & React.RefAttributes<import("./select").WarpSelect>): React.JSX.Element;
@@ -59,3 +59,171 @@ test('can reset select by resetting surrounding form', async () => {
59
59
  // Value should be reset back to "strawberries"
60
60
  expect(wSelect.value).toBe('strawberries');
61
61
  });
62
+ test('change event target.value reflects the selected value', async () => {
63
+ render(html `
64
+ <w-select label="Fruit">
65
+ <option value="apples">Apples</option>
66
+ <option value="pears">Pears</option>
67
+ </w-select>
68
+ `);
69
+ const wSelect = document.querySelector('w-select');
70
+ await wSelect.updateComplete;
71
+ const nativeSelect = wSelect.shadowRoot.querySelector('select');
72
+ let observedTargetValue = '';
73
+ wSelect.addEventListener('change', (e) => {
74
+ observedTargetValue = (e.target.value ?? '');
75
+ });
76
+ nativeSelect.value = 'pears';
77
+ nativeSelect.dispatchEvent(new Event('change'));
78
+ expect(observedTargetValue).toBe('pears');
79
+ });
80
+ test('keeps native select in sync when host value changes', async () => {
81
+ render(html `
82
+ <w-select label="Gender" value="male">
83
+ <option value="male">Male</option>
84
+ <option value="female">Female</option>
85
+ </w-select>
86
+ `);
87
+ const wSelect = document.querySelector('w-select');
88
+ await wSelect.updateComplete;
89
+ const nativeSelect = wSelect.shadowRoot.querySelector('select');
90
+ wSelect.value = 'female';
91
+ await wSelect.updateComplete;
92
+ expect(nativeSelect.value).toBe('female');
93
+ });
94
+ test('syncs host value when native select value changes without firing change', async () => {
95
+ render(html `
96
+ <form>
97
+ <w-select label="Gender" name="gender">
98
+ <option value="">Choose</option>
99
+ <option value="male">Male</option>
100
+ <option value="female">Female</option>
101
+ </w-select>
102
+ </form>
103
+ `);
104
+ const form = document.querySelector('form');
105
+ const wSelect = document.querySelector('w-select');
106
+ await wSelect.updateComplete;
107
+ const nativeSelect = wSelect.shadowRoot.querySelector('select');
108
+ // Simulates browser session restore/autocomplete updating only the internal native select.
109
+ nativeSelect.value = 'female';
110
+ window.dispatchEvent(new PageTransitionEvent('pageshow', { persisted: true }));
111
+ await expect.poll(() => wSelect.value).toBe('female');
112
+ expect(new FormData(form).get('gender')).toBe('female');
113
+ });
114
+ test('does not sync host value from the browser default first option', async () => {
115
+ render(html `
116
+ <w-select label="Default first option">
117
+ <option value="alpha">Alpha</option>
118
+ <option value="beta">Beta</option>
119
+ </w-select>
120
+ `);
121
+ const wSelect = document.querySelector('w-select');
122
+ await wSelect.updateComplete;
123
+ window.dispatchEvent(new PageTransitionEvent('pageshow', { persisted: true }));
124
+ expect(wSelect.value ?? '').toBe('');
125
+ });
126
+ test('pageshow does not overwrite an intentionally empty host value', async () => {
127
+ render(html `
128
+ <w-select label="Intentional empty" value="">
129
+ <option value="male">Male</option>
130
+ <option value="female">Female</option>
131
+ </w-select>
132
+ `);
133
+ const wSelect = document.querySelector('w-select');
134
+ await wSelect.updateComplete;
135
+ const nativeSelect = wSelect.shadowRoot.querySelector('select');
136
+ expect(nativeSelect.value).toBe('');
137
+ window.dispatchEvent(new PageTransitionEvent('pageshow', { persisted: true }));
138
+ expect(wSelect.value ?? '').toBe('');
139
+ });
140
+ test('formStateRestoreCallback syncs value from provided state', async () => {
141
+ render(html `
142
+ <form>
143
+ <w-select label="Restore state" name="gender">
144
+ <option value="">Choose</option>
145
+ <option value="male">Male</option>
146
+ <option value="female">Female</option>
147
+ </w-select>
148
+ </form>
149
+ `);
150
+ const form = document.querySelector('form');
151
+ const wSelect = document.querySelector('w-select');
152
+ await wSelect.updateComplete;
153
+ wSelect.formStateRestoreCallback('female', 'restore');
154
+ const nativeSelect = wSelect.shadowRoot.querySelector('select');
155
+ expect(wSelect.value).toBe('female');
156
+ expect(nativeSelect.value).toBe('female');
157
+ expect(new FormData(form).get('gender')).toBe('female');
158
+ });
159
+ test('formStateRestoreCallback with null falls back to native value reconciliation', async () => {
160
+ render(html `
161
+ <form>
162
+ <w-select label="Restore fallback" name="gender">
163
+ <option value="">Choose</option>
164
+ <option value="male">Male</option>
165
+ <option value="female">Female</option>
166
+ </w-select>
167
+ </form>
168
+ `);
169
+ const form = document.querySelector('form');
170
+ const wSelect = document.querySelector('w-select');
171
+ await wSelect.updateComplete;
172
+ const nativeSelect = wSelect.shadowRoot.querySelector('select');
173
+ nativeSelect.value = 'female';
174
+ wSelect.formStateRestoreCallback(null, 'restore');
175
+ expect(wSelect.value).toBe('female');
176
+ expect(new FormData(form).get('gender')).toBe('female');
177
+ });
178
+ test('change event keeps backward compatibility by exposing detail', async () => {
179
+ render(html `
180
+ <w-select label="Legacy change detail">
181
+ <option value="alpha">Alpha</option>
182
+ <option value="beta">Beta</option>
183
+ </w-select>
184
+ `);
185
+ const wSelect = document.querySelector('w-select');
186
+ await wSelect.updateComplete;
187
+ const nativeSelect = wSelect.shadowRoot.querySelector('select');
188
+ let observedDetail;
189
+ wSelect.addEventListener('change', (event) => {
190
+ observedDetail = event.detail;
191
+ });
192
+ nativeSelect.value = 'beta';
193
+ nativeSelect.dispatchEvent(new Event('change'));
194
+ expect(observedDetail).toBe('beta');
195
+ });
196
+ test('emits a single host change event for one native change interaction', async () => {
197
+ render(html `
198
+ <w-select label="Single change event">
199
+ <option value="alpha">Alpha</option>
200
+ <option value="beta">Beta</option>
201
+ </w-select>
202
+ `);
203
+ const wSelect = document.querySelector('w-select');
204
+ await wSelect.updateComplete;
205
+ const nativeSelect = wSelect.shadowRoot.querySelector('select');
206
+ let changeCount = 0;
207
+ wSelect.addEventListener('change', () => {
208
+ changeCount += 1;
209
+ });
210
+ nativeSelect.value = 'beta';
211
+ nativeSelect.dispatchEvent(new Event('change'));
212
+ expect(changeCount).toBe(1);
213
+ });
214
+ test('reflects dynamic light-DOM option selected changes into native select', async () => {
215
+ render(html `
216
+ <w-select label="Dynamic options">
217
+ <option value="alpha">Alpha</option>
218
+ <option value="beta">Beta</option>
219
+ </w-select>
220
+ `);
221
+ const wSelect = document.querySelector('w-select');
222
+ await wSelect.updateComplete;
223
+ const lightOptions = Array.from(wSelect.querySelectorAll('option'));
224
+ lightOptions[1].setAttribute('selected', '');
225
+ await expect.poll(() => {
226
+ const nativeSelect = wSelect.shadowRoot.querySelector('select');
227
+ return nativeSelect.value;
228
+ }).toBe('beta');
229
+ });
@@ -199,9 +199,9 @@ export const OpenEnded = {
199
199
  args: {},
200
200
  render() {
201
201
  return html `
202
- <form id="openended" style="margin-bottom: 16px" lang="nb">
202
+ <form id="openended" style="margin-bottom: 16px">
203
203
  <w-slider
204
- label="Produksjonsår"
204
+ label="Model year"
205
205
  min="1950"
206
206
  max="2025"
207
207
  data-testid="openended"
@@ -209,12 +209,12 @@ export const OpenEnded = {
209
209
  >
210
210
  <w-slider-thumb
211
211
  slot="from"
212
- aria-label="Fra år"
212
+ aria-label="From year"
213
213
  name="from-year"
214
214
  ></w-slider-thumb>
215
215
  <w-slider-thumb
216
216
  slot="to"
217
- aria-label="Til år"
217
+ aria-label="To year"
218
218
  name="to-year"
219
219
  ></w-slider-thumb>
220
220
  </w-slider>
@@ -238,9 +238,9 @@ export const OpenEnded = {
238
238
  );
239
239
  overunderSlider.labelFormatter = function (slot) {
240
240
  if (slot === 'from') {
241
- return 'Før 1950';
241
+ return 'Before 1950';
242
242
  }
243
- return '2025 +';
243
+ return '2025+';
244
244
  };
245
245
  overunderSlider.valueFormatter = function (value, slot) {
246
246
  if (slot === 'from' && value === '') {
@@ -265,9 +265,6 @@ export const OpenEnded = {
265
265
  },
266
266
  };
267
267
  export const SingleError = {
268
- args: {
269
- locale: 'nb',
270
- },
271
268
  render() {
272
269
  return html `
273
270
  <w-slider label="Single" min="0" max="100">
@@ -282,7 +279,7 @@ export const RangeError = {
282
279
  render() {
283
280
  return html `
284
281
  <form id="rangeerror" style="margin-bottom: 16px">
285
- <w-slider label="Production year" min="1950" max="2025" over under>
282
+ <w-slider label="Model year" min="1950" max="2025">
286
283
  <p slot="description">
287
284
  Try typing a from value higher than a to value
288
285
  </p>
@@ -317,18 +314,15 @@ export const RangeError = {
317
314
  };
318
315
  export const CustomError = {
319
316
  args: {
320
- locale: 'nb',
321
317
  error: "I'm an external error telling you something is wrong",
322
318
  invalid: true,
323
319
  },
324
320
  render(args) {
325
321
  return html `
326
322
  <w-slider
327
- label="Production year"
323
+ label="Model year"
328
324
  min="1950"
329
325
  max="2025"
330
- over
331
- under
332
326
  error="${args.error}"
333
327
  ?invalid="${args.invalid}"
334
328
  >
@@ -339,18 +333,13 @@ export const CustomError = {
339
333
  },
340
334
  };
341
335
  export const Description = {
342
- args: {
343
- locale: 'nb',
344
- },
345
336
  render() {
346
337
  return html `
347
338
  <w-slider
348
- label="Production year"
339
+ label="Model year"
349
340
  min="1950"
350
341
  max="2025"
351
- over
352
- under
353
- help-text="Production year of the car"
342
+ help-text="Model year of the car"
354
343
  >
355
344
  <w-slider-thumb slot="from" name="from"></w-slider-thumb>
356
345
  <w-slider-thumb slot="to" name="to"></w-slider-thumb>
@@ -359,17 +348,14 @@ export const Description = {
359
348
  },
360
349
  };
361
350
  export const VisuallyHiddenLabel = {
362
- args: {
363
- locale: 'nb',
364
- },
365
351
  render() {
366
352
  return html `
367
353
  <w-slider
368
354
  min="1950"
369
355
  max="2025"
370
- help-text="Production year of the car"
356
+ help-text="Model year of the car"
371
357
  >
372
- <legend class="sr-only" slot="label">Production year</legend>
358
+ <legend class="sr-only" slot="label">Model year</legend>
373
359
  <w-slider-thumb slot="from" name="from"></w-slider-thumb>
374
360
  <w-slider-thumb slot="to" name="to"></w-slider-thumb>
375
361
  </w-slider>
@@ -377,20 +363,15 @@ export const VisuallyHiddenLabel = {
377
363
  },
378
364
  };
379
365
  export const HiddenMinimumMaximumLabels = {
380
- args: {
381
- locale: 'nb',
382
- },
383
366
  render() {
384
367
  return html `
385
368
  <w-slider
386
369
  min="1950"
387
370
  max="2025"
388
- over
389
- under
390
- help-text="Production year of the car"
371
+ help-text="Model year of the car"
391
372
  data-testid="hidden-minmax-label"
392
373
  >
393
- <legend class="sr-only" slot="label">Production year</legend>
374
+ <legend class="sr-only" slot="label">Model year</legend>
394
375
  <w-slider-thumb slot="from" name="from"></w-slider-thumb>
395
376
  <w-slider-thumb slot="to" name="to"></w-slider-thumb>
396
377
  </w-slider>
@@ -404,9 +385,6 @@ export const HiddenMinimumMaximumLabels = {
404
385
  },
405
386
  };
406
387
  export const VisuallyHiddenTextfield = {
407
- args: {
408
- locale: 'nb',
409
- },
410
388
  render({ locale }) {
411
389
  return html `
412
390
  <output class="text-xs">
@@ -2637,7 +2637,7 @@ Please compile your catalog first.
2637
2637
  w-textfield {
2638
2638
  --w-textfield-placeholder-color-text: var(--w-s-color-text);
2639
2639
  }
2640
- `;var Oe={};var B,b,W,ee,pe,Y,re,me,Ie,u=class extends Te(Ae){constructor(){super(...arguments);ne(this,b);this.invalid=!1;this.openEnded=!1;this.suffix="";this._showTooltip=!1;this._inputHasFocus=!1;this._hiddenTextfield=!1;ne(this,B,null);this.anchorPositioningStyleElement=null}resetFormControl(){this.value=xe(this,B)}async updateFieldAfterValidation(){let r=this.shadowRoot.querySelector("w-textfield");await g(this,b,re).call(this,r.value,!0)}async connectedCallback(){if(super.connectedCallback(),ke(this,B,this.value),this.setValue(this.value),this.slot&&!this.ariaDescription&&(this.slot==="from"?this.ariaDescription=z.t({id:"slider.label.from",comment:"Accessible label for the 'from value' input field in a range slider",message:"From"}):this.slot==="to"&&(this.ariaDescription=z.t({id:"slider.label.to",comment:"Accessible label for the 'to value' input field in a range slider",message:"To"}))),"anchorName"in document.documentElement.style)await this.updateComplete;else{let a=Oe.url.substring(0,Oe.url.lastIndexOf("/"));try{let[{default:s}]=await Promise.all([import(`${a}/oddbird-css-anchor-positioning.js`),this.updateComplete]);this.anchorPositioningStyleElement||(this.anchorPositioningStyleElement=document.createElement("style"),this.shadowRoot.prepend(this.anchorPositioningStyleElement)),this.anchorPositioningStyleElement.textContent=`
2640
+ `;var Oe={};var B,b,W,ee,pe,Y,re,me,Ie,u=class extends Te(Ae){constructor(){super(...arguments);ne(this,b);this.disabled=!1;this.invalid=!1;this.openEnded=!1;this.suffix="";this._showTooltip=!1;this._inputHasFocus=!1;this._hiddenTextfield=!1;ne(this,B,null);this.anchorPositioningStyleElement=null}resetFormControl(){this.value=xe(this,B)}async updateFieldAfterValidation(){let r=this.shadowRoot.querySelector("w-textfield");await g(this,b,re).call(this,r.value,!0)}async connectedCallback(){if(super.connectedCallback(),ke(this,B,this.value),this.setValue(this.value),this.slot&&!this.ariaDescription&&(this.slot==="from"?this.ariaDescription=z.t({id:"slider.label.from",comment:"Accessible label for the 'from value' input field in a range slider",message:"From"}):this.slot==="to"&&(this.ariaDescription=z.t({id:"slider.label.to",comment:"Accessible label for the 'to value' input field in a range slider",message:"To"}))),"anchorName"in document.documentElement.style)await this.updateComplete;else{let a=Oe.url.substring(0,Oe.url.lastIndexOf("/"));try{let[{default:s}]=await Promise.all([import(`${a}/oddbird-css-anchor-positioning.js`),this.updateComplete]);this.anchorPositioningStyleElement||(this.anchorPositioningStyleElement=document.createElement("style"),this.shadowRoot.prepend(this.anchorPositioningStyleElement)),this.anchorPositioningStyleElement.textContent=`
2641
2641
  /*
2642
2642
  * The polyfill can only anchor to ::before and ::after pseudo elements, not the pseudo element slider thumb.
2643
2643
  * We work around that by recreating a transparent version of the active range