@lion/ui 0.15.2 → 0.15.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 (85) hide show
  1. package/README.md +44 -9
  2. package/components/form-core/src/validate/LionValidationFeedback.js +2 -2
  3. package/components/form-core/src/validate/ValidateMixin.js +3 -3
  4. package/components/form-core/test/validate/lion-validation-feedback.test.js +2 -2
  5. package/components/form-core/test-suites/ValidateMixin.suite.js +23 -11
  6. package/components/form-core/translations/bg.js +4 -4
  7. package/components/form-core/translations/cs.js +4 -4
  8. package/components/form-core/translations/de.js +4 -4
  9. package/components/form-core/translations/en.js +4 -4
  10. package/components/form-core/translations/es.js +4 -4
  11. package/components/form-core/translations/fr.js +4 -4
  12. package/components/form-core/translations/hu.js +4 -4
  13. package/components/form-core/translations/it.js +4 -4
  14. package/components/form-core/translations/nl.js +4 -4
  15. package/components/form-core/translations/pl.js +4 -4
  16. package/components/form-core/translations/ro.js +4 -4
  17. package/components/form-core/translations/ru.js +4 -4
  18. package/components/form-core/translations/sk.js +4 -4
  19. package/components/form-core/translations/uk.js +4 -4
  20. package/components/form-core/translations/zh.js +4 -4
  21. package/components/input-stepper/src/LionInputStepper.js +48 -52
  22. package/components/input-stepper/test/lion-input-stepper.test.js +253 -41
  23. package/components/pagination/src/LionPagination.js +2 -1
  24. package/components/pagination/test/lion-pagination.test.js +41 -0
  25. package/components/validate-messages/translations/it.js +2 -2
  26. package/custom-elements.json +148 -170
  27. package/dist-types/components/form-core/test-suites/ValidateMixin.suite.d.ts.map +1 -1
  28. package/dist-types/components/input-stepper/src/LionInputStepper.d.ts +4 -20
  29. package/dist-types/components/input-stepper/src/LionInputStepper.d.ts.map +1 -1
  30. package/dist-types/components/input-stepper/test/lion-input-stepper.test.d.ts.map +1 -1
  31. package/dist-types/components/pagination/src/LionPagination.d.ts.map +1 -1
  32. package/dist-types/tsconfig.tsbuildinfo +1 -1
  33. package/docs/components/checkbox-group/overview.md +2 -2
  34. package/docs/components/checkbox-group/use-cases.md +3 -9
  35. package/docs/components/collapsible/overview.md +1 -1
  36. package/docs/components/combobox/overview.md +1 -1
  37. package/docs/components/combobox/use-cases.md +1 -1
  38. package/docs/components/dialog/overview.md +1 -1
  39. package/docs/components/fieldset/overview.md +5 -5
  40. package/docs/components/form/overview.md +3 -3
  41. package/docs/components/form/use-cases.md +2 -2
  42. package/docs/components/icon/use-cases.md +1 -1
  43. package/docs/components/input/overview.md +1 -1
  44. package/docs/components/input-amount/overview.md +3 -3
  45. package/docs/components/input-date/overview.md +3 -3
  46. package/docs/components/input-datepicker/overview.md +5 -5
  47. package/docs/components/input-email/overview.md +2 -2
  48. package/docs/components/input-file/overview.md +1 -1
  49. package/docs/components/input-iban/overview.md +2 -2
  50. package/docs/components/input-range/overview.md +2 -2
  51. package/docs/components/input-stepper/api-table.md +2 -3
  52. package/docs/components/input-stepper/overview.md +1 -1
  53. package/docs/components/input-tel/overview.md +1 -1
  54. package/docs/components/input-tel/use-cases.md +6 -6
  55. package/docs/components/input-tel-dropdown/overview.md +1 -1
  56. package/docs/components/listbox/use-cases.md +1 -1
  57. package/docs/components/radio-group/overview.md +3 -3
  58. package/docs/components/radio-group/use-cases.md +3 -9
  59. package/docs/components/select/overview.md +1 -1
  60. package/docs/components/select/use-cases.md +6 -6
  61. package/docs/components/select-rich/use-cases.md +2 -2
  62. package/docs/components/tabs/examples.md +1 -1
  63. package/docs/fundamentals/index.md +4 -4
  64. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  65. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  66. package/docs/fundamentals/rationales/accessibility.md +1 -1
  67. package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
  68. package/docs/fundamentals/systems/core/overview.md +1 -1
  69. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  70. package/docs/fundamentals/systems/form/model-value.md +1 -1
  71. package/docs/fundamentals/systems/form/overview.md +28 -28
  72. package/docs/fundamentals/systems/form/validate.md +2 -2
  73. package/docs/fundamentals/systems/localize/overview.md +4 -4
  74. package/docs/fundamentals/systems/overlays/configuration.md +2 -2
  75. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  76. package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
  77. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  78. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  79. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  80. package/docs/guides/how-to/create-a-form.md +4 -4
  81. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  82. package/docs/guides/index.md +8 -8
  83. package/docs/guides/principles/index.md +5 -5
  84. package/docs/guides/principles/styling.md +1 -1
  85. package/package.json +1 -1
package/README.md CHANGED
@@ -26,15 +26,15 @@
26
26
  </p>
27
27
 
28
28
  <p align="center">
29
- <a href="https://lion-web.netlify.app">Website</a>
29
+ <a href="https://lion.js.org">Website</a>
30
30
  ·
31
- <a href="https://lion-web.netlify.app/fundamentals/">Fundamentals</a>
31
+ <a href="https://lion.js.org/fundamentals/">Fundamentals</a>
32
32
  ·
33
- <a href="https://lion-web.netlify.app/guides/">Guides</a>
33
+ <a href="https://lion.js.org/guides/">Guides</a>
34
34
  ·
35
- <a href="https://lion-web.netlify.app/components/">Components</a>
35
+ <a href="https://lion.js.org/components/">Components</a>
36
36
  ·
37
- <a href="https://lion-web.netlify.app/blog/">Blog</a>
37
+ <a href="https://lion.js.org/blog/">Blog</a>
38
38
  </p>
39
39
 
40
40
  **Lion is a set of highly performant, accessible and flexible Web Components.!**
@@ -51,9 +51,38 @@ They provide an unopinionated, white-label layer that can be extended to your ow
51
51
  > This makes sense as the main use case is to extend those components and if you do you do not want to override existing stylings.
52
52
 
53
53
  <p align="center">
54
- <a href="https://lion-web.netlify.app/guides/"><strong>Explore the Lion Guides&nbsp;&nbsp;▶</strong></a>
54
+ <a href="https://lion.js.org/guides/"><strong>Explore the Lion Guides&nbsp;&nbsp;▶</strong></a>
55
55
  </p>
56
56
 
57
+ <details>
58
+ <summary><h2>Table of Contents</h2></summary>
59
+ <ul>
60
+ <li><a href="#astro-migration"> Astro migration </a>
61
+ <ul>
62
+ <li><a href="#astro-how-to"> Astro how to </a></li>
63
+ <li><a href="#issues-which-are-not-caused-by-the-migration-not-to-be-fixed-now"> Issues which are not caused by migration </a></li>
64
+ </ul>
65
+ </li>
66
+ <li><a href="#how-to-install"> Installation </a></li>
67
+ <li><a href="#how-to-use"> How to use </a>
68
+ <ul>
69
+ <li><a href="#extend-a-web-component"> Extend a web component </a></li>
70
+ <li><a href="#use-a-javascript-system"> Use a JavaScript system </a></li>
71
+ <li><a href="#use-a-web-component"> Use a Web Component </a></li>
72
+ </ul>
73
+ </li>
74
+ <li><a href="#issues"> Issues </a></li>
75
+ <li><a href="#feature-requests"> Feature request </a></li>
76
+ <li><a href="#content"> Content </a></li>
77
+ <li><a href="#technologies"> Technologies </a></li>
78
+ <li><a href="#rationale"> Rationale </a></li>
79
+ <li><a href="#coding-guidelines"> Coding guidelines </a></li>
80
+ <li><a href="#how-to-contribute"> How to contribute </a></li>
81
+ <li><a href="#site-deployment"> Site deployment </a></li>
82
+ <li><a href="#content"> Contact </a></li>
83
+ </ul>
84
+ </details>
85
+
57
86
  ## Astro migration
58
87
 
59
88
  - Keep using `/docs` on the root level as we used it in the `master` branch. The documentation is copied into Astro related directories on `npm run start` and when when anything in `/docs` is updated.
@@ -77,8 +106,6 @@ They provide an unopinionated, white-label layer that can be extended to your ow
77
106
 
78
107
  Note. There is the same error on master. This issue is not caused by the migration
79
108
 
80
- ### TODO
81
-
82
109
  ## How to install
83
110
 
84
111
  ```bash
@@ -184,7 +211,7 @@ This means you only have to apply your own Design System: by delivering styles,
184
211
 
185
212
  ## Coding guidelines
186
213
 
187
- Check out our [coding guidelines](https://lion-web.netlify.app/guides/principles/definitions-and-terms/) for more detailed information.
214
+ Check out our [coding guidelines](https://lion.js.org/guides/principles/definitions-and-terms/) for more detailed information.
188
215
 
189
216
  ## How to contribute
190
217
 
@@ -193,6 +220,14 @@ Check out our [coding guidelines](https://lion-web.netlify.app/guides/principles
193
220
  Lion Web Components are only as good as its contributions.
194
221
  Read our [contribution guide](https://github.com/ing-bank/lion/blob/master/CONTRIBUTING.md) and feel free to enhance/improve Lion. We keep feature requests closed while we're not working on them.
195
222
 
223
+ ## Site deployment
224
+
225
+ We use [GitHub Pages](https://docs.github.com/en/pages) to host our static website. Collaborators with push permission to the repository branch [`gh-pages`](https://github.com/ing-bank/lion/tree/gh-pages) can trigger a deploy by simply running:
226
+
227
+ ```bash
228
+ npm run deploy
229
+ ```
230
+
196
231
  ## Contact
197
232
 
198
233
  Feel free to create a github issue for any feedback or questions you might have.
@@ -87,11 +87,11 @@ export class LionValidationFeedback extends LocalizeMixin(LitElement) {
87
87
  ${this.feedbackData &&
88
88
  this.feedbackData.map(
89
89
  ({ message, type, validator }) => html`
90
- <div class="validation-feedback__type">
90
+ <span class="validation-feedback__type">
91
91
  ${message && type
92
92
  ? this._localizeManager.msg(`lion-form-core:validation${capitalize(type)}`)
93
93
  : nothing}
94
- </div>
94
+ </span>
95
95
  ${this._messageTemplate({ message, type, validator })}
96
96
  `,
97
97
  )}
@@ -1,15 +1,15 @@
1
1
  /* eslint-disable class-methods-use-this, camelcase, no-param-reassign, max-classes-per-file */
2
- import { SlotMixin, DisabledMixin } from '@lion/ui/core.js';
2
+ import { DisabledMixin, SlotMixin } from '@lion/ui/core.js';
3
3
  import { dedupeMixin } from '@open-wc/dedupe-mixin';
4
4
  // TODO: make form-core independent from localize
5
5
  import { getLocalizeManager } from '@lion/ui/localize-no-side-effects.js';
6
6
  import { ScopedElementsMixin } from '../../../core/src/ScopedElementsMixin.js';
7
+ import { FormControlMixin } from '../FormControlMixin.js';
7
8
  import { AsyncQueue } from '../utils/AsyncQueue.js';
8
9
  import { pascalCase } from '../utils/pascalCase.js';
9
10
  import { SyncUpdatableMixin } from '../utils/SyncUpdatableMixin.js';
10
11
  import { LionValidationFeedback } from './LionValidationFeedback.js';
11
12
  import { Unparseable } from './Unparseable.js';
12
- import { FormControlMixin } from '../FormControlMixin.js';
13
13
  // eslint-disable-next-line no-unused-vars
14
14
  import { ResultValidator as MetaValidator } from './ResultValidator.js';
15
15
  // eslint-disable-next-line no-unused-vars
@@ -399,7 +399,7 @@ export const ValidateMixinImplementation = superclass =>
399
399
  this.__validateCompleteResolve = resolve;
400
400
  });
401
401
 
402
- if (this.disabled) {
402
+ if (this.disabled || this.readOnly) {
403
403
  this.__clearValidationResults();
404
404
  this.__finishValidationPass();
405
405
  this._updateFeedbackComponent();
@@ -61,12 +61,12 @@ describe('lion-validation-feedback', () => {
61
61
  await el.updateComplete;
62
62
 
63
63
  const validationFeedbackType = el.shadowRoot?.querySelector('.validation-feedback__type');
64
- expect(validationFeedbackType?.textContent?.trim()).to.equal('Error');
64
+ expect(validationFeedbackType?.textContent?.trim()).to.equal('Error,');
65
65
 
66
66
  el.feedbackData = [{ message: 'hello', type: 'info', validator: new AlwaysInvalid() }];
67
67
  await el.updateComplete;
68
68
 
69
- expect(validationFeedbackType?.textContent?.trim()).to.equal('Info');
69
+ expect(validationFeedbackType?.textContent?.trim()).to.equal('Info,');
70
70
  });
71
71
 
72
72
  it('it does not share the type if there is no message', async () => {
@@ -1,26 +1,26 @@
1
- import { LitElement } from 'lit';
2
- import { aTimeout, defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing';
3
1
  import {
4
- getFormControlMembers,
5
- AsyncAlwaysInvalid,
6
- AsyncAlwaysValid,
7
2
  AlwaysInvalid,
8
3
  AlwaysValid,
4
+ AsyncAlwaysInvalid,
5
+ AsyncAlwaysValid,
6
+ getFormControlMembers,
9
7
  } from '@lion/ui/form-core-test-helpers.js';
10
- import sinon from 'sinon';
11
8
  import {
12
- ResultValidator,
13
- ValidateMixin,
14
9
  EqualsLength,
15
- Unparseable,
16
10
  MaxLength,
17
11
  MinLength,
18
- Validator,
19
12
  Required,
13
+ ResultValidator,
14
+ Unparseable,
15
+ ValidateMixin,
16
+ Validator,
20
17
  } from '@lion/ui/form-core.js';
18
+ import { aTimeout, defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing';
19
+ import { LitElement } from 'lit';
20
+ import sinon from 'sinon';
21
21
 
22
- import '@lion/ui/define/lion-validation-feedback.js';
23
22
  import '@lion/ui/define/lion-field.js';
23
+ import '@lion/ui/define/lion-validation-feedback.js';
24
24
 
25
25
  /**
26
26
  * @typedef {import('@lion/ui/form-core.js').LionField} LionField
@@ -631,6 +631,18 @@ export function runValidateMixinSuite(customConfig) {
631
631
  isCatValidator.param = 'Garfield';
632
632
  expect(catSpy.callCount).to.equal(2);
633
633
  });
634
+
635
+ it('Validators will not be called on readOnly fields', async () => {
636
+ const el = /** @type {ValidateElement} */ (
637
+ await fixture(html`
638
+ <${tag} .validators=${[new IsCat()]}>${lightDom}</${tag}>
639
+ `)
640
+ );
641
+
642
+ el.readOnly = true;
643
+ el.modelValue = 'dog';
644
+ expect(el.validationStates.error.IsCat).to.be.undefined;
645
+ });
634
646
  });
635
647
 
636
648
  describe('Async Validator Integration', () => {
@@ -1,6 +1,6 @@
1
1
  export default {
2
- validationError: 'Грешка',
3
- validationWarning: 'Предупреждение',
4
- validationSuccess: 'Успех',
5
- validationInfo: 'Информация',
2
+ validationError: 'Грешка,',
3
+ validationWarning: 'Предупреждение,',
4
+ validationSuccess: 'Успех,',
5
+ validationInfo: 'Информация,',
6
6
  };
@@ -1,6 +1,6 @@
1
1
  export default {
2
- validationError: 'Chyba',
3
- validationWarning: 'Varování',
4
- validationSuccess: 'Úspěch',
5
- validationInfo: 'Informace',
2
+ validationError: 'Chyba,',
3
+ validationWarning: 'Varování,',
4
+ validationSuccess: 'Úspěch,',
5
+ validationInfo: 'Informace,',
6
6
  };
@@ -1,6 +1,6 @@
1
1
  export default {
2
- validationError: 'Fehler',
3
- validationWarning: 'Warnhinweis',
4
- validationSuccess: 'Erfolgreich',
5
- validationInfo: 'Info',
2
+ validationError: 'Fehler,',
3
+ validationWarning: 'Warnhinweis,',
4
+ validationSuccess: 'Erfolgreich,',
5
+ validationInfo: 'Info,',
6
6
  };
@@ -1,6 +1,6 @@
1
1
  export default {
2
- validationError: 'Error',
3
- validationWarning: 'Warning',
4
- validationSuccess: 'Success',
5
- validationInfo: 'Info',
2
+ validationError: 'Error,',
3
+ validationWarning: 'Warning,',
4
+ validationSuccess: 'Success,',
5
+ validationInfo: 'Info,',
6
6
  };
@@ -1,6 +1,6 @@
1
1
  export default {
2
- validationError: 'Error',
3
- validationWarning: 'Advertencia',
4
- validationSuccess: 'Satisfactorio',
5
- validationInfo: 'Información',
2
+ validationError: 'Error,',
3
+ validationWarning: 'Advertencia,',
4
+ validationSuccess: 'Satisfactorio,',
5
+ validationInfo: 'Información,',
6
6
  };
@@ -1,6 +1,6 @@
1
1
  export default {
2
- validationError: 'Erreur',
3
- validationWarning: 'Avertissement',
4
- validationSuccess: 'Succès',
5
- validationInfo: 'Info',
2
+ validationError: 'Erreur,',
3
+ validationWarning: 'Avertissement,',
4
+ validationSuccess: 'Succès,',
5
+ validationInfo: 'Info,',
6
6
  };
@@ -1,6 +1,6 @@
1
1
  export default {
2
- validationError: 'Hiba',
3
- validationWarning: 'Figyelmeztetés',
4
- validationSuccess: 'Sikeres',
5
- validationInfo: 'Információ',
2
+ validationError: 'Hiba,',
3
+ validationWarning: 'Figyelmeztetés,',
4
+ validationSuccess: 'Sikeres,',
5
+ validationInfo: 'Információ,',
6
6
  };
@@ -1,6 +1,6 @@
1
1
  export default {
2
- validationError: 'Errore',
3
- validationWarning: 'Avvertenza',
4
- validationSuccess: 'Operazione riuscita',
5
- validationInfo: 'Info',
2
+ validationError: 'Errore,',
3
+ validationWarning: 'Avvertenza,',
4
+ validationSuccess: 'Operazione riuscita,',
5
+ validationInfo: 'Info,',
6
6
  };
@@ -1,6 +1,6 @@
1
1
  export default {
2
- validationError: 'Fout',
3
- validationWarning: 'Waarschuwing',
4
- validationSuccess: 'Succes',
5
- validationInfo: 'Informatie',
2
+ validationError: 'Fout,',
3
+ validationWarning: 'Waarschuwing,',
4
+ validationSuccess: 'Succes,',
5
+ validationInfo: 'Informatie,',
6
6
  };
@@ -1,6 +1,6 @@
1
1
  export default {
2
- validationError: 'Błąd',
3
- validationWarning: 'Ostrzeżenie',
4
- validationSuccess: 'Zrealizowano pomyślnie',
5
- validationInfo: 'Informacja',
2
+ validationError: 'Błąd,',
3
+ validationWarning: 'Ostrzeżenie,',
4
+ validationSuccess: 'Zrealizowano pomyślnie,',
5
+ validationInfo: 'Informacja,',
6
6
  };
@@ -1,6 +1,6 @@
1
1
  export default {
2
- validationError: 'Eroare',
3
- validationWarning: 'Atenție',
4
- validationSuccess: 'Succes',
5
- validationInfo: 'Informații',
2
+ validationError: 'Eroare,',
3
+ validationWarning: 'Atenție,',
4
+ validationSuccess: 'Succes,',
5
+ validationInfo: 'Informații,',
6
6
  };
@@ -1,6 +1,6 @@
1
1
  export default {
2
- validationError: 'Ошибка',
3
- validationWarning: 'Предупреждение',
4
- validationSuccess: 'Успешно',
5
- validationInfo: 'Информация',
2
+ validationError: 'Ошибка,',
3
+ validationWarning: 'Предупреждение,',
4
+ validationSuccess: 'Успешно,',
5
+ validationInfo: 'Информация,',
6
6
  };
@@ -1,6 +1,6 @@
1
1
  export default {
2
- validationError: 'Chyba',
3
- validationWarning: 'Varovanie',
4
- validationSuccess: 'Úspešné',
5
- validationInfo: 'Info',
2
+ validationError: 'Chyba,',
3
+ validationWarning: 'Varovanie,',
4
+ validationSuccess: 'Úspešné,',
5
+ validationInfo: 'Info,',
6
6
  };
@@ -1,6 +1,6 @@
1
1
  export default {
2
- validationError: 'Помилка',
3
- validationWarning: 'Попередження',
4
- validationSuccess: 'Успішно',
5
- validationInfo: 'Інформація',
2
+ validationError: 'Помилка,',
3
+ validationWarning: 'Попередження,',
4
+ validationSuccess: 'Успішно,',
5
+ validationInfo: 'Інформація,',
6
6
  };
@@ -1,6 +1,6 @@
1
1
  export default {
2
- validationError: '错误',
3
- validationWarning: '警告',
4
- validationSuccess: '成功',
5
- validationInfo: '信息',
2
+ validationError: '错误,',
3
+ validationWarning: '警告,',
4
+ validationSuccess: '成功,',
5
+ validationInfo: '信息,',
6
6
  };
@@ -1,4 +1,4 @@
1
- import { html, css, render } from 'lit';
1
+ import { html, css, render, nothing } from 'lit';
2
2
  import { formatNumber, LocalizeMixin, parseNumber } from '@lion/ui/localize-no-side-effects.js';
3
3
  import { LionInput } from '@lion/ui/input.js';
4
4
  import { IsNumber, MinNumber, MaxNumber } from '@lion/ui/form-core.js';
@@ -96,8 +96,6 @@ export class LionInputStepper extends LocalizeMixin(LionInput) {
96
96
 
97
97
  this._increment = this._increment.bind(this);
98
98
  this._decrement = this._decrement.bind(this);
99
- this._onEnterButton = this._onEnterButton.bind(this);
100
- this._onLeaveButton = this._onLeaveButton.bind(this);
101
99
  }
102
100
 
103
101
  connectedCallback() {
@@ -244,6 +242,26 @@ export class LionInputStepper extends LocalizeMixin(LionInput) {
244
242
  this._inputNode.removeAttribute('aria-valuenow');
245
243
  this._inputNode.removeAttribute('aria-valuetext');
246
244
  }
245
+ this._destroyOutputContent();
246
+ }
247
+
248
+ _destroyOutputContent() {
249
+ const outputElement = /** @type {HTMLElement} */ (
250
+ this.shadowRoot?.querySelector('.input-stepper__value')
251
+ );
252
+
253
+ const timeoutValue = outputElement?.dataset?.selfDestruct
254
+ ? Number(outputElement.dataset.selfDestruct)
255
+ : 2000;
256
+ clearTimeout(this.timer);
257
+ if (outputElement) {
258
+ this.timer = setTimeout(() => {
259
+ if (outputElement.parentNode) {
260
+ this.__valueText = nothing;
261
+ this.requestUpdate();
262
+ }
263
+ }, timeoutValue);
264
+ }
247
265
  }
248
266
 
249
267
  /**
@@ -267,12 +285,19 @@ export class LionInputStepper extends LocalizeMixin(LionInput) {
267
285
  _increment() {
268
286
  const { step, min, max } = this.values;
269
287
  const stepMin = min !== Infinity ? min : 0;
288
+ const epsilon = 1e-10; // Tolerance for floating-point comparison
270
289
 
271
- let newValue = this.currentValue + step;
290
+ let newValue;
272
291
 
273
- if ((this.currentValue + stepMin) % step !== 0) {
274
- // If the value is not aligned to step, align it to the nearest step
275
- newValue = Math.floor(this.currentValue / step) * step + step + (stepMin % step);
292
+ const remainder = (this.currentValue - stepMin) % step;
293
+ const isAligned = Math.abs(remainder) < epsilon || Math.abs(remainder - step) < epsilon;
294
+
295
+ if (!isAligned) {
296
+ // If the value is not aligned to step, align it to the next valid step
297
+ newValue = Math.ceil((this.currentValue - stepMin) / step) * step + stepMin;
298
+ } else {
299
+ // If the value is aligned, just add the step
300
+ newValue = this.currentValue + step;
276
301
  }
277
302
 
278
303
  if (newValue <= max || max === Infinity) {
@@ -289,12 +314,19 @@ export class LionInputStepper extends LocalizeMixin(LionInput) {
289
314
  _decrement() {
290
315
  const { step, max, min } = this.values;
291
316
  const stepMin = min !== Infinity ? min : 0;
317
+ const epsilon = 1e-10; // Tolerance for floating-point comparison
318
+
319
+ let newValue;
292
320
 
293
- let newValue = this.currentValue - step;
321
+ const remainder = (this.currentValue - stepMin) % step;
322
+ const isAligned = Math.abs(remainder) < epsilon || Math.abs(remainder - step) < epsilon;
294
323
 
295
- if ((this.currentValue + stepMin) % step !== 0) {
296
- // If the value is not aligned to step, align it to the nearest step
297
- newValue = Math.floor(this.currentValue / step) * step + (stepMin % step);
324
+ if (!isAligned) {
325
+ // If the value is not aligned to step, align it to the previous valid step
326
+ newValue = Math.floor((this.currentValue - stepMin) / step) * step + stepMin;
327
+ } else {
328
+ // If the value is aligned, just subtract the step
329
+ newValue = this.currentValue - step;
298
330
  }
299
331
 
300
332
  if (newValue >= min || min === Infinity) {
@@ -380,10 +412,8 @@ export class LionInputStepper extends LocalizeMixin(LionInput) {
380
412
  <button
381
413
  ?disabled=${this.disabled || this.readOnly}
382
414
  @click=${this._decrement}
383
- @focus=${this._onEnterButton}
384
- @blur=${this._onLeaveButton}
385
415
  type="button"
386
- aria-label="${this.msgLit('lion-input-stepper:decrease')}"
416
+ aria-label="${this.msgLit('lion-input-stepper:decrease')} ${this.fieldName}"
387
417
  >
388
418
  ${this._decrementorSignTemplate()}
389
419
  </button>
@@ -400,10 +430,8 @@ export class LionInputStepper extends LocalizeMixin(LionInput) {
400
430
  <button
401
431
  ?disabled=${this.disabled || this.readOnly}
402
432
  @click=${this._increment}
403
- @focus=${this._onEnterButton}
404
- @blur=${this._onLeaveButton}
405
433
  type="button"
406
- aria-label="${this.msgLit('lion-input-stepper:increase')}"
434
+ aria-label="${this.msgLit('lion-input-stepper:increase')} ${this.fieldName}"
407
435
  >
408
436
  ${this._incrementorSignTemplate()}
409
437
  </button>
@@ -413,7 +441,9 @@ export class LionInputStepper extends LocalizeMixin(LionInput) {
413
441
  /** @protected */
414
442
  _inputGroupTemplate() {
415
443
  return html`
416
- <div class="input-stepper__value">${this.__valueText}</div>
444
+ <output for="${this._inputId}" data-self-destruct="2000" class="input-stepper__value"
445
+ >${this.__valueText}</output
446
+ >
417
447
  <div class="input-group">
418
448
  ${this._inputGroupBeforeTemplate()}
419
449
  <div class="input-group__container">
@@ -424,38 +454,4 @@ export class LionInputStepper extends LocalizeMixin(LionInput) {
424
454
  </div>
425
455
  `;
426
456
  }
427
-
428
- /**
429
- * @protected
430
- * @param {Event} ev
431
- */
432
- // eslint-disable-next-line no-unused-vars
433
- _onEnterButton(ev) {
434
- const valueNode = /** @type {HTMLElement} */ (
435
- this.shadowRoot?.querySelector('.input-stepper__value')
436
- );
437
- valueNode.setAttribute('aria-live', 'assertive');
438
- }
439
-
440
- /**
441
- * Redispatch leave event on host when catching leave event
442
- * on the incrementor and decrementor button.
443
- *
444
- * This redispatched leave event will be caught by
445
- * InteractionStateMixin to set "touched" state to true.
446
- *
447
- * Interacting with the buttons is "user interactions"
448
- * the same way as focusing + blurring the field (native input)
449
- *
450
- * @protected
451
- * @param {Event} ev
452
- */
453
- // eslint-disable-next-line no-unused-vars
454
- _onLeaveButton(ev) {
455
- const valueNode = /** @type {HTMLElement} */ (
456
- this.shadowRoot?.querySelector('.input-stepper__value')
457
- );
458
- valueNode.removeAttribute('aria-live');
459
- this.dispatchEvent(new Event(this._leaveEvent));
460
- }
461
457
  }