feat: add loading indicator (#857)

feat: add loading indicator
This commit is contained in:
Goffert van Gool 2020-08-06 13:35:26 +02:00 committed by GitHub
parent 9ecab4d5b2
commit c224baa801
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 360 additions and 0 deletions

View file

@ -0,0 +1,5 @@
---
'@lion/progress-indicator': minor
---
Initial release

View file

@ -0,0 +1,90 @@
[//]: # 'AUTO INSERT HEADER PREPUBLISH'
# Progress Indicator
`lion-progress-indicator` implements accessibility requirements for progress indicators.
```js script
import { html } from 'lit-html';
import './demo/custom-progress-indicator.js';
export default {
title: 'Others/Progress Indicator',
};
```
## Features
- Accessibility compliant
- Localized "Loading" label
- Implementation independent of visuals
## How to use
### Installation
```bash
npm i --save @lion/progress-indicator
```
```js
import { LionProgressIndicator } from '@lion/progress-indicator';
// or
import '@lion/progress-indicator/lion-progress-indicator.js';
```
### Example
```html
<lion-progress-indicator></lion-progress-indicator>
```
## Extended indicator with a custom visual
`LionProgressIndicator` is designed to be extended to add visuals. Implement the `_graphicTemplate` method to set the rendered content, and apply styles normally.
### Example extension
```js
class CustomProgressIndicator extends LionProgressIndicator {
static get styles() {
return [
css`
svg {
animation: spinner-rotate 2s linear infinite;
display: inline-block;
height: 48px;
width: 48px;
}
circle {
fill: none;
stroke-width: 3.6;
stroke: firebrick;
stroke-dasharray: 100, 28;
}
@keyframes spinner-rotate {
to {
transform: rotate(360deg);
}
}
`,
];
}
_graphicTemplate() {
return html`<svg viewBox="22 22 44 44">
<circle cx="44" cy="44" r="20.2" />
</svg>`;
}
}
```
### Result
```js preview-story
export const customProgressDemo = () => html`
<custom-progress-indicator></custom-progress-indicator>
`;
```

View file

@ -0,0 +1,56 @@
import { svg, css } from '@lion/core';
import { LionProgressIndicator } from '../index.js';
export class CustomProgressIndicator extends LionProgressIndicator {
static get styles() {
return [
css`
:host {
display: inline-block;
}
svg {
animation: spinner-rotate 2s linear infinite;
display: inline-block;
height: 48px;
width: 48px;
}
circle {
animation: spinner-dash 1.35s ease-in-out infinite;
fill: none;
stroke-width: 3.6;
stroke: firebrick;
stroke-dasharray: 100, 28; /* This is a fallback for IE11 */
}
@keyframes spinner-rotate {
to {
transform: rotate(360deg);
}
}
@keyframes spinner-dash {
0% {
stroke-dasharray: 6, 122;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 100, 28;
stroke-dashoffset: -16;
}
100% {
stroke-dasharray: 6, 122;
stroke-dashoffset: -127;
}
}
`,
];
}
_graphicTemplate() {
return svg`<svg viewBox="22 22 44 44">
<circle cx="44" cy="44" r="20.2" />
</svg>`;
}
}

View file

@ -0,0 +1,3 @@
import { CustomProgressIndicator } from './CustomProgressIndicator.js';
customElements.define('custom-progress-indicator', CustomProgressIndicator);

View file

@ -0,0 +1 @@
export { LionProgressIndicator } from './src/LionProgressIndicator.js';

View file

@ -0,0 +1,3 @@
import { LionProgressIndicator } from './src/LionProgressIndicator.js';
customElements.define('lion-progress-indicator', LionProgressIndicator);

View file

@ -0,0 +1,46 @@
{
"name": "@lion/progress-indicator",
"version": "0.0.0",
"description": "A progress indicator that is easily styleable and accessible in all contexts",
"license": "MIT",
"author": "ing-bank",
"homepage": "https://github.com/ing-bank/lion/",
"repository": {
"type": "git",
"url": "https://github.com/ing-bank/lion.git",
"directory": "packages/progress-indicator"
},
"main": "index.js",
"module": "index.js",
"files": [
"*.d.ts",
"*.js",
"src",
"test",
"translations",
"types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
"start": "cd ../../ && yarn dev-server --open packages/progress-indicator/README.md",
"test": "cd ../../ && yarn test:browser --grep \"packages/progress-indicator/test/**/*.test.js\"",
"test:watch": "cd ../../ && yarn test:browser:watch --grep \"packages/progress-indicator/test/**/*.test.js\""
},
"sideEffects": [
"lion-progress-indicator.js"
],
"dependencies": {
"@lion/core": "0.8.0",
"@lion/localize": "0.13.1"
},
"keywords": [
"lion",
"loading-indicator",
"progress-indicator",
"spinner",
"web-components"
],
"publishConfig": {
"access": "public"
}
}

View file

@ -0,0 +1,88 @@
/* eslint-disable class-methods-use-this */
import { nothing, LitElement } from '@lion/core';
import { localize, LocalizeMixin } from '@lion/localize';
export class LionProgressIndicator extends LocalizeMixin(LitElement) {
static get localizeNamespaces() {
return [
{
'lion-progress-indicator': locale => {
switch (locale) {
case 'bg-BG':
case 'bg':
return import('../translations/bg.js');
case 'cs-CZ':
case 'cs':
return import('../translations/cs.js');
case 'de-DE':
case 'de':
return import('../translations/de.js');
case 'en-AU':
case 'en-GB':
case 'en-US':
case 'en-PH':
case 'en':
return import('../translations/en.js');
case 'es-ES':
case 'es':
return import('../translations/es.js');
case 'fr-BE':
case 'fr-FR':
case 'fr':
return import('../translations/fr.js');
case 'hu-HU':
case 'hu':
return import('../translations/hu.js');
case 'it-IT':
case 'it':
return import('../translations/it.js');
case 'nl-BE':
case 'nl-NL':
case 'nl':
return import('../translations/nl.js');
case 'pl-PL':
case 'pl':
return import('../translations/pl.js');
case 'ro-RO':
case 'ro':
return import('../translations/ro.js');
case 'ru-RU':
case 'ru':
return import('../translations/ru.js');
case 'sk-SK':
case 'sk':
return import('./translations/sk.js');
case 'uk-UA':
case 'uk':
return import('../translations/uk.js');
case 'zh-CN':
case 'zh':
return import('../translations/zh.js');
default:
return import('../translations/en.js');
}
},
},
];
}
_graphicTemplate() {
return nothing;
}
render() {
return this._graphicTemplate();
}
connectedCallback() {
super.connectedCallback();
this.setAttribute('role', 'status');
this.setAttribute('aria-live', 'polite');
}
onLocaleUpdated() {
const label = localize.msg('lion-progress-indicator:loading');
this.setAttribute('aria-label', label);
}
}

View file

@ -0,0 +1,23 @@
import { expect, fixture } from '@open-wc/testing';
import { html } from '@lion/core';
import '../lion-progress-indicator.js';
describe('lion-progress-indicator', () => {
describe('Accessibility', () => {
it('adds a label', async () => {
const el = await fixture(html` <lion-progress-indicator></lion-progress-indicator> `);
expect(el.getAttribute('aria-label')).to.equal('Loading');
});
it('sets the right role', async () => {
const el = await fixture(html` <lion-progress-indicator></lion-progress-indicator> `);
expect(el.getAttribute('role')).to.equal('status');
});
it('sets aria-live to "polite"', async () => {
const el = await fixture(html` <lion-progress-indicator></lion-progress-indicator> `);
expect(el.getAttribute('aria-live')).to.equal('polite');
});
});
});

View file

@ -0,0 +1,3 @@
export default {
loading: 'Зареждане',
};

View file

@ -0,0 +1,3 @@
export default {
loading: 'Načítání',
};

View file

@ -0,0 +1,3 @@
export default {
loading: 'Wird geladen',
};

View file

@ -0,0 +1,3 @@
export default {
loading: 'Loading',
};

View file

@ -0,0 +1,3 @@
export default {
loading: 'Cargando',
};

View file

@ -0,0 +1,3 @@
export default {
loading: 'Chargement',
};

View file

@ -0,0 +1,3 @@
export default {
loading: 'Betöltés',
};

View file

@ -0,0 +1,3 @@
export default {
loading: 'Caricamento in corso',
};

View file

@ -0,0 +1,3 @@
export default {
loading: 'Laden',
};

View file

@ -0,0 +1,3 @@
export default {
loading: 'Ładuję',
};

View file

@ -0,0 +1,3 @@
export default {
loading: 'Se incarca',
};

View file

@ -0,0 +1,3 @@
export default {
loading: 'погрузка',
};

View file

@ -0,0 +1,3 @@
export default {
loading: 'погрузка',
};

View file

@ -0,0 +1,3 @@
export default {
loading: 'завантаження',
};

View file

@ -0,0 +1,3 @@
export default {
loading: '载入中',
};