Skip to content

Commit 8e43602

Browse files
committed
feat(switch): component to toggle a property on or off
1 parent 8223b4a commit 8e43602

File tree

12 files changed

+523
-1
lines changed

12 files changed

+523
-1
lines changed

packages/switch/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Switch
2+
3+
[//]: # 'AUTO INSERT HEADER PREPUBLISH'
4+
5+
`lion-input-switch` is a component that is used to toggle a property or feature on or off.
6+
7+
## Features
8+
9+
- Get or set the checked state (boolean) - `choiceChecked()`
10+
- Get or set the value of the choice - `choiceValue()`
11+
- Pre-select an option by setting the `checked` boolean attribute
12+
13+
## How to use
14+
15+
### Installation
16+
17+
```sh
18+
npm i --save @lion/switch
19+
```
20+
21+
```js
22+
import '@lion/swith/lion-input-switch.js';
23+
```
24+
25+
### Example
26+
27+
```html
28+
<lion-input-switch name="airplaneMode" label="Airplane mode" checked></lion-input-switch>
29+
```

packages/switch/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { LionInputSwitch } from './src/LionInputSwitch.js';
2+
export { LionButtonSwitch } from './src/LionButtonSwitch.js';
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { LionButtonSwitch } from './src/LionButtonSwitch.js';
2+
3+
customElements.define('lion-button-switch', LionButtonSwitch);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { LionInputSwitch } from './src/LionInputSwitch.js';
2+
3+
customElements.define('lion-input-switch', LionInputSwitch);

packages/switch/package.json

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"name": "@lion/switch",
3+
"version": "0.0.1",
4+
"description": "A Switch is used for switching a property or feature on and off",
5+
"author": "ing-bank",
6+
"homepage": "https://github.com/ing-bank/lion/",
7+
"license": "MIT",
8+
"publishConfig": {
9+
"access": "public"
10+
},
11+
"repository": {
12+
"type": "git",
13+
"url": "https://github.com/ing-bank/lion.git",
14+
"directory": "packages/switch"
15+
},
16+
"scripts": {
17+
"prepublishOnly": "../../scripts/npm-prepublish.js"
18+
},
19+
"keywords": [
20+
"lion",
21+
"web-components",
22+
"switch"
23+
],
24+
"main": "index.js",
25+
"module": "index.js",
26+
"files": [
27+
"docs",
28+
"src",
29+
"stories",
30+
"test",
31+
"translations",
32+
"*.js"
33+
],
34+
"dependencies": {
35+
"@lion/button": "^0.3.29",
36+
"@lion/choice-input": "^0.2.39",
37+
"@lion/core": "^0.2.1",
38+
"@lion/field": "^0.3.3"
39+
},
40+
"devDependencies": {
41+
"@lion/form": "^0.1.68",
42+
"@lion/localize": "^0.4.20",
43+
"@lion/validate": "^0.2.37",
44+
"@open-wc/demoing-storybook": "^0.2.0",
45+
"@open-wc/testing": "^2.3.4",
46+
"sinon": "^7.2.2"
47+
}
48+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { LionButton } from '@lion/button';
2+
import { html, css } from '@lion/core';
3+
4+
export class LionButtonSwitch extends LionButton {
5+
static get properties() {
6+
return {
7+
checked: {
8+
type: Boolean,
9+
reflect: true,
10+
},
11+
};
12+
}
13+
14+
static get styles() {
15+
return [
16+
...super.styles,
17+
css`
18+
:host {
19+
display: inline-block;
20+
position: relative;
21+
width: 36px;
22+
height: 16px;
23+
/* Override "button" styles */
24+
min-height: auto;
25+
padding: 0;
26+
}
27+
28+
.btn {
29+
height: 100%;
30+
/* Override "button" styles */
31+
min-height: auto;
32+
padding: 0;
33+
}
34+
35+
.button-switch__track {
36+
background: #eee;
37+
width: 100%;
38+
height: 100%;
39+
}
40+
41+
.button-switch__thumb {
42+
background: #ccc;
43+
width: 50%;
44+
height: 100%;
45+
position: absolute;
46+
top: 0;
47+
}
48+
49+
:host([checked]) .button-switch__thumb {
50+
right: 0;
51+
}
52+
`,
53+
];
54+
}
55+
56+
// eslint-disable-next-line class-methods-use-this
57+
_renderBefore() {
58+
return html`
59+
<div class="button-switch__track"></div>
60+
<div class="button-switch__thumb"></div>
61+
`;
62+
}
63+
64+
constructor() {
65+
super();
66+
this.role = 'switch';
67+
this.checked = false;
68+
this.addEventListener('click', this.__handleToggleStateChange);
69+
}
70+
71+
connectedCallback() {
72+
super.connectedCallback();
73+
this.setAttribute('aria-checked', `${this.checked}`);
74+
}
75+
76+
firstUpdated(changedProperties) {
77+
super.firstUpdated(changedProperties);
78+
this.removeAttribute('type');
79+
}
80+
81+
__handleToggleStateChange() {
82+
if (this.disabled) {
83+
return;
84+
}
85+
this.checked = !this.checked;
86+
this.dispatchEvent(
87+
new Event('checked-changed', {
88+
composed: true,
89+
bubbles: true,
90+
}),
91+
);
92+
}
93+
94+
/**
95+
* We synchronously update aria-checked to support voice over on safari.
96+
*
97+
* @override
98+
*/
99+
_requestUpdate(name, oldValue) {
100+
super._requestUpdate(name, oldValue);
101+
if (this.isConnected && name === 'checked') {
102+
this.setAttribute('aria-checked', `${this.checked}`);
103+
}
104+
}
105+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { html, css } from '@lion/core';
2+
import { LionField } from '@lion/field';
3+
import { ChoiceInputMixin } from '@lion/choice-input';
4+
5+
import '../lion-button-switch.js';
6+
7+
export class LionInputSwitch extends ChoiceInputMixin(LionField) {
8+
static get styles() {
9+
return [
10+
super.styles,
11+
css`
12+
:host([disabled]) {
13+
color: #adadad;
14+
}
15+
`,
16+
];
17+
}
18+
19+
get slots() {
20+
return {
21+
...super.slots,
22+
input: () => document.createElement('lion-button-switch'),
23+
};
24+
}
25+
26+
render() {
27+
return html`
28+
<div class="input-switch__container">
29+
<slot name="label"></slot>
30+
<slot name="help-text"></slot>
31+
<slot name="feedback"></slot>
32+
</div>
33+
<slot name="input"></slot>
34+
`;
35+
}
36+
37+
connectedCallback() {
38+
super.connectedCallback();
39+
this.inputElement.addEventListener(
40+
'checked-changed',
41+
this.__handleButtonSwitchCheckedChanged.bind(this),
42+
);
43+
this._syncButtonSwitch();
44+
}
45+
46+
updated(changedProperties) {
47+
super.updated(changedProperties);
48+
this._syncButtonSwitch();
49+
}
50+
51+
__handleButtonSwitchCheckedChanged() {
52+
// TODO: should be replaced by "_inputNode" after the next breaking change
53+
// https://github.com/ing-bank/lion/blob/master/packages/field/src/FormControlMixin.js#L78
54+
this.checked = this.inputElement.checked;
55+
}
56+
57+
_syncButtonSwitch() {
58+
this.inputElement.checked = this.checked;
59+
this.inputElement.disabled = this.disabled;
60+
}
61+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { storiesOf, html } from '@open-wc/demoing-storybook';
2+
import { LitElement } from '@lion/core';
3+
4+
import { LocalizeMixin } from '@lion/localize';
5+
6+
import '../lion-input-switch.js';
7+
import '@lion/form/lion-form.js';
8+
9+
storiesOf('Forms|Switch', module)
10+
.add(
11+
'All text slots',
12+
() => html`
13+
<lion-input-switch label="Label" help-text="Help text"> </lion-input-switch>
14+
`,
15+
)
16+
.add(
17+
'Disabled',
18+
() => html`
19+
<lion-input-switch label="Disabled label" disabled> </lion-input-switch>
20+
`,
21+
)
22+
.add('Validation', () => {
23+
const isTrue = value => value && value.checked && value.checked === true;
24+
const isTrueValidator = (...factoryParams) => [
25+
(...params) => ({
26+
isTrue: isTrue(...params),
27+
}),
28+
...factoryParams,
29+
];
30+
const tagName = 'lion-switch-validation-demo';
31+
if (!customElements.get(tagName)) {
32+
customElements.define(
33+
tagName,
34+
class extends LocalizeMixin(LitElement) {
35+
static get localizeNamespaces() {
36+
const result = [
37+
{
38+
'lion-validate+isTrue': () =>
39+
Promise.resolve({
40+
info: {
41+
isTrue: 'You will not get the latest news!',
42+
},
43+
}),
44+
},
45+
...super.localizeNamespaces,
46+
];
47+
return result;
48+
}
49+
50+
render() {
51+
return html`
52+
<lion-form id="postsForm" @submit="${this.submit}">
53+
<form>
54+
<lion-input-switch name="emailAddress" label="Share email address">
55+
</lion-input-switch>
56+
<lion-input-switch name="subjectField" label="Show subject field" checked>
57+
</lion-input-switch>
58+
<lion-input-switch name="characterCount" label="Character count">
59+
</lion-input-switch>
60+
<lion-input-switch
61+
name="newsletterCheck"
62+
label="* Subscribe to newsletter"
63+
.infoValidators="${[isTrueValidator()]}"
64+
>
65+
</lion-input-switch>
66+
<button type="submit">
67+
Submit
68+
</button>
69+
</form>
70+
</lion-form>
71+
`;
72+
}
73+
74+
submit() {
75+
const form = this.shadowRoot.querySelector('#postsForm');
76+
if (form.errorState === false) {
77+
console.log(form.serializeGroup());
78+
}
79+
}
80+
},
81+
);
82+
}
83+
return html`
84+
<lion-switch-validation-demo></lion-switch-validation-demo>
85+
`;
86+
});

0 commit comments

Comments
 (0)