feat(progress-indicator): add support for progress bar (#1713)
Co-authored-by: gerjanvangeest <gerjanvangeest@users.noreply.github.com>
This commit is contained in:
parent
0b97918d1e
commit
a616fd8dca
8 changed files with 435 additions and 44 deletions
5
.changeset/sixty-donkeys-tell.md
Normal file
5
.changeset/sixty-donkeys-tell.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@lion/progress-indicator': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add option to make the progress-indicator determinate
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { css, html } from '@lion/core';
|
||||||
|
import { LionProgressIndicator } from '@lion/progress-indicator';
|
||||||
|
|
||||||
|
export class MyDeterminateProgressBar extends LionProgressIndicator {
|
||||||
|
static get styles() {
|
||||||
|
return [
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress__filled {
|
||||||
|
height: inherit;
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
_graphicTemplate() {
|
||||||
|
return html` <div class="progress__filled" style="width: ${this._progressPercentage}%"></div> `;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('my-determinate-progress-bar', MyDeterminateProgressBar);
|
||||||
|
|
@ -1,27 +1,22 @@
|
||||||
import { html, css } from '@lion/core';
|
import { html, css } from '@lion/core';
|
||||||
import { LionProgressIndicator } from '@lion/progress-indicator';
|
import { LionProgressIndicator } from '@lion/progress-indicator';
|
||||||
|
|
||||||
export class CustomProgressIndicator extends LionProgressIndicator {
|
export class MyIndeterminateProgressSpinner extends LionProgressIndicator {
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [
|
return [
|
||||||
css`
|
css`
|
||||||
:host {
|
.progress__icon {
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress--icon {
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
animation: spinner-rotate 2s linear infinite;
|
animation: spinner-rotate 2s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress--icon--circle {
|
.progress__filled {
|
||||||
animation: spinner-dash 1.35s ease-in-out infinite;
|
animation: spinner-dash 1.35s ease-in-out infinite;
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke-width: 6px;
|
stroke-width: 6px;
|
||||||
stroke: var(--primary-color);
|
stroke: var(--primary-color);
|
||||||
stroke-dasharray: 100, 28; /* This is a fallback for IE11 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spinner-rotate {
|
@keyframes spinner-rotate {
|
||||||
|
|
@ -50,11 +45,11 @@ export class CustomProgressIndicator extends LionProgressIndicator {
|
||||||
|
|
||||||
_graphicTemplate() {
|
_graphicTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<svg class="progress--icon" viewBox="20 20 47 47">
|
<svg class="progress__icon" viewBox="20 20 47 47">
|
||||||
<circle class="progress--icon--circle" cx="44" cy="44" r="20.2" />
|
<circle class="progress__filled" cx="44" cy="44" r="20.2" />
|
||||||
</svg>
|
</svg>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('custom-progress-indicator', CustomProgressIndicator);
|
customElements.define('my-indeterminate-progress-spinner', MyIndeterminateProgressSpinner);
|
||||||
|
|
@ -2,37 +2,87 @@
|
||||||
|
|
||||||
```js script
|
```js script
|
||||||
import { html } from '@mdjs/mdjs-preview';
|
import { html } from '@mdjs/mdjs-preview';
|
||||||
import './assets/custom-progress-indicator.js';
|
import '@lion/progress-indicator/define';
|
||||||
|
import './assets/my-indeterminate-progress-spinner.js';
|
||||||
|
import './assets/my-determinate-progress-bar.js';
|
||||||
|
|
||||||
|
const changeProgress = () => {
|
||||||
|
const progressBar = document.getElementsByName('my-bar')[0];
|
||||||
|
progressBar.value = Math.floor(Math.random() * 101);
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
## Extended indicator with a custom visual
|
## Styled progress bar example
|
||||||
|
|
||||||
`LionProgressIndicator` is designed to be extended to add visuals. Implement the `_graphicTemplate()` method to set the rendered content and apply styles normally.
|
Add custom styles and more features by extending the `LionProgressIndicator`.
|
||||||
|
|
||||||
### Example extension
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
class CustomProgressIndicator extends LionProgressIndicator {
|
export class MyDeterminateProgressBar extends LionProgressIndicator {
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [
|
return [
|
||||||
css`
|
css`
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress--icon {
|
.progress__filled {
|
||||||
|
height: inherit;
|
||||||
|
background-color: green;
|
||||||
|
border-radius: inherit;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
_graphicTemplate() {
|
||||||
|
return html`
|
||||||
|
<div class="progress__filled" style="width: \${this._progressPercentage}%"></div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
By given the progress-indicator a value it becomes determinate.
|
||||||
|
The min is automatically set to "0" and max to "100", but they can be set to your local needs.
|
||||||
|
|
||||||
|
```js preview-story
|
||||||
|
export const progressBarDemo = () =>
|
||||||
|
html`
|
||||||
|
<my-determinate-progress-bar
|
||||||
|
aria-label="Interest rate"
|
||||||
|
name="my-bar"
|
||||||
|
value="50"
|
||||||
|
></my-determinate-progress-bar>
|
||||||
|
<button @click="${changeProgress}">Randomize Value</button>
|
||||||
|
`;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Styled progress spinner example
|
||||||
|
|
||||||
|
`LionProgressIndicator` is designed to be extended to add visuals. Implement the `_graphicTemplate()` method to set the rendered content and apply styles normally.
|
||||||
|
|
||||||
|
```js
|
||||||
|
class MyIndeterminateProgressSpinner extends LionProgressIndicator {
|
||||||
|
static get styles() {
|
||||||
|
return [
|
||||||
|
css`
|
||||||
|
.progress__icon {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
animation: spinner-rotate 2s linear infinite;
|
animation: spinner-rotate 2s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress--icon--circle {
|
.progress__filled {
|
||||||
animation: spinner-dash 1.35s ease-in-out infinite;
|
animation: spinner-dash 1.35s ease-in-out infinite;
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke-width: 6px;
|
stroke-width: 6px;
|
||||||
stroke: var(--primary-color);
|
stroke: var(--primary-color);
|
||||||
stroke-dasharray: 100, 28; /* This is a fallback for IE11 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spinner-rotate {
|
@keyframes spinner-rotate {
|
||||||
|
|
@ -61,16 +111,15 @@ class CustomProgressIndicator extends LionProgressIndicator {
|
||||||
|
|
||||||
_graphicTemplate() {
|
_graphicTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<svg class="progress--icon" viewBox="20 20 47 47">
|
<svg class="progress__icon" viewBox="20 20 47 47">
|
||||||
<circle class="progress--icon--circle" cx="44" cy="44" r="20.2" />
|
<circle class="progress__filled" cx="44" cy="44" r="20.2" />
|
||||||
</svg>
|
</svg>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Result
|
|
||||||
|
|
||||||
```js preview-story
|
```js preview-story
|
||||||
export const main = () => html` <custom-progress-indicator></custom-progress-indicator> `;
|
export const main = () =>
|
||||||
|
html` <my-indeterminate-progress-spinner></my-indeterminate-progress-spinner> `;
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -2,19 +2,26 @@
|
||||||
|
|
||||||
A web component that implements accessibility requirements for progress indicators.
|
A web component that implements accessibility requirements for progress indicators.
|
||||||
|
|
||||||
```html
|
```js script
|
||||||
<lion-progress-indicator></lion-progress-indicator>
|
import { html } from '@mdjs/mdjs-preview';
|
||||||
|
import '@lion/progress-indicator/define';
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: You don't see a live demo here as it would be empty, since there is no styling. Check out the [examples](./examples.md) if you want to see a possible implementation.
|
```html
|
||||||
|
<lion-progress-indicator aria-label="Interest rate" value="50"></lion-progress-indicator>
|
||||||
|
```
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
This component is designed to be extended in order to add visuals.
|
This component is designed to be extended in order to add visuals.
|
||||||
|
|
||||||
|
- Can be indeterminate or determinate, depending on whether it has a value.
|
||||||
- Accessibility compliant
|
- Accessibility compliant
|
||||||
- Localized "Loading" label
|
- Localized "Loading" label in case of an indeterminate progress-indicator
|
||||||
- Implementation independent of visuals
|
- Implementation independent of visuals
|
||||||
|
- `value`: progress value, setting this makes the progress-indicator determinate.
|
||||||
|
- `min`: progress min value
|
||||||
|
- `max`: progress max value
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"lion",
|
"lion",
|
||||||
"loading-indicator",
|
"loading-indicator",
|
||||||
|
"progress-bar",
|
||||||
"progress-indicator",
|
"progress-indicator",
|
||||||
"spinner",
|
"spinner",
|
||||||
"web-components"
|
"web-components"
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,27 @@
|
||||||
/* eslint-disable class-methods-use-this, import/no-extraneous-dependencies */
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
|
import { LitElement, nothing } from '@lion/core';
|
||||||
import { nothing, LitElement } from '@lion/core';
|
|
||||||
import { localize, LocalizeMixin } from '@lion/localize';
|
import { localize, LocalizeMixin } from '@lion/localize';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('@lion/core').TemplateResult} TemplateResult
|
||||||
|
*/
|
||||||
export class LionProgressIndicator extends LocalizeMixin(LitElement) {
|
export class LionProgressIndicator extends LocalizeMixin(LitElement) {
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
value: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
min: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
max: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
_ariaLabel: { attribute: 'aria-label', type: String },
|
||||||
|
_ariaLabelledby: { attribute: 'aria-labelledby', type: String },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static get localizeNamespaces() {
|
static get localizeNamespaces() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|
@ -67,7 +85,41 @@ export class LionProgressIndicator extends LocalizeMixin(LitElement) {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @readonly
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
get indeterminate() {
|
||||||
|
return !this.hasAttribute('value');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In case of a determinate progress-indicator it returns the progress percentage
|
||||||
|
* based on value, min & max.
|
||||||
|
* Could be used for styling inside the _graphicTemplate
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* style="width: ${this._progressPercentage}%"
|
||||||
|
*/
|
||||||
|
get _progressPercentage() {
|
||||||
|
if (this.indeterminate) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return ((this.value - this.min) / (this.max - this.min)) * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.value = 0;
|
||||||
|
this.min = 0;
|
||||||
|
this.max = 100;
|
||||||
|
this._ariaLabel = '';
|
||||||
|
this._ariaLabelledby = '';
|
||||||
|
this.__hasDefaultLabelSet = false;
|
||||||
|
}
|
||||||
|
|
||||||
/** @protected */
|
/** @protected */
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
_graphicTemplate() {
|
_graphicTemplate() {
|
||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
@ -78,12 +130,76 @@ export class LionProgressIndicator extends LocalizeMixin(LitElement) {
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this.setAttribute('role', 'status');
|
this.setAttribute('role', 'progressbar');
|
||||||
this.setAttribute('aria-live', 'polite');
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update aria labels on state change.
|
||||||
|
* @param {import('@lion/core').PropertyValues } changedProperties
|
||||||
|
*/
|
||||||
|
updated(changedProperties) {
|
||||||
|
super.updated(changedProperties);
|
||||||
|
|
||||||
|
if (this.indeterminate) {
|
||||||
|
if (changedProperties.has('_ariaLabel') || changedProperties.has('_ariaLabelledby')) {
|
||||||
|
this._setDefaultLabel();
|
||||||
|
}
|
||||||
|
if (changedProperties.has('value')) {
|
||||||
|
this._resetAriaValueAttributes();
|
||||||
|
this._setDefaultLabel();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (changedProperties.has('value')) {
|
||||||
|
if (!this.value || typeof this.value !== 'number') {
|
||||||
|
this.removeAttribute('value');
|
||||||
|
} else if (this.value < this.min) {
|
||||||
|
this.value = this.min;
|
||||||
|
this.setAttribute('aria-valuenow', this.min.toString());
|
||||||
|
} else if (this.value > this.max) {
|
||||||
|
this.value = this.max;
|
||||||
|
this.setAttribute('aria-valuenow', this.max.toString());
|
||||||
|
} else {
|
||||||
|
this.setAttribute('aria-valuenow', this.value.toString());
|
||||||
|
}
|
||||||
|
if (this.__hasDefaultLabelSet === true) {
|
||||||
|
this.removeAttribute('aria-label');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changedProperties.has('min')) {
|
||||||
|
this.setAttribute('aria-valuemin', this.min.toString());
|
||||||
|
if (this.value < this.min) {
|
||||||
|
this.value = this.min;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changedProperties.has('max')) {
|
||||||
|
this.setAttribute('aria-valuemax', this.max.toString());
|
||||||
|
if (this.value > this.max) {
|
||||||
|
this.value = this.max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onLocaleUpdated() {
|
onLocaleUpdated() {
|
||||||
const label = localize.msg('lion-progress-indicator:loading');
|
super.onLocaleUpdated();
|
||||||
this.setAttribute('aria-label', label);
|
// only set default label for indeterminate
|
||||||
|
if (this.indeterminate) {
|
||||||
|
this._setDefaultLabel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_resetAriaValueAttributes() {
|
||||||
|
this.removeAttribute('aria-valuenow');
|
||||||
|
this.removeAttribute('aria-valuemin');
|
||||||
|
this.removeAttribute('aria-valuemax');
|
||||||
|
}
|
||||||
|
|
||||||
|
_setDefaultLabel() {
|
||||||
|
if (this._ariaLabelledby) {
|
||||||
|
this.removeAttribute('aria-label');
|
||||||
|
} else if (!this._ariaLabel) {
|
||||||
|
this.setAttribute('aria-label', localize.msg('lion-progress-indicator:loading'));
|
||||||
|
this.__hasDefaultLabelSet = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,211 @@
|
||||||
import { expect, fixture } from '@open-wc/testing';
|
|
||||||
import { html } from '@lion/core';
|
import { html } from '@lion/core';
|
||||||
|
import { expect, fixture as _fixture } from '@open-wc/testing';
|
||||||
|
|
||||||
import '@lion/progress-indicator/define';
|
import '@lion/progress-indicator/define';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('../src/LionProgressIndicator').LionProgressIndicator} LionProgressIndicator
|
||||||
|
* @typedef {import('@lion/core').TemplateResult} TemplateResult
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fixture = /** @type {(arg: TemplateResult) => Promise<LionProgressIndicator>} */ (_fixture);
|
||||||
|
|
||||||
describe('lion-progress-indicator', () => {
|
describe('lion-progress-indicator', () => {
|
||||||
describe('Accessibility', () => {
|
describe('indeterminate', async () => {
|
||||||
it('adds a label', async () => {
|
it('is indeterminate when has no value attribute', async () => {
|
||||||
const el = await fixture(html` <lion-progress-indicator></lion-progress-indicator> `);
|
const el = await fixture(html` <lion-progress-indicator></lion-progress-indicator> `);
|
||||||
|
expect(el.indeterminate).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds a label by default', async () => {
|
||||||
|
const el = await fixture(html` <lion-progress-indicator></lion-progress-indicator> `);
|
||||||
|
await el.localizeNamespacesLoaded;
|
||||||
expect(el.getAttribute('aria-label')).to.equal('Loading');
|
expect(el.getAttribute('aria-label')).to.equal('Loading');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets the right role', async () => {
|
it('can override a label with "aria-label"', async () => {
|
||||||
const el = await fixture(html` <lion-progress-indicator></lion-progress-indicator> `);
|
const el = await fixture(
|
||||||
expect(el.getAttribute('role')).to.equal('status');
|
html` <lion-progress-indicator aria-label="foo"></lion-progress-indicator> `,
|
||||||
|
);
|
||||||
|
await el.localizeNamespacesLoaded;
|
||||||
|
expect(el.getAttribute('aria-label')).to.equal('foo');
|
||||||
|
el.setAttribute('aria-label', 'bar');
|
||||||
|
expect(el.getAttribute('aria-label')).to.equal('bar');
|
||||||
|
el.removeAttribute('aria-label');
|
||||||
|
await el.updateComplete;
|
||||||
|
expect(el.getAttribute('aria-label')).to.equal('Loading');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets aria-live to "polite"', async () => {
|
it('can override a label with "aria-labelledby"', async () => {
|
||||||
|
const el = await fixture(
|
||||||
|
html` <lion-progress-indicator aria-labelledby="foo-id"></lion-progress-indicator> `,
|
||||||
|
);
|
||||||
|
await el.localizeNamespacesLoaded;
|
||||||
|
expect(el.getAttribute('aria-labelledby')).to.equal('foo-id');
|
||||||
|
expect(el.hasAttribute('aria-label')).to.be.false;
|
||||||
|
el.setAttribute('aria-labelledby', 'bar-id');
|
||||||
|
expect(el.getAttribute('aria-labelledby')).to.equal('bar-id');
|
||||||
|
expect(el.hasAttribute('aria-label')).to.be.false;
|
||||||
|
el.removeAttribute('aria-labelledby');
|
||||||
|
await el.updateComplete;
|
||||||
|
expect(el.hasAttribute('aria-labelledby')).to.be.false;
|
||||||
|
expect(el.getAttribute('aria-label')).to.equal('Loading');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loosses default aria-label when switch to determinate state', async () => {
|
||||||
const el = await fixture(html` <lion-progress-indicator></lion-progress-indicator> `);
|
const el = await fixture(html` <lion-progress-indicator></lion-progress-indicator> `);
|
||||||
expect(el.getAttribute('aria-live')).to.equal('polite');
|
await el.localizeNamespacesLoaded;
|
||||||
|
expect(el.getAttribute('aria-label')).to.equal('Loading');
|
||||||
|
el.setAttribute('value', '30');
|
||||||
|
await el.updateComplete;
|
||||||
|
expect(el.hasAttribute('aria-label')).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps own aria-label when switch to determinate state', async () => {
|
||||||
|
const el = await fixture(
|
||||||
|
html` <lion-progress-indicator aria-label="foo"></lion-progress-indicator> `,
|
||||||
|
);
|
||||||
|
expect(el.getAttribute('aria-label')).to.equal('foo');
|
||||||
|
el.setAttribute('value', '30');
|
||||||
|
await el.updateComplete;
|
||||||
|
expect(el.getAttribute('aria-label')).to.equal('foo');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('determinate', async () => {
|
||||||
|
it('is determinate when it has a value', async () => {
|
||||||
|
const el = await fixture(
|
||||||
|
html` <lion-progress-indicator value="25" aria-label="foo"></lion-progress-indicator> `,
|
||||||
|
);
|
||||||
|
expect(el.indeterminate).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can update value', async () => {
|
||||||
|
const el = await fixture(
|
||||||
|
html` <lion-progress-indicator value="25" aria-label="foo"></lion-progress-indicator> `,
|
||||||
|
);
|
||||||
|
expect(el.getAttribute('aria-valuenow')).to.equal('25');
|
||||||
|
el.value = 30;
|
||||||
|
await el.updateComplete;
|
||||||
|
expect(el.getAttribute('aria-valuenow')).to.equal('30');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can update min', async () => {
|
||||||
|
const el = await fixture(
|
||||||
|
html` <lion-progress-indicator value="50" aria-label="foo"></lion-progress-indicator> `,
|
||||||
|
);
|
||||||
|
expect(el.getAttribute('aria-valuemin')).to.equal('0');
|
||||||
|
el.min = 30;
|
||||||
|
await el.updateComplete;
|
||||||
|
expect(el.getAttribute('aria-valuemin')).to.equal('30');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can update max', async () => {
|
||||||
|
const el = await fixture(
|
||||||
|
html` <lion-progress-indicator value="50" aria-label="foo"></lion-progress-indicator> `,
|
||||||
|
);
|
||||||
|
expect(el.getAttribute('aria-valuemax')).to.equal('100');
|
||||||
|
el.max = 70;
|
||||||
|
await el.updateComplete;
|
||||||
|
expect(el.getAttribute('aria-valuemax')).to.equal('70');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('min & max limits value', async () => {
|
||||||
|
const el = await fixture(
|
||||||
|
html` <lion-progress-indicator value="150" aria-label="foo"></lion-progress-indicator> `,
|
||||||
|
);
|
||||||
|
// sets to default max: 100
|
||||||
|
expect(el.getAttribute('aria-valuenow')).to.equal('100');
|
||||||
|
el.value = -20;
|
||||||
|
await el.updateComplete;
|
||||||
|
// sets to default min: 0
|
||||||
|
expect(el.getAttribute('aria-valuenow')).to.equal('0');
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO make this feature available
|
||||||
|
it.skip('supports valuetext', async () => {
|
||||||
|
const el = await fixture(
|
||||||
|
html`
|
||||||
|
<lion-progress-indicator
|
||||||
|
value="8"
|
||||||
|
value-text="{value}% (34 minutes) remaining"
|
||||||
|
></lion-progress-indicator>
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
expect(el.getAttribute('aria-valuetext')).to.equal('8% (34 minutes) remaining');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('becomes indeterminate if value gets removed', async () => {
|
||||||
|
const el = await fixture(
|
||||||
|
html`<lion-progress-indicator value="30"></lion-progress-indicator> `,
|
||||||
|
);
|
||||||
|
el.removeAttribute('value');
|
||||||
|
await el.updateComplete;
|
||||||
|
expect(el.indeterminate).to.be.true;
|
||||||
|
expect(el.getAttribute('aria-label')).to.equal('Loading');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("becomes indeterminate if value ain't a number", async () => {
|
||||||
|
const el = await fixture(
|
||||||
|
html`<lion-progress-indicator value="30"></lion-progress-indicator> `,
|
||||||
|
);
|
||||||
|
el.setAttribute('value', '');
|
||||||
|
await el.updateComplete;
|
||||||
|
expect(el.indeterminate).to.be.true;
|
||||||
|
await el.updateComplete;
|
||||||
|
expect(el.hasAttribute('aria-valuenow')).to.be.false;
|
||||||
|
expect(el.hasAttribute('aria-valuemin')).to.be.false;
|
||||||
|
expect(el.hasAttribute('aria-valuemax')).to.be.false;
|
||||||
|
expect(el.getAttribute('aria-label')).to.equal('Loading');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Subclasers', () => {
|
||||||
|
it('can use _progressPercentage getter to get the progress percentage', async () => {
|
||||||
|
const el = await fixture(
|
||||||
|
html`
|
||||||
|
<lion-progress-indicator max="50" value="10" aria-label="foo"></lion-progress-indicator>
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
expect(el._progressPercentage).to.equal(20);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Accessibility', () => {
|
||||||
|
it('by default', async () => {
|
||||||
|
const el = await fixture(html` <lion-progress-indicator></lion-progress-indicator> `);
|
||||||
|
expect(el.getAttribute('role')).to.equal('progressbar');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('indeterminate', () => {
|
||||||
|
it('passes a11y test', async () => {
|
||||||
|
const el = await fixture(html` <lion-progress-indicator></lion-progress-indicator> `);
|
||||||
|
await expect(el).to.be.accessible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('determinate', () => {
|
||||||
|
it('passes a11y test', async () => {
|
||||||
|
const el = await fixture(
|
||||||
|
html` <lion-progress-indicator value="25" aria-label="foo"></lion-progress-indicator> `,
|
||||||
|
);
|
||||||
|
await expect(el).to.be.accessible();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('once value is set', async () => {
|
||||||
|
const el = await fixture(
|
||||||
|
html` <lion-progress-indicator value="25" aria-label="foo"></lion-progress-indicator> `,
|
||||||
|
);
|
||||||
|
expect(el.getAttribute('aria-valuenow')).to.equal('25');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows to set min & max values', async () => {
|
||||||
|
const el = await fixture(
|
||||||
|
html` <lion-progress-indicator value="25" aria-label="foo"></lion-progress-indicator> `,
|
||||||
|
);
|
||||||
|
expect(el.getAttribute('aria-valuemin')).to.equal('0');
|
||||||
|
expect(el.getAttribute('aria-valuemax')).to.equal('100');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue