Skip to content

Commit

Permalink
feat(input-stepper): a11y enhancement & added translations (#2173)
Browse files Browse the repository at this point in the history
  • Loading branch information
gerjanvangeest authored Feb 19, 2024
1 parent 19256d3 commit 87005af
Show file tree
Hide file tree
Showing 40 changed files with 382 additions and 69 deletions.
5 changes: 5 additions & 0 deletions .changeset/pink-islands-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@lion/ui': minor
---

[input-stepper] a11y enhancement & added translations
14 changes: 7 additions & 7 deletions docs/components/input-stepper/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ A web component that enables the user to increase and decrease a numeric value b

```js script
import { html } from '@mdjs/mdjs-preview';
import { loadDefaultFeedbackMessages } from '@lion/ui/validate-messages.js';
import '@lion/ui/define/lion-input-stepper.js';
loadDefaultFeedbackMessages();
```

```js preview-story
export const main = () => html`
<lion-input-stepper max="5" min="0" name="count">
<label slot="label">RSVP</label>
<div slot="help-text">Max. 5 guests</div>
</lion-input-stepper>
`;
```html preview-story
<lion-input-stepper max="5" min="0" name="count">
<label slot="label">RSVP</label>
<div slot="help-text">Max. 5 guests</div>
</lion-input-stepper>
```

## Features
Expand Down
51 changes: 30 additions & 21 deletions docs/components/input-stepper/use-cases.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,49 @@

```js script
import { html } from '@mdjs/mdjs-preview';
import { loadDefaultFeedbackMessages } from '@lion/ui/validate-messages.js';
import '@lion/ui/define/lion-input-stepper.js';
loadDefaultFeedbackMessages();
```

## Default with no specification
## Default

When no range or step is defined, it can go infinite with default step value as `1`. You can also specify prefix content using `after` slot.

```js preview-story
export const defaultMode = () => html`
<label>How old is the existence?</label>
<lion-input-stepper name="year">
<div slot="after">In Billion Years</div>
</lion-input-stepper>
`;
```html preview-story
<lion-input-stepper name="year">
<label slot="label">How old is the existence?</label>
<div slot="after" data-description>In Billion Years</div>
</lion-input-stepper>
```

## Step and Value
## Attributes & Properties

### Step and Value

Use `step` attribute to specify the incrementor or decrementor difference and `value` to set the default value.

```js preview-story
export const steps = () => html`
<p><strong>Min:</strong> 100, <strong>Value:</strong> 200, <strong>Step:</strong> 100</p>
<lion-input-stepper min="100" step="100" name="value" value="200"></lion-input-stepper>
`;
```html preview-story
<lion-input-stepper
label="Amount of oranges"
min="100"
step="100"
name="value"
value="200"
></lion-input-stepper>
```

## Range
### Range

Use `min` and `max` attribute to specify range.
Use `min` and `max` attribute to specify a range.

```js preview-story
export const range = () => html`
<p><strong>Min:</strong> 200, <strong>Max:</strong> 500, <strong>Step:</strong> 100</p>
<lion-input-stepper min="200" max="500" name="value" step="100" value="200"></lion-input-stepper>
`;
```html preview-story
<lion-input-stepper
label="Amount of oranges"
min="200"
max="500"
name="value"
step="100"
value="200"
></lion-input-stepper>
```
86 changes: 53 additions & 33 deletions packages/ui/components/input-stepper/src/LionInputStepper.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { html, css, render } from 'lit';
import { LocalizeMixin } from '@lion/ui/localize-no-side-effects.js';
import { LionInput } from '@lion/ui/input.js';
import { IsNumber, MinNumber, MaxNumber } from '@lion/ui/form-core.js';
import { localizeNamespaceLoader } from './localizeNamespaceLoader.js';

/**
* @typedef {import('lit').RenderOptions} RenderOptions
Expand All @@ -11,7 +13,7 @@ import { IsNumber, MinNumber, MaxNumber } from '@lion/ui/form-core.js';
*
* @customElement lion-input-stepper
*/
export class LionInputStepper extends LionInput {
export class LionInputStepper extends LocalizeMixin(LionInput) {
static get styles() {
return [
...super.styles,
Expand Down Expand Up @@ -41,6 +43,11 @@ export class LionInputStepper extends LionInput {
};
}

static localizeNamespaces = [
{ 'lion-input-stepper': localizeNamespaceLoader },
...super.localizeNamespaces,
];

/**
* @returns {number}
*/
Expand Down Expand Up @@ -82,9 +89,7 @@ export class LionInputStepper extends LionInput {
this.addEventListener('keydown', this.__keyDownHandler);
this._inputNode.setAttribute('inputmode', 'decimal');
this._inputNode.setAttribute('autocomplete', 'off');
this.setAttribute('aria-label', this.label);
this.step = this.hasAttribute('step') ? this.step : 1;
this.__setAriaLabelsAndValidator();
this.__setDefaultValidators();
this.__toggleSpinnerButtonsState();
}

Expand Down Expand Up @@ -117,6 +122,14 @@ export class LionInputStepper extends LionInput {
this._inputNode.step = `${this.step}`;
this.values.step = this.step;
}

if (changedProperties.has('_ariaLabelledNodes')) {
this.__reflectAriaAttrToSpinButton('aria-labelledby', this._ariaLabelledNodes);
}

if (changedProperties.has('_ariaDescribedNodes')) {
this.__reflectAriaAttrToSpinButton('aria-describedby', this._ariaDescribedNodes);
}
}

get slots() {
Expand All @@ -128,41 +141,47 @@ export class LionInputStepper extends LionInput {
}

/**
* Set aria labels and apply validators
* Based on FormControlMixin __reflectAriaAttr()
*
* Will handle help text, validation feedback and character counter,
* prefix/suffix/before/after (if they contain data-description flag attr).
* Also, contents of id references that will be put in the <lion-field>._ariaDescribedby property
* from an external context, will be read by a screen reader.
* @param {string} attrName
* @param {Element[]} nodes
* @private
*/
__setAriaLabelsAndValidator() {
const ariaAttributes = {
'aria-valuemax': this.values.max,
'aria-valuemin': this.values.min,
};
__reflectAriaAttrToSpinButton(attrName, nodes) {
const string = nodes.map(n => n.id).join(' ');
this.setAttribute(attrName, string);
}

const minMaxValidators = /** @type {(MaxNumber | MinNumber)[]} */ (
Object.entries(ariaAttributes)
.map(([key, val]) => {
if (val !== Infinity) {
this.setAttribute(key, `${val}`);
return key === 'aria-valuemax' ? new MaxNumber(val) : new MinNumber(val);
}
return null;
})
.filter(validator => validator !== null)
/**
* Set aria labels and apply validators
* @private
*/
__setDefaultValidators() {
const validators = /** @type {(IsNumber| MaxNumber | MinNumber)[]} */ (
[
new IsNumber(),
this.min !== Infinity ? new MinNumber(this.min) : null,
this.max !== Infinity ? new MaxNumber(this.max) : null,
].filter(validator => validator !== null)
);
const validators = [new IsNumber(), ...minMaxValidators];
this.defaultValidators.push(...validators);
}

/**
* Update values on keyboard arrow up and down event
* @param {KeyboardEvent} e - keyboard event
* @param {KeyboardEvent} ev - keyboard event
* @private
*/
__keyDownHandler(e) {
if (e.key === 'ArrowUp') {
__keyDownHandler(ev) {
if (ev.key === 'ArrowUp') {
this.__increment();
}

if (e.key === 'ArrowDown') {
if (ev.key === 'ArrowDown') {
this.__decrement();
}
}
Expand All @@ -177,6 +196,9 @@ export class LionInputStepper extends LionInput {
const incrementButton = this.__getSlot('suffix');
const disableIncrementor = this.currentValue >= max && max !== Infinity;
const disableDecrementor = this.currentValue <= min && min !== Infinity;
if (disableDecrementor || disableIncrementor) {
this._inputNode.focus();
}
decrementButton[disableDecrementor ? 'setAttribute' : 'removeAttribute']('disabled', 'true');
incrementButton[disableIncrementor ? 'setAttribute' : 'removeAttribute']('disabled', 'true');
this.setAttribute('aria-valuenow', `${this.currentValue}`);
Expand All @@ -201,10 +223,10 @@ export class LionInputStepper extends LionInput {
* @private
*/
__increment() {
const { step, max } = this.values;
const { step, min, max } = this.values;
const newValue = this.currentValue + step;
if (newValue <= max || max === Infinity) {
this.value = `${newValue}`;
this.value = newValue < min && min !== Infinity ? `${min}` : `${newValue}`;
this.__toggleSpinnerButtonsState();
this._proxyInputEvent();
}
Expand All @@ -215,10 +237,10 @@ export class LionInputStepper extends LionInput {
* @private
*/
__decrement() {
const { step, min } = this.values;
const { step, min, max } = this.values;
const newValue = this.currentValue - step;
if (newValue >= min || min === Infinity) {
this.value = `${newValue}`;
this.value = newValue > max && max !== Infinity ? `${max}` : `${newValue}`;
this.__toggleSpinnerButtonsState();
this._proxyInputEvent();
}
Expand Down Expand Up @@ -301,9 +323,8 @@ export class LionInputStepper extends LionInput {
?disabled=${this.disabled || this.readOnly}
@click=${this.__decrement}
@blur=${this.__boundOnLeaveButton}
tabindex="-1"
type="button"
aria-label="decrement"
aria-label="${this.msgLit('lion-input-stepper:decrease')}"
>
${this._decrementorSignTemplate()}
</button>
Expand All @@ -321,9 +342,8 @@ export class LionInputStepper extends LionInput {
?disabled=${this.disabled || this.readOnly}
@click=${this.__increment}
@blur=${this.__boundOnLeaveButton}
tabindex="-1"
type="button"
aria-label="increment"
aria-label="${this.msgLit('lion-input-stepper:increase')}"
>
${this._incrementorSignTemplate()}
</button>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* eslint-disable import/no-extraneous-dependencies */
export const localizeNamespaceLoader = /** @param {string} locale */ locale => {
switch (locale) {
case 'bg-BG':
return import('@lion/ui/input-stepper-translations/bg-BG.js');
case 'bg':
return import('@lion/ui/input-stepper-translations/bg.js');
case 'cs-CZ':
return import('@lion/ui/input-stepper-translations/cs-CZ.js');
case 'cs':
return import('@lion/ui/input-stepper-translations/cs.js');
case 'de-DE':
return import('@lion/ui/input-stepper-translations/de-DE.js');
case 'de':
return import('@lion/ui/input-stepper-translations/de.js');
case 'en-AU':
return import('@lion/ui/input-stepper-translations/en-AU.js');
case 'en-GB':
return import('@lion/ui/input-stepper-translations/en-GB.js');
case 'en-US':
return import('@lion/ui/input-stepper-translations/en-US.js');
case 'en-PH':
case 'en':
return import('@lion/ui/input-stepper-translations/en.js');
case 'es-ES':
return import('@lion/ui/input-stepper-translations/es-ES.js');
case 'es':
return import('@lion/ui/input-stepper-translations/es.js');
case 'fr-FR':
return import('@lion/ui/input-stepper-translations/fr-FR.js');
case 'fr-BE':
return import('@lion/ui/input-stepper-translations/fr-BE.js');
case 'fr':
return import('@lion/ui/input-stepper-translations/fr.js');
case 'hu-HU':
return import('@lion/ui/input-stepper-translations/hu-HU.js');
case 'hu':
return import('@lion/ui/input-stepper-translations/hu.js');
case 'it-IT':
return import('@lion/ui/input-stepper-translations/it-IT.js');
case 'it':
return import('@lion/ui/input-stepper-translations/it.js');
case 'nl-BE':
return import('@lion/ui/input-stepper-translations/nl-BE.js');
case 'nl-NL':
return import('@lion/ui/input-stepper-translations/nl-NL.js');
case 'nl':
return import('@lion/ui/input-stepper-translations/nl.js');
case 'pl-PL':
return import('@lion/ui/input-stepper-translations/pl-PL.js');
case 'pl':
return import('@lion/ui/input-stepper-translations/pl.js');
case 'ro-RO':
return import('@lion/ui/input-stepper-translations/ro-RO.js');
case 'ro':
return import('@lion/ui/input-stepper-translations/ro.js');
case 'ru-RU':
return import('@lion/ui/input-stepper-translations/ru-RU.js');
case 'ru':
return import('@lion/ui/input-stepper-translations/ru.js');
case 'sk-SK':
return import('@lion/ui/input-stepper-translations/sk-SK.js');
case 'sk':
return import('@lion/ui/input-stepper-translations/sk.js');
case 'uk-UA':
return import('@lion/ui/input-stepper-translations/uk-UA.js');
case 'uk':
return import('@lion/ui/input-stepper-translations/uk.js');
case 'zh-CN':
case 'zh':
return import('@lion/ui/input-stepper-translations/zh.js');
default:
return import('@lion/ui/input-stepper-translations/en.js');
}
};
Loading

0 comments on commit 87005af

Please sign in to comment.