feat: flatten modelValue and remove checkedValue
Co-authored-by: daKmoR <Thomas.Allmer@ing.com> Co-authored-by: CubLion <Alex.Ghiu@ing.com>
This commit is contained in:
parent
3995eb8397
commit
848ff06887
30 changed files with 2436 additions and 2247 deletions
|
|
@ -79,7 +79,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "./bundlesize/dist/all/all-*.js",
|
"path": "./bundlesize/dist/all/all-*.js",
|
||||||
"maxSize": "30 kB"
|
"maxSize": "40 kB"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,11 @@ import '@lion/checkbox-group/lion-checkbox-group.js';
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<lion-checkbox-group
|
<lion-checkbox-group
|
||||||
name="scientistsGroup"
|
name="scientists[]"
|
||||||
label="Favorite scientists"
|
label="Favorite scientists"
|
||||||
>
|
>
|
||||||
<lion-checkbox name="scientists[]" label="Archimedes" .choiceValue=${'Archimedes'}></lion-checkbox>
|
<lion-checkbox label="Archimedes" .choiceValue=${'Archimedes'}></lion-checkbox>
|
||||||
<lion-checkbox name="scientists[]" label="Francis Bacon" .choiceValue=${'Francis Bacon'}></lion-checkbox>
|
<lion-checkbox label="Francis Bacon" .choiceValue=${'Francis Bacon'}></lion-checkbox>
|
||||||
<lion-checkbox name="scientists[]" label="Marie Curie" .choiceValue=${'Marie Curie'}></lion-checkbox>
|
<lion-checkbox label="Marie Curie" .choiceValue=${'Marie Curie'}></lion-checkbox>
|
||||||
</lion-checkbox-group>
|
</lion-checkbox-group>
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
"*.js"
|
"*.js"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@lion/choice-input": "0.5.9",
|
||||||
"@lion/core": "0.4.3",
|
"@lion/core": "0.4.3",
|
||||||
"@lion/fieldset": "0.6.9"
|
"@lion/fieldset": "0.6.9"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,9 @@
|
||||||
|
import { ChoiceGroupMixin } from '@lion/choice-input';
|
||||||
import { LionFieldset } from '@lion/fieldset';
|
import { LionFieldset } from '@lion/fieldset';
|
||||||
|
|
||||||
export class LionCheckboxGroup extends LionFieldset {
|
export class LionCheckboxGroup extends ChoiceGroupMixin(LionFieldset) {
|
||||||
// eslint-disable-next-line class-methods-use-this
|
constructor() {
|
||||||
_isEmpty(modelValues) {
|
super();
|
||||||
const keys = Object.keys(modelValues);
|
this.multipleChoice = true;
|
||||||
for (let i = 0; i < keys.length; i += 1) {
|
|
||||||
const modelValue = modelValues[keys[i]];
|
|
||||||
if (Array.isArray(modelValue)) {
|
|
||||||
// grouped via myName[]
|
|
||||||
return !modelValue.some(node => node.checked);
|
|
||||||
}
|
|
||||||
return !modelValue.checked;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,24 +16,24 @@ Its purpose is to provide a way for users to check **multiple** options amongst
|
||||||
<Story name="Default">
|
<Story name="Default">
|
||||||
{html`
|
{html`
|
||||||
<lion-checkbox-group
|
<lion-checkbox-group
|
||||||
name="scientistsGroup"
|
name="scientists[]"
|
||||||
label="Favorite scientists"
|
label="Favorite scientists"
|
||||||
>
|
>
|
||||||
<lion-checkbox name="scientists[]" label="Archimedes" .choiceValue=${'Archimedes'}></lion-checkbox>
|
<lion-checkbox label="Archimedes" .choiceValue=${'Archimedes'}></lion-checkbox>
|
||||||
<lion-checkbox name="scientists[]" label="Francis Bacon" .choiceValue=${'Francis Bacon'}></lion-checkbox>
|
<lion-checkbox label="Francis Bacon" .choiceValue=${'Francis Bacon'}></lion-checkbox>
|
||||||
<lion-checkbox name="scientists[]" label="Marie Curie" .choiceValue=${'Marie Curie'}></lion-checkbox>
|
<lion-checkbox label="Marie Curie" .choiceValue=${'Marie Curie'}></lion-checkbox>
|
||||||
</lion-checkbox-group>
|
</lion-checkbox-group>
|
||||||
`}
|
`}
|
||||||
</Story>
|
</Story>
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<lion-checkbox-group
|
<lion-checkbox-group
|
||||||
name="scientistsGroup"
|
name="scientists[]"
|
||||||
label="Favourite scientists"
|
label="Favourite scientists"
|
||||||
>
|
>
|
||||||
<lion-checkbox name="scientists[]" label="Archimedes" .choiceValue=${'Archimedes'}></lion-checkbox>
|
<lion-checkbox label="Archimedes" .choiceValue=${'Archimedes'}></lion-checkbox>
|
||||||
<lion-checkbox name="scientists[]" label="Francis Bacon" .choiceValue=${'Francis Bacon'}></lion-checkbox>
|
<lion-checkbox label="Francis Bacon" .choiceValue=${'Francis Bacon'}></lion-checkbox>
|
||||||
<lion-checkbox name="scientists[]" label="Marie Curie" .choiceValue=${'Marie Curie'}></lion-checkbox>
|
<lion-checkbox label="Marie Curie" .choiceValue=${'Marie Curie'}></lion-checkbox>
|
||||||
</lion-checkbox-group>
|
</lion-checkbox-group>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -62,20 +62,17 @@ You can pre-select options by targeting the `modelValue` object of the option an
|
||||||
|
|
||||||
<Story name="Pre-select">
|
<Story name="Pre-select">
|
||||||
{html`
|
{html`
|
||||||
<lion-checkbox-group name="scientistsGroup" label="Favorite scientists">
|
<lion-checkbox-group name="scientists" label="Favorite scientists">
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Archimedes"
|
label="Archimedes"
|
||||||
.choiceValue=${'Archimedes'}
|
.choiceValue=${'Archimedes'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Francis Bacon"
|
label="Francis Bacon"
|
||||||
.choiceValue=${'Francis Bacon'}
|
.choiceValue=${'Francis Bacon'}
|
||||||
checked
|
checked
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Marie Curie"
|
label="Marie Curie"
|
||||||
.modelValue=${{ value: 'Marie Curie', checked: true }}
|
.modelValue=${{ value: 'Marie Curie', checked: true }}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
|
|
@ -84,20 +81,17 @@ You can pre-select options by targeting the `modelValue` object of the option an
|
||||||
</Story>
|
</Story>
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<lion-checkbox-group name="scientistsGroup" label="Favorite scientists">
|
<lion-checkbox-group name="scientists[]" label="Favorite scientists">
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Archimedes"
|
label="Archimedes"
|
||||||
.choiceValue=${'Archimedes'}
|
.choiceValue=${'Archimedes'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Francis Bacon"
|
label="Francis Bacon"
|
||||||
.choiceValue=${'Francis Bacon'}
|
.choiceValue=${'Francis Bacon'}
|
||||||
checked
|
checked
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Marie Curie"
|
label="Marie Curie"
|
||||||
.modelValue=${{ value: 'Marie Curie', checked: true }}
|
.modelValue=${{ value: 'Marie Curie', checked: true }}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
|
|
@ -110,19 +104,16 @@ You can disable the entire group by setting the `disabled` attribute on the `<li
|
||||||
|
|
||||||
<Story name="Disabled">
|
<Story name="Disabled">
|
||||||
{html`
|
{html`
|
||||||
<lion-checkbox-group name="scientistsGroup" label="Favorite scientists" disabled>
|
<lion-checkbox-group name="scientists[]" label="Favorite scientists" disabled>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Archimedes"
|
label="Archimedes"
|
||||||
.choiceValue=${'Archimedes'}
|
.choiceValue=${'Archimedes'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Francis Bacon"
|
label="Francis Bacon"
|
||||||
.choiceValue=${'Francis Bacon'}
|
.choiceValue=${'Francis Bacon'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Marie Curie"
|
label="Marie Curie"
|
||||||
.modelValue=${{ value: 'Marie Curie', checked: true }}
|
.modelValue=${{ value: 'Marie Curie', checked: true }}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
|
|
@ -131,19 +122,16 @@ You can disable the entire group by setting the `disabled` attribute on the `<li
|
||||||
</Story>
|
</Story>
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<lion-checkbox-group name="scientistsGroup" label="Favorite scientists" disabled>
|
<lion-checkbox-group name="scientists[]" label="Favorite scientists" disabled>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Archimedes"
|
label="Archimedes"
|
||||||
.choiceValue=${'Archimedes'}
|
.choiceValue=${'Archimedes'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Francis Bacon"
|
label="Francis Bacon"
|
||||||
.choiceValue=${'Francis Bacon'}
|
.choiceValue=${'Francis Bacon'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Marie Curie"
|
label="Marie Curie"
|
||||||
.modelValue=${{ value: 'Marie Curie', checked: true }}
|
.modelValue=${{ value: 'Marie Curie', checked: true }}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
|
|
@ -164,23 +152,20 @@ The interaction states of the `<lion-checkbox-group>` are evaluated in order to
|
||||||
};
|
};
|
||||||
return html`
|
return html`
|
||||||
<lion-checkbox-group
|
<lion-checkbox-group
|
||||||
id="scientistsGroup"
|
id="scientists"
|
||||||
name="scientistsGroup"
|
name="scientists[]"
|
||||||
label="Favorite scientists"
|
label="Favorite scientists"
|
||||||
.validators=${[new Required()]}
|
.validators=${[new Required()]}
|
||||||
>
|
>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Archimedes"
|
label="Archimedes"
|
||||||
.choiceValue=${'Archimedes'}
|
.choiceValue=${'Archimedes'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Francis Bacon"
|
label="Francis Bacon"
|
||||||
.choiceValue=${'Francis Bacon'}
|
.choiceValue=${'Francis Bacon'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Marie Curie"
|
label="Marie Curie"
|
||||||
.choiceValue=${'Marie Curie'}
|
.choiceValue=${'Marie Curie'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
|
|
@ -201,23 +186,20 @@ const validate = () => {
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<lion-checkbox-group
|
<lion-checkbox-group
|
||||||
id="scientistsGroup"
|
id="scientists"
|
||||||
name="scientistsGroup"
|
name="scientists[]"
|
||||||
label="Favorite scientists"
|
label="Favorite scientists"
|
||||||
.validators=${[new Required()]}
|
.validators=${[new Required()]}
|
||||||
>
|
>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Archimedes"
|
label="Archimedes"
|
||||||
.choiceValue=${'Archimedes'}
|
.choiceValue=${'Archimedes'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Francis Bacon"
|
label="Francis Bacon"
|
||||||
.choiceValue=${'Francis Bacon'}
|
.choiceValue=${'Francis Bacon'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Marie Curie"
|
label="Marie Curie"
|
||||||
.choiceValue=${'Marie Curie'}
|
.choiceValue=${'Marie Curie'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
|
|
@ -239,41 +221,33 @@ Below is a more advanced validator on the group that evaluates the children chec
|
||||||
this.name = 'HasMinTwoChecked';
|
this.name = 'HasMinTwoChecked';
|
||||||
}
|
}
|
||||||
execute(value) {
|
execute(value) {
|
||||||
let hasError = false;
|
return value.length < 2;
|
||||||
const selectedValues = value['scientists[]'].filter(v => v.checked === true);
|
|
||||||
if (!(selectedValues.length >= 2)) {
|
|
||||||
hasError = true;
|
|
||||||
}
|
|
||||||
return hasError;
|
|
||||||
}
|
}
|
||||||
static async getMessage() {
|
static async getMessage() {
|
||||||
return 'You need to select at least 2 values.';
|
return 'You need to select at least 2 values.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const validate = () => {
|
const validate = () => {
|
||||||
const checkboxGroup = document.querySelector('#scientistsGroup2');
|
const checkboxGroup = document.querySelector('#scientists2');
|
||||||
checkboxGroup.submitted = !checkboxGroup.submitted;
|
checkboxGroup.submitted = !checkboxGroup.submitted;
|
||||||
};
|
};
|
||||||
return html`
|
return html`
|
||||||
<lion-checkbox-group
|
<lion-checkbox-group
|
||||||
id="scientistsGroup2"
|
id="scientists2"
|
||||||
name="scientistsGroup"
|
name="scientists[]"
|
||||||
label="Favorite scientists"
|
label="Favorite scientists"
|
||||||
help-text="You should have at least 2 of those"
|
help-text="You should have at least 2 of those"
|
||||||
.validators=${[new Required(), new HasMinTwoChecked()]}
|
.validators=${[new Required(), new HasMinTwoChecked()]}
|
||||||
>
|
>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Archimedes"
|
label="Archimedes"
|
||||||
.choiceValue=${'Archimedes'}
|
.choiceValue=${'Archimedes'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Francis Bacon"
|
label="Francis Bacon"
|
||||||
.choiceValue=${'Francis Bacon'}
|
.choiceValue=${'Francis Bacon'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Marie Curie"
|
label="Marie Curie"
|
||||||
.choiceValue=${'Marie Curie'}
|
.choiceValue=${'Marie Curie'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
|
|
@ -295,12 +269,7 @@ class HasMinTwoChecked extends Validator {
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(value) {
|
execute(value) {
|
||||||
let hasError = false;
|
return value.length < 2;
|
||||||
const selectedValues = value['scientists[]'].filter(v => v.checked === true);
|
|
||||||
if (!(selectedValues.length >= 2)) {
|
|
||||||
hasError = true;
|
|
||||||
}
|
|
||||||
return hasError;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getMessage() {
|
static async getMessage() {
|
||||||
|
|
@ -309,31 +278,28 @@ class HasMinTwoChecked extends Validator {
|
||||||
}
|
}
|
||||||
|
|
||||||
const validate = () => {
|
const validate = () => {
|
||||||
const checkboxGroup = document.querySelector('#scientistsGroup');
|
const checkboxGroup = document.querySelector('#scientists2');
|
||||||
checkboxGroup.submitted = !checkboxGroup.submitted;
|
checkboxGroup.submitted = !checkboxGroup.submitted;
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<lion-checkbox-group
|
<lion-checkbox-group
|
||||||
id="scientistsGroup"
|
id="scientists2"
|
||||||
name="scientistsGroup"
|
name="scientists[]"
|
||||||
label="Favorite scientists"
|
label="Favorite scientists"
|
||||||
help-text="You should have at least 2 of those"
|
help-text="You should have at least 2 of those"
|
||||||
.validators=${[new Required(), new HasMinTwoChecked()]}
|
.validators=${[new Required(), new HasMinTwoChecked()]}
|
||||||
>
|
>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Archimedes"
|
label="Archimedes"
|
||||||
.choiceValue=${'Archimedes'}
|
.choiceValue=${'Archimedes'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Francis Bacon"
|
label="Francis Bacon"
|
||||||
.choiceValue=${'Francis Bacon'}
|
.choiceValue=${'Francis Bacon'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Marie Curie"
|
label="Marie Curie"
|
||||||
.choiceValue=${'Marie Curie'}
|
.choiceValue=${'Marie Curie'}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
import { expect, html, fixture, nextFrame } from '@open-wc/testing';
|
|
||||||
|
|
||||||
import { localizeTearDown } from '@lion/localize/test-helpers.js';
|
|
||||||
import { Required } from '@lion/validate';
|
|
||||||
|
|
||||||
import '@lion/checkbox/lion-checkbox.js';
|
import '@lion/checkbox/lion-checkbox.js';
|
||||||
|
import { localizeTearDown } from '@lion/localize/test-helpers.js';
|
||||||
|
import { expect, fixture, html } from '@open-wc/testing';
|
||||||
import '../lion-checkbox-group.js';
|
import '../lion-checkbox-group.js';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
@ -11,36 +8,12 @@ beforeEach(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('<lion-checkbox-group>', () => {
|
describe('<lion-checkbox-group>', () => {
|
||||||
it('can be required', async () => {
|
|
||||||
const el = await fixture(html`
|
|
||||||
<lion-checkbox-group .validators=${[new Required()]}>
|
|
||||||
<lion-checkbox name="sports[]" .choiceValue=${'running'}></lion-checkbox>
|
|
||||||
<lion-checkbox name="sports[]" .choiceValue=${'swimming'}></lion-checkbox>
|
|
||||||
</lion-checkbox-group>
|
|
||||||
`);
|
|
||||||
await nextFrame();
|
|
||||||
|
|
||||||
expect(el.hasFeedbackFor).to.deep.equal(['error']);
|
|
||||||
expect(el.validationStates.error.Required).to.be.true;
|
|
||||||
el.formElements['sports[]'][0].checked = true;
|
|
||||||
expect(el.hasFeedbackFor).to.deep.equal([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('is accessible', async () => {
|
it('is accessible', async () => {
|
||||||
const el = await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-checkbox-group name="scientistsGroup" label="Who are your favorite scientists?">
|
<lion-checkbox-group name="scientists[]" label="Who are your favorite scientists?">
|
||||||
|
<lion-checkbox label="Archimedes" .choiceValue=${'Archimedes'}></lion-checkbox>
|
||||||
|
<lion-checkbox label="Francis Bacon" .choiceValue=${'Francis Bacon'}></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Archimedes"
|
|
||||||
.choiceValue=${'Archimedes'}
|
|
||||||
></lion-checkbox>
|
|
||||||
<lion-checkbox
|
|
||||||
name="scientists[]"
|
|
||||||
label="Francis Bacon"
|
|
||||||
.choiceValue=${'Francis Bacon'}
|
|
||||||
></lion-checkbox>
|
|
||||||
<lion-checkbox
|
|
||||||
name="scientists[]"
|
|
||||||
label="Marie Curie"
|
label="Marie Curie"
|
||||||
.modelValue=${{ value: 'Marie Curie', checked: false }}
|
.modelValue=${{ value: 'Marie Curie', checked: false }}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
|
|
@ -51,20 +24,14 @@ describe('<lion-checkbox-group>', () => {
|
||||||
|
|
||||||
it('is accessible when pre-selected', async () => {
|
it('is accessible when pre-selected', async () => {
|
||||||
const el = await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-checkbox-group name="scientistsGroup" label="Who are your favorite scientists?">
|
<lion-checkbox-group name="scientists[]" label="Who are your favorite scientists?">
|
||||||
|
<lion-checkbox label="Archimedes" .choiceValue=${'Archimedes'}></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Archimedes"
|
|
||||||
.choiceValue=${'Archimedes'}
|
|
||||||
></lion-checkbox>
|
|
||||||
<lion-checkbox
|
|
||||||
name="scientists[]"
|
|
||||||
label="Francis Bacon"
|
label="Francis Bacon"
|
||||||
.choiceValue=${'Francis Bacon'}
|
.choiceValue=${'Francis Bacon'}
|
||||||
.choiceChecked=${true}
|
.choiceChecked=${true}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Marie Curie"
|
label="Marie Curie"
|
||||||
.modelValue=${{ value: 'Marie Curie', checked: true }}
|
.modelValue=${{ value: 'Marie Curie', checked: true }}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
|
|
@ -75,23 +42,10 @@ describe('<lion-checkbox-group>', () => {
|
||||||
|
|
||||||
it('is accessible when disabled', async () => {
|
it('is accessible when disabled', async () => {
|
||||||
const el = await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-checkbox-group
|
<lion-checkbox-group name="scientists[]" label="Who are your favorite scientists?" disabled>
|
||||||
name="scientistsGroup"
|
<lion-checkbox label="Archimedes" .choiceValue=${'Archimedes'}></lion-checkbox>
|
||||||
label="Who are your favorite scientists?"
|
<lion-checkbox label="Francis Bacon" .choiceValue=${'Francis Bacon'}></lion-checkbox>
|
||||||
disabled
|
|
||||||
>
|
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="scientists[]"
|
|
||||||
label="Archimedes"
|
|
||||||
.choiceValue=${'Archimedes'}
|
|
||||||
></lion-checkbox>
|
|
||||||
<lion-checkbox
|
|
||||||
name="scientists[]"
|
|
||||||
label="Francis Bacon"
|
|
||||||
.choiceValue=${'Francis Bacon'}
|
|
||||||
></lion-checkbox>
|
|
||||||
<lion-checkbox
|
|
||||||
name="scientists[]"
|
|
||||||
label="Marie Curie"
|
label="Marie Curie"
|
||||||
.modelValue=${{ value: 'Marie Curie', checked: true }}
|
.modelValue=${{ value: 'Marie Curie', checked: true }}
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
|
export { ChoiceGroupMixin } from './src/ChoiceGroupMixin.js';
|
||||||
export { ChoiceInputMixin } from './src/ChoiceInputMixin.js';
|
export { ChoiceInputMixin } from './src/ChoiceInputMixin.js';
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@
|
||||||
"@lion/field": "0.8.9"
|
"@lion/field": "0.8.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@lion/fieldset": "0.6.9",
|
||||||
"@lion/input": "0.5.9",
|
"@lion/input": "0.5.9",
|
||||||
"@lion/validate": "0.6.6",
|
"@lion/validate": "0.6.6",
|
||||||
"@open-wc/demoing-storybook": "^1.8.3",
|
"@open-wc/demoing-storybook": "^1.8.3",
|
||||||
|
|
|
||||||
167
packages/choice-input/src/ChoiceGroupMixin.js
Normal file
167
packages/choice-input/src/ChoiceGroupMixin.js
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
import { dedupeMixin } from '@lion/core';
|
||||||
|
import { InteractionStateMixin, FormRegistrarMixin } from '@lion/field';
|
||||||
|
|
||||||
|
export const ChoiceGroupMixin = dedupeMixin(
|
||||||
|
superclass =>
|
||||||
|
// eslint-disable-next-line
|
||||||
|
class ChoiceGroupMixin extends FormRegistrarMixin(InteractionStateMixin(superclass)) {
|
||||||
|
get modelValue() {
|
||||||
|
const elems = this._getCheckedElements();
|
||||||
|
if (this.multipleChoice) {
|
||||||
|
return elems.map(el => el.modelValue.value);
|
||||||
|
}
|
||||||
|
return elems ? elems.modelValue.value : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
set modelValue(value) {
|
||||||
|
this._setCheckedElements(value, (el, val) => el.modelValue.value === val);
|
||||||
|
}
|
||||||
|
|
||||||
|
get serializedValue() {
|
||||||
|
const elems = this._getCheckedElements();
|
||||||
|
if (this.multipleChoice) {
|
||||||
|
return this.modelValue;
|
||||||
|
}
|
||||||
|
return elems ? elems.serializedValue : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
set serializedValue(value) {
|
||||||
|
this._setCheckedElements(value, (el, val) => el.serializedValue === val);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.multipleChoice = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
if (!this.multipleChoice) {
|
||||||
|
this.addEventListener('model-value-changed', this._checkSingleChoiceElements);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
if (!this.multipleChoice) {
|
||||||
|
this.removeEventListener('model-value-changed', this._checkSingleChoiceElements);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override from FormRegistrarMixin
|
||||||
|
*/
|
||||||
|
addFormElement(child, indexToInsertAt) {
|
||||||
|
this._throwWhenInvalidChildModelValue(child);
|
||||||
|
this.__delegateNameAttribute(child);
|
||||||
|
super.addFormElement(child, indexToInsertAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override from LionFieldset
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
get _childrenCanHaveSameName() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override from LionFieldset
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
get _childNamesCanBeDuplicate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_throwWhenInvalidChildModelValue(child) {
|
||||||
|
if (
|
||||||
|
typeof child.modelValue.checked !== 'boolean' ||
|
||||||
|
!Object.prototype.hasOwnProperty.call(child.modelValue, 'value')
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`The ${this.tagName.toLowerCase()} name="${
|
||||||
|
this.name
|
||||||
|
}" does not allow to register ${child.tagName.toLowerCase()} with .modelValue="${
|
||||||
|
child.modelValue
|
||||||
|
}" - The modelValue should represent an Object { value: "foo", checked: false }`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_isEmpty() {
|
||||||
|
const value = this.modelValue;
|
||||||
|
if (this.multipleChoice) {
|
||||||
|
return this.modelValue.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'string' && value === '') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_checkSingleChoiceElements(ev) {
|
||||||
|
const { target } = ev;
|
||||||
|
if (target.checked === false) return;
|
||||||
|
|
||||||
|
const groupName = target.name;
|
||||||
|
this.formElementsArray
|
||||||
|
.filter(i => i.name === groupName)
|
||||||
|
.forEach(choice => {
|
||||||
|
if (choice !== target) {
|
||||||
|
choice.checked = false; // eslint-disable-line no-param-reassign
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.__triggerCheckedValueChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
_getCheckedElements() {
|
||||||
|
const filtered = this.formElementsArray.filter(el => el.checked === true);
|
||||||
|
if (this.multipleChoice) {
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
return filtered.length > 0 ? filtered[0] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _setCheckedElements(value, check) {
|
||||||
|
if (!this.__readyForRegistration) {
|
||||||
|
await this.registrationReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this.formElementsArray.length; i += 1) {
|
||||||
|
if (this.multipleChoice) {
|
||||||
|
this.formElementsArray[i].checked = value.includes(this.formElementsArray[i].value);
|
||||||
|
} else if (check(this.formElementsArray[i], value)) {
|
||||||
|
// Allows checking against custom values e.g. formattedValue or serializedValue
|
||||||
|
this.formElementsArray[i].checked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__triggerCheckedValueChanged() {
|
||||||
|
const value = this.modelValue;
|
||||||
|
if (value != null && value !== this.__previousCheckedValue) {
|
||||||
|
this.touched = true;
|
||||||
|
this.__previousCheckedValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__delegateNameAttribute(child) {
|
||||||
|
if (!child.name || child.name === this.name) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
child.name = this.name;
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`The ${this.tagName.toLowerCase()} name="${
|
||||||
|
this.name
|
||||||
|
}" does not allow to register ${child.tagName.toLowerCase()} with custom names (name="${
|
||||||
|
child.name
|
||||||
|
}" given)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
305
packages/choice-input/test/ChoiceGroupMixin.test.js
Normal file
305
packages/choice-input/test/ChoiceGroupMixin.test.js
Normal file
|
|
@ -0,0 +1,305 @@
|
||||||
|
import { html } from '@lion/core';
|
||||||
|
import { LionFieldset } from '@lion/fieldset';
|
||||||
|
import { LionInput } from '@lion/input';
|
||||||
|
import { Required } from '@lion/validate';
|
||||||
|
import { expect, fixture, nextFrame } from '@open-wc/testing';
|
||||||
|
import { ChoiceGroupMixin } from '../src/ChoiceGroupMixin.js';
|
||||||
|
import { ChoiceInputMixin } from '../src/ChoiceInputMixin.js';
|
||||||
|
|
||||||
|
describe('ChoiceGroupMixin', () => {
|
||||||
|
before(() => {
|
||||||
|
class ChoiceInput extends ChoiceInputMixin(LionInput) {}
|
||||||
|
customElements.define('choice-group-input', ChoiceInput);
|
||||||
|
|
||||||
|
class ChoiceGroup extends ChoiceGroupMixin(LionFieldset) {}
|
||||||
|
customElements.define('choice-group', ChoiceGroup);
|
||||||
|
|
||||||
|
class ChoiceGroupMultiple extends ChoiceGroupMixin(LionFieldset) {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.multipleChoice = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customElements.define('choice-group-multiple', ChoiceGroupMultiple);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a single modelValue representing the currently checked radio value', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<choice-group name="gender">
|
||||||
|
<choice-group-input .choiceValue=${'male'}></choice-group-input>
|
||||||
|
<choice-group-input .choiceValue=${'female'} checked></choice-group-input>
|
||||||
|
<choice-group-input .choiceValue=${'other'}></choice-group-input>
|
||||||
|
</choice-group>
|
||||||
|
`);
|
||||||
|
await nextFrame();
|
||||||
|
expect(el.modelValue).to.equal('female');
|
||||||
|
el.formElementsArray[0].checked = true;
|
||||||
|
expect(el.modelValue).to.equal('male');
|
||||||
|
el.formElementsArray[2].checked = true;
|
||||||
|
expect(el.modelValue).to.equal('other');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if a child element without a modelValue like { value: "foo", checked: false } tries to register', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<choice-group name="gender">
|
||||||
|
<choice-group-input .choiceValue=${'male'}></choice-group-input>
|
||||||
|
<choice-group-input .choiceValue=${'female'} checked></choice-group-input>
|
||||||
|
<choice-group-input .choiceValue=${'other'}></choice-group-input>
|
||||||
|
</choice-group>
|
||||||
|
`);
|
||||||
|
await nextFrame();
|
||||||
|
const invalidChild = await fixture(html`
|
||||||
|
<choice-group-input .modelValue=${'Lara'}></choice-group-input>
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
el.addFormElement(invalidChild);
|
||||||
|
}).to.throw(
|
||||||
|
'The choice-group name="gender" does not allow to register choice-group-input with .modelValue="Lara" - The modelValue should represent an Object { value: "foo", checked: false }',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('automatically sets the name property of child radios to its own name', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<choice-group name="gender">
|
||||||
|
<choice-group-input .choiceValue=${'female'} checked></choice-group-input>
|
||||||
|
<choice-group-input .choiceValue=${'other'}></choice-group-input>
|
||||||
|
</choice-group>
|
||||||
|
`);
|
||||||
|
await nextFrame();
|
||||||
|
|
||||||
|
expect(el.formElementsArray[0].name).to.equal('gender');
|
||||||
|
expect(el.formElementsArray[1].name).to.equal('gender');
|
||||||
|
|
||||||
|
const validChild = await fixture(html`
|
||||||
|
<choice-group-input .choiceValue=${'male'}></choice-group-input>
|
||||||
|
`);
|
||||||
|
el.appendChild(validChild);
|
||||||
|
|
||||||
|
expect(el.formElementsArray[2].name).to.equal('gender');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if a child element with a different name than the group tries to register', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<choice-group name="gender">
|
||||||
|
<choice-group-input .choiceValue=${'female'} checked></choice-group-input>
|
||||||
|
<choice-group-input .choiceValue=${'other'}></choice-group-input>
|
||||||
|
</choice-group>
|
||||||
|
`);
|
||||||
|
await nextFrame();
|
||||||
|
const invalidChild = await fixture(html`
|
||||||
|
<choice-group-input name="foo" .choiceValue=${'male'}></choice-group-input>
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
el.addFormElement(invalidChild);
|
||||||
|
}).to.throw(
|
||||||
|
'The choice-group name="gender" does not allow to register choice-group-input with custom names (name="foo" given)',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can set initial modelValue on creation', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<choice-group name="gender" .modelValue=${'other'}>
|
||||||
|
<choice-group-input .choiceValue=${'male'}></choice-group-input>
|
||||||
|
<choice-group-input .choiceValue=${'female'}></choice-group-input>
|
||||||
|
<choice-group-input .choiceValue=${'other'}></choice-group-input>
|
||||||
|
</choice-group>
|
||||||
|
`);
|
||||||
|
|
||||||
|
await nextFrame();
|
||||||
|
await el.registrationReady;
|
||||||
|
await el.updateComplete;
|
||||||
|
|
||||||
|
expect(el.modelValue).to.equal('other');
|
||||||
|
expect(el.formElementsArray[2].checked).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can handle complex data via choiceValue', async () => {
|
||||||
|
const date = new Date(2018, 11, 24, 10, 33, 30, 0);
|
||||||
|
|
||||||
|
const el = await fixture(html`
|
||||||
|
<choice-group name="data">
|
||||||
|
<choice-group-input .choiceValue=${{ some: 'data' }}></choice-group-input>
|
||||||
|
<choice-group-input .choiceValue=${date} checked></choice-group-input>
|
||||||
|
</choice-group>
|
||||||
|
`);
|
||||||
|
await nextFrame();
|
||||||
|
|
||||||
|
expect(el.modelValue).to.equal(date);
|
||||||
|
el.formElementsArray[0].checked = true;
|
||||||
|
expect(el.modelValue).to.deep.equal({ some: 'data' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can handle 0 and empty string as valid values', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<choice-group name="data">
|
||||||
|
<choice-group-input .choiceValue=${0} checked></choice-group-input>
|
||||||
|
<choice-group-input .choiceValue=${''}></choice-group-input>
|
||||||
|
</choice-group>
|
||||||
|
`);
|
||||||
|
await nextFrame();
|
||||||
|
|
||||||
|
expect(el.modelValue).to.equal(0);
|
||||||
|
el.formElementsArray[1].checked = true;
|
||||||
|
expect(el.modelValue).to.equal('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can check a radio by supplying an available modelValue', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<choice-group name="gender">
|
||||||
|
<choice-group-input .modelValue="${{ value: 'male', checked: false }}"></choice-group-input>
|
||||||
|
<choice-group-input
|
||||||
|
.modelValue="${{ value: 'female', checked: true }}"
|
||||||
|
></choice-group-input>
|
||||||
|
<choice-group-input
|
||||||
|
.modelValue="${{ value: 'other', checked: false }}"
|
||||||
|
></choice-group-input>
|
||||||
|
</choice-group>
|
||||||
|
`);
|
||||||
|
await nextFrame();
|
||||||
|
expect(el.modelValue).to.equal('female');
|
||||||
|
el.modelValue = 'other';
|
||||||
|
expect(el.formElementsArray[2].checked).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('expect child nodes to only fire one model-value-changed event per instance', async () => {
|
||||||
|
let counter = 0;
|
||||||
|
const el = await fixture(html`
|
||||||
|
<choice-group
|
||||||
|
name="gender"
|
||||||
|
@model-value-changed=${() => {
|
||||||
|
counter += 1;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<choice-group-input .choiceValue=${'male'}></choice-group-input>
|
||||||
|
<choice-group-input .modelValue=${{ value: 'female', checked: true }}></choice-group-input>
|
||||||
|
<choice-group-input .choiceValue=${'other'}></choice-group-input>
|
||||||
|
</choice-group>
|
||||||
|
`);
|
||||||
|
await nextFrame();
|
||||||
|
counter = 0; // reset after setup which may result in different results
|
||||||
|
|
||||||
|
el.formElementsArray[0].checked = true;
|
||||||
|
expect(counter).to.equal(2); // male becomes checked, female becomes unchecked
|
||||||
|
|
||||||
|
// not changed values trigger no event
|
||||||
|
el.formElementsArray[0].checked = true;
|
||||||
|
expect(counter).to.equal(2);
|
||||||
|
|
||||||
|
el.formElementsArray[2].checked = true;
|
||||||
|
expect(counter).to.equal(4); // other becomes checked, male becomes unchecked
|
||||||
|
|
||||||
|
// not found values trigger no event
|
||||||
|
el.modelValue = 'foo';
|
||||||
|
expect(counter).to.equal(4);
|
||||||
|
|
||||||
|
el.modelValue = 'male';
|
||||||
|
expect(counter).to.equal(6); // male becomes checked, other becomes unchecked
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be required', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<choice-group name="gender" .validators=${[new Required()]}>
|
||||||
|
<choice-group-input .choiceValue=${'male'}></choice-group-input>
|
||||||
|
<choice-group-input
|
||||||
|
.choiceValue=${{ subObject: 'satisfies required' }}
|
||||||
|
></choice-group-input>
|
||||||
|
</choice-group>
|
||||||
|
`);
|
||||||
|
expect(el.hasFeedbackFor).to.include('error');
|
||||||
|
expect(el.validationStates).to.have.a.property('error');
|
||||||
|
expect(el.validationStates.error).to.have.a.property('Required');
|
||||||
|
|
||||||
|
el.formElementsArray[0].checked = true;
|
||||||
|
expect(el.hasFeedbackFor).not.to.include('error');
|
||||||
|
expect(el.validationStates).to.have.a.property('error');
|
||||||
|
expect(el.validationStates.error).not.to.have.a.property('Required');
|
||||||
|
|
||||||
|
el.formElementsArray[1].checked = true;
|
||||||
|
expect(el.hasFeedbackFor).not.to.include('error');
|
||||||
|
expect(el.validationStates).to.have.a.property('error');
|
||||||
|
expect(el.validationStates.error).not.to.have.a.property('Required');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns serialized value', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<choice-group name="gender">
|
||||||
|
<choice-group-input .choiceValue=${'male'}></choice-group-input>
|
||||||
|
<choice-group-input .choiceValue=${'female'}></choice-group-input>
|
||||||
|
</choice-group>
|
||||||
|
`);
|
||||||
|
el.formElementsArray[0].checked = true;
|
||||||
|
expect(el.serializedValue).to.deep.equal({ checked: true, value: 'male' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns serialized value on unchecked state', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<choice-group name="gender">
|
||||||
|
<choice-group-input .choiceValue=${'male'}></choice-group-input>
|
||||||
|
<choice-group-input .choiceValue=${'female'}></choice-group-input>
|
||||||
|
</choice-group>
|
||||||
|
`);
|
||||||
|
await nextFrame();
|
||||||
|
|
||||||
|
expect(el.serializedValue).to.deep.equal('');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('multipleChoice', () => {
|
||||||
|
it('has a single modelValue representing all currently checked values', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<choice-group-multiple name="gender[]">
|
||||||
|
<choice-group-input .choiceValue=${'male'}></choice-group-input>
|
||||||
|
<choice-group-input .choiceValue=${'female'} checked></choice-group-input>
|
||||||
|
<choice-group-input .choiceValue=${'other'}></choice-group-input>
|
||||||
|
</choice-group-multiple>
|
||||||
|
`);
|
||||||
|
await nextFrame();
|
||||||
|
expect(el.modelValue).to.eql(['female']);
|
||||||
|
el.formElementsArray[0].checked = true;
|
||||||
|
expect(el.modelValue).to.eql(['male', 'female']);
|
||||||
|
el.formElementsArray[2].checked = true;
|
||||||
|
expect(el.modelValue).to.eql(['male', 'female', 'other']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can check multiple checkboxes by setting the modelValue', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<lion-checkbox-group name="gender[]">
|
||||||
|
<lion-checkbox .choiceValue=${'male'}></lion-checkbox>
|
||||||
|
<lion-checkbox .choiceValue=${'female'}></lion-checkbox>
|
||||||
|
<lion-checkbox .choiceValue=${'other'}></lion-checkbox>
|
||||||
|
</lion-checkbox-group>
|
||||||
|
`);
|
||||||
|
|
||||||
|
await nextFrame();
|
||||||
|
await el.registrationReady;
|
||||||
|
await el.updateComplete;
|
||||||
|
el.modelValue = ['male', 'other'];
|
||||||
|
expect(el.modelValue).to.eql(['male', 'other']);
|
||||||
|
expect(el.formElementsArray[0].checked).to.be.true;
|
||||||
|
expect(el.formElementsArray[2].checked).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('unchecks non-matching checkboxes when setting the modelValue', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<lion-checkbox-group name="gender[]">
|
||||||
|
<lion-checkbox .choiceValue=${'male'} checked></lion-checkbox>
|
||||||
|
<lion-checkbox .choiceValue=${'female'}></lion-checkbox>
|
||||||
|
<lion-checkbox .choiceValue=${'other'} checked></lion-checkbox>
|
||||||
|
</lion-checkbox-group>
|
||||||
|
`);
|
||||||
|
|
||||||
|
await nextFrame();
|
||||||
|
await el.registrationReady;
|
||||||
|
await el.updateComplete;
|
||||||
|
expect(el.modelValue).to.eql(['male', 'other']);
|
||||||
|
expect(el.formElementsArray[0].checked).to.be.true;
|
||||||
|
expect(el.formElementsArray[2].checked).to.be.true;
|
||||||
|
|
||||||
|
el.modelValue = ['female'];
|
||||||
|
expect(el.formElementsArray[0].checked).to.be.false;
|
||||||
|
expect(el.formElementsArray[1].checked).to.be.true;
|
||||||
|
expect(el.formElementsArray[2].checked).to.be.false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import { expect, fixture } from '@open-wc/testing';
|
|
||||||
import { html } from '@lion/core';
|
import { html } from '@lion/core';
|
||||||
import sinon from 'sinon';
|
|
||||||
import { Required } from '@lion/validate';
|
|
||||||
|
|
||||||
import { LionInput } from '@lion/input';
|
import { LionInput } from '@lion/input';
|
||||||
|
import { Required } from '@lion/validate';
|
||||||
|
import { expect, fixture } from '@open-wc/testing';
|
||||||
|
import sinon from 'sinon';
|
||||||
import { ChoiceInputMixin } from '../src/ChoiceInputMixin.js';
|
import { ChoiceInputMixin } from '../src/ChoiceInputMixin.js';
|
||||||
|
|
||||||
describe('ChoiceInputMixin', () => {
|
describe('ChoiceInputMixin', () => {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { dedupeMixin } from '@lion/core';
|
import { dedupeMixin } from '@lion/core';
|
||||||
import { formRegistrarManager } from './formRegistrarManager.js';
|
|
||||||
import { FormRegisteringMixin } from './FormRegisteringMixin.js';
|
import { FormRegisteringMixin } from './FormRegisteringMixin.js';
|
||||||
|
import { formRegistrarManager } from './formRegistrarManager.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This allows an element to become the manager of a register
|
* This allows an element to become the manager of a register
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import { expect, fixture, html, defineCE, unsafeStatic } from '@open-wc/testing';
|
|
||||||
import { LitElement } from '@lion/core';
|
import { LitElement } from '@lion/core';
|
||||||
|
import { defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
|
|
||||||
import { FormRegistrarMixin } from '../src/FormRegistrarMixin.js';
|
|
||||||
import { FormRegisteringMixin } from '../src/FormRegisteringMixin.js';
|
import { FormRegisteringMixin } from '../src/FormRegisteringMixin.js';
|
||||||
import { FormRegistrarPortalMixin } from '../src/FormRegistrarPortalMixin.js';
|
|
||||||
import { formRegistrarManager } from '../src/formRegistrarManager.js';
|
import { formRegistrarManager } from '../src/formRegistrarManager.js';
|
||||||
|
import { FormRegistrarMixin } from '../src/FormRegistrarMixin.js';
|
||||||
|
import { FormRegistrarPortalMixin } from '../src/FormRegistrarPortalMixin.js';
|
||||||
|
|
||||||
export const runRegistrationSuite = customConfig => {
|
export const runRegistrationSuite = customConfig => {
|
||||||
const cfg = {
|
const cfg = {
|
||||||
|
|
@ -127,6 +126,25 @@ export const runRegistrationSuite = customConfig => {
|
||||||
expect(el.formElements.length).to.equal(1);
|
expect(el.formElements.length).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('adds elements to formElements in the right order (DOM)', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<${parentTag}>
|
||||||
|
<${childTag}></${childTag}>
|
||||||
|
<${childTag}></${childTag}>
|
||||||
|
<${childTag}></${childTag}>
|
||||||
|
</${parentTag}>
|
||||||
|
`);
|
||||||
|
const newField = await fixture(html`
|
||||||
|
<${childTag}></${childTag}>
|
||||||
|
`);
|
||||||
|
newField.myProp = 'test';
|
||||||
|
|
||||||
|
el.children[1].insertAdjacentElement('beforebegin', newField);
|
||||||
|
|
||||||
|
expect(el.formElements.length).to.equal(4);
|
||||||
|
expect(el.children[1].myProp).to.equal('test');
|
||||||
|
});
|
||||||
|
|
||||||
describe('FormRegistrarPortalMixin', () => {
|
describe('FormRegistrarPortalMixin', () => {
|
||||||
it('throws if there is no .registrationTarget', async () => {
|
it('throws if there is no .registrationTarget', async () => {
|
||||||
// we test the private api directly as errors thrown from a web component are in a
|
// we test the private api directly as errors thrown from a web component are in a
|
||||||
|
|
|
||||||
|
|
@ -370,13 +370,13 @@ export class LionFieldset extends FormRegistrarMixin(
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
addFormElement(child) {
|
addFormElement(child, indexToInsertAt) {
|
||||||
const { name } = child;
|
const { name } = child;
|
||||||
if (!name) {
|
if (!name) {
|
||||||
console.info('Error Node:', child); // eslint-disable-line no-console
|
console.info('Error Node:', child); // eslint-disable-line no-console
|
||||||
throw new TypeError('You need to define a name');
|
throw new TypeError('You need to define a name');
|
||||||
}
|
}
|
||||||
if (name === this.name) {
|
if (name === this.name && !this._childrenCanHaveSameName) {
|
||||||
console.info('Error Node:', child); // eslint-disable-line no-console
|
console.info('Error Node:', child); // eslint-disable-line no-console
|
||||||
throw new TypeError(`You can not have the same name "${name}" as your parent`);
|
throw new TypeError(`You can not have the same name "${name}" as your parent`);
|
||||||
}
|
}
|
||||||
|
|
@ -385,11 +385,16 @@ export class LionFieldset extends FormRegistrarMixin(
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
child.makeRequestToBeDisabled();
|
child.makeRequestToBeDisabled();
|
||||||
}
|
}
|
||||||
if (name.substr(-2) === '[]') {
|
|
||||||
|
if (name.substr(-2) === '[]' || this._childNamesCanBeDuplicate) {
|
||||||
if (!Array.isArray(this.formElements[name])) {
|
if (!Array.isArray(this.formElements[name])) {
|
||||||
this.formElements[name] = [];
|
this.formElements[name] = [];
|
||||||
}
|
}
|
||||||
|
if (indexToInsertAt > 0) {
|
||||||
|
this.formElements[name].splice(indexToInsertAt, 0, child);
|
||||||
|
} else {
|
||||||
this.formElements[name].push(child);
|
this.formElements[name].push(child);
|
||||||
|
}
|
||||||
} else if (!this.formElements[name]) {
|
} else if (!this.formElements[name]) {
|
||||||
this.formElements[name] = child;
|
this.formElements[name] = child;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -417,6 +422,16 @@ export class LionFieldset extends FormRegistrarMixin(
|
||||||
this.validate();
|
this.validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
get _childrenCanHaveSameName() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
get _childNamesCanBeDuplicate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gathers initial model values of all children. Used
|
* Gathers initial model values of all children. Used
|
||||||
* when resetGroup() is called.
|
* when resetGroup() is called.
|
||||||
|
|
|
||||||
|
|
@ -62,18 +62,18 @@ For usage and installation please see the appropriate packages.
|
||||||
name="checkers"
|
name="checkers"
|
||||||
.validators="${[new Required()]}"
|
.validators="${[new Required()]}"
|
||||||
>
|
>
|
||||||
<lion-checkbox name="checkers[]" value="foo" label="I like foo"></lion-checkbox>
|
<lion-checkbox .choiceValue=${'foo'} label="I like foo"></lion-checkbox>
|
||||||
<lion-checkbox name="checkers[]" value="bar" label="I like bar"></lion-checkbox>
|
<lion-checkbox .choiceValue=${'bar'} label="I like bar"></lion-checkbox>
|
||||||
<lion-checkbox name="checkers[]" value="baz" label="I like baz"></lion-checkbox>
|
<lion-checkbox .choiceValue=${'baz'} label="I like baz"></lion-checkbox>
|
||||||
</lion-checkbox-group>
|
</lion-checkbox-group>
|
||||||
<lion-radio-group
|
<lion-radio-group
|
||||||
name="dinosaurs"
|
name="dinosaurs"
|
||||||
label="Favorite dinosaur"
|
label="Favorite dinosaur"
|
||||||
.validators="${[new Required()]}"
|
.validators="${[new Required()]}"
|
||||||
>
|
>
|
||||||
<lion-radio name="dinosaurs[]" value="allosaurus" label="allosaurus"></lion-radio>
|
<lion-radio .choiceValue=${'allosaurus'} label="allosaurus"></lion-radio>
|
||||||
<lion-radio name="dinosaurs[]" value="brontosaurus" label="brontosaurus"></lion-radio>
|
<lion-radio .choiceValue=${'brontosaurus'} label="brontosaurus"></lion-radio>
|
||||||
<lion-radio name="dinosaurs[]" value="diplodocus" label="diplodocus"></lion-radio>
|
<lion-radio .choiceValue=${'diplodocus'} label="diplodocus"></lion-radio>
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
<lion-select-rich name="favoriteColor" label="Favorite color">
|
<lion-select-rich name="favoriteColor" label="Favorite color">
|
||||||
<lion-options slot="input">
|
<lion-options slot="input">
|
||||||
|
|
@ -104,7 +104,6 @@ For usage and installation please see the appropriate packages.
|
||||||
></lion-input-range>
|
></lion-input-range>
|
||||||
<lion-checkbox-group name="terms" .validators="${[new Required()]}">
|
<lion-checkbox-group name="terms" .validators="${[new Required()]}">
|
||||||
<lion-checkbox
|
<lion-checkbox
|
||||||
name="terms[]"
|
|
||||||
label="I blindly accept all terms and conditions"
|
label="I blindly accept all terms and conditions"
|
||||||
></lion-checkbox>
|
></lion-checkbox>
|
||||||
</lion-checkbox-group>
|
</lion-checkbox-group>
|
||||||
|
|
|
||||||
|
|
@ -169,10 +169,10 @@ In the following example we will demonstrate this with interaction states, the m
|
||||||
<h3>
|
<h3>
|
||||||
Set conditions for validation feedback visibility
|
Set conditions for validation feedback visibility
|
||||||
</h3>
|
</h3>
|
||||||
<lion-checkbox-group name="props" @model-value-changed="${fetchConditionsAndReevaluate}">
|
<lion-checkbox-group name="props[]" @model-value-changed="${fetchConditionsAndReevaluate}">
|
||||||
${props.map(
|
${props.map(
|
||||||
p => html`
|
p => html`
|
||||||
<lion-checkbox name="props[]" .label="${p}" .choiceValue="${p}"> </lion-checkbox>
|
<lion-checkbox .label="${p}" .choiceValue="${p}"> </lion-checkbox>
|
||||||
`,
|
`,
|
||||||
)}
|
)}
|
||||||
</lion-checkbox-group>
|
</lion-checkbox-group>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { html, css, LitElement, DisabledMixin } from '@lion/core';
|
|
||||||
import { FormRegisteringMixin } from '@lion/field';
|
|
||||||
import { ChoiceInputMixin } from '@lion/choice-input';
|
import { ChoiceInputMixin } from '@lion/choice-input';
|
||||||
|
import { css, DisabledMixin, html, LitElement } from '@lion/core';
|
||||||
|
import { FormRegisteringMixin } from '@lion/field';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option
|
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option
|
||||||
|
|
@ -16,6 +16,10 @@ export class LionOption extends DisabledMixin(ChoiceInputMixin(FormRegisteringMi
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
reflect: true,
|
reflect: true,
|
||||||
},
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
reflect: true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,10 @@ import '@lion/radio-group/lion-radio-group.js';
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<lion-radio-group name="dinosGroup" label="What are your favourite dinosaurs?">
|
<lion-radio-group name="dinos" label="What are your favourite dinosaurs?">
|
||||||
<lion-radio name="dinos[]" label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
<lion-radio label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
<lion-radio label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="diplodocus" .choiceValue=${'diplodocus'} checked></lion-radio>
|
<lion-radio label="diplodocus" .choiceValue=${'diplodocus'} checked></lion-radio>
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
"*.js"
|
"*.js"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@lion/choice-input": "0.5.9",
|
||||||
"@lion/core": "0.4.3",
|
"@lion/core": "0.4.3",
|
||||||
"@lion/fieldset": "0.6.9"
|
"@lion/fieldset": "0.6.9"
|
||||||
},
|
},
|
||||||
|
|
@ -39,6 +40,7 @@
|
||||||
"@lion/radio": "0.3.9",
|
"@lion/radio": "0.3.9",
|
||||||
"@lion/validate": "0.6.6",
|
"@lion/validate": "0.6.6",
|
||||||
"@open-wc/demoing-storybook": "^1.8.3",
|
"@open-wc/demoing-storybook": "^1.8.3",
|
||||||
"@open-wc/testing": "^2.5.0"
|
"@open-wc/testing": "^2.5.0",
|
||||||
|
"sinon": "^7.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,22 @@
|
||||||
|
import { ChoiceGroupMixin } from '@lion/choice-input';
|
||||||
import { LionFieldset } from '@lion/fieldset';
|
import { LionFieldset } from '@lion/fieldset';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LionRadioGroup: extends the lion-fieldset
|
* LionRadioGroup: extends the lion-fieldset
|
||||||
*
|
*
|
||||||
* <lion-radio-group>
|
* <lion-radio-group name="radios">
|
||||||
* <label slot="label">My Radio</label>
|
* <label slot="label">My Radio</label>
|
||||||
* <lion-radio name="name[]">
|
* <lion-radio>
|
||||||
* <label slot="label">Male</label>
|
* <label slot="label">Male</label>
|
||||||
* </lion-radio>
|
* </lion-radio>
|
||||||
* <lion-radio name="name[]">
|
* <lion-radio>
|
||||||
* <label slot="label">Female</label>
|
* <label slot="label">Female</label>
|
||||||
* </lion-radio>
|
* </lion-radio>
|
||||||
* </lion-radio-group>
|
* </lion-radio-group>
|
||||||
*
|
*
|
||||||
* You can preselect an option by setting marking an lion-radio checked.
|
* You can preselect an option by setting marking an lion-radio checked.
|
||||||
* Example:
|
* Example:
|
||||||
* <lion-radio name="name[]" checked>
|
* <lion-radio checked></lion-radio>
|
||||||
*
|
*
|
||||||
* It extends LionFieldset so it inherits it's features.
|
* It extends LionFieldset so it inherits it's features.
|
||||||
*
|
*
|
||||||
|
|
@ -24,102 +25,9 @@ import { LionFieldset } from '@lion/fieldset';
|
||||||
* @extends {LionFieldset}
|
* @extends {LionFieldset}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class LionRadioGroup extends LionFieldset {
|
export class LionRadioGroup extends ChoiceGroupMixin(LionFieldset) {
|
||||||
get checkedValue() {
|
|
||||||
const el = this._getCheckedRadioElement();
|
|
||||||
return el ? el.modelValue.value : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
set checkedValue(value) {
|
|
||||||
this._setCheckedRadioElement(value, (el, val) => el.modelValue.value === val);
|
|
||||||
}
|
|
||||||
|
|
||||||
get serializedValue() {
|
|
||||||
const el = this._getCheckedRadioElement();
|
|
||||||
return el ? el.serializedValue : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
set serializedValue(value) {
|
|
||||||
this._setCheckedRadioElement(value, (el, val) => el.serializedValue === val);
|
|
||||||
}
|
|
||||||
|
|
||||||
get formattedValue() {
|
|
||||||
const el = this._getCheckedRadioElement();
|
|
||||||
return el ? el.formattedValue : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
set formattedValue(value) {
|
|
||||||
this._setCheckedRadioElement(value, (el, val) => el.formattedValue === val);
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this.addEventListener('model-value-changed', this._checkRadioElements);
|
|
||||||
this._setRole('radiogroup');
|
this._setRole('radiogroup');
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
|
||||||
super.disconnectedCallback();
|
|
||||||
this.removeEventListener('model-value-changed', this._checkRadioElements);
|
|
||||||
}
|
|
||||||
|
|
||||||
_checkRadioElements(ev) {
|
|
||||||
const { target } = ev;
|
|
||||||
if (target.type !== 'radio' || target.checked === false) return;
|
|
||||||
|
|
||||||
const groupName = target.name;
|
|
||||||
this.formElementsArray
|
|
||||||
.filter(i => i.name === groupName)
|
|
||||||
.forEach(radio => {
|
|
||||||
if (radio !== target) {
|
|
||||||
radio.checked = false; // eslint-disable-line no-param-reassign
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.__triggerCheckedValueChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
_getCheckedRadioElement() {
|
|
||||||
const filtered = this.formElementsArray.filter(el => el.checked === true);
|
|
||||||
return filtered.length > 0 ? filtered[0] : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
async _setCheckedRadioElement(value, check) {
|
|
||||||
if (!this.__readyForRegistration) {
|
|
||||||
await this.registrationReady;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < this.formElementsArray.length; i += 1) {
|
|
||||||
if (check(this.formElementsArray[i], value)) {
|
|
||||||
this.formElementsArray[i].checked = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onFocusOut() {
|
|
||||||
this.touched = true;
|
|
||||||
this.focused = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
__triggerCheckedValueChanged() {
|
|
||||||
const value = this.checkedValue;
|
|
||||||
if (value != null && value !== this.__previousCheckedValue) {
|
|
||||||
this.dispatchEvent(
|
|
||||||
new CustomEvent('checked-value-changed', { bubbles: true, composed: true }),
|
|
||||||
);
|
|
||||||
this.touched = true;
|
|
||||||
this.__previousCheckedValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_isEmpty() {
|
|
||||||
const value = this.checkedValue;
|
|
||||||
if (typeof value === 'string' && value === '') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (value === undefined || value === null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,19 +11,19 @@ You should use `<lion-radio>`s inside this element.
|
||||||
|
|
||||||
<Story name="Default">
|
<Story name="Default">
|
||||||
{html`
|
{html`
|
||||||
<lion-radio-group name="dinosGroup" label="What are your favourite dinosaurs?">
|
<lion-radio-group name="dinos" label="What are your favourite dinosaurs?">
|
||||||
<lion-radio name="dinos[]" label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
<lion-radio label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
<lion-radio label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
<lion-radio label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
`}
|
`}
|
||||||
</Story>
|
</Story>
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<lion-radio-group name="dinosGroup" label="What are your favourite dinosaurs?">
|
<lion-radio-group name="dinos" label="What are your favourite dinosaurs?">
|
||||||
<lion-radio name="dinos[]" label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
<lion-radio label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
<lion-radio label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
<lion-radio label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -60,19 +60,19 @@ You can pre-select an option by adding the checked attribute to the selected `li
|
||||||
|
|
||||||
<Story name="Pre-select">
|
<Story name="Pre-select">
|
||||||
{html`
|
{html`
|
||||||
<lion-radio-group name="dinosGroup" label="What are your favourite dinosaurs?">
|
<lion-radio-group name="dinos" label="What are your favourite dinosaurs?">
|
||||||
<lion-radio name="dinos[]" label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
<lion-radio label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="brontosaurus" .choiceValue=${'brontosaurus'} checked></lion-radio>
|
<lion-radio label="brontosaurus" .choiceValue=${'brontosaurus'} checked></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
<lion-radio label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
`}
|
`}
|
||||||
</Story>
|
</Story>
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<lion-radio-group name="dinosGroup" label="What are your favourite dinosaurs?">
|
<lion-radio-group name="dinos" label="What are your favourite dinosaurs?">
|
||||||
<lion-radio name="dinos[]" label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
<lion-radio label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="brontosaurus" .choiceValue=${'brontosaurus'} checked></lion-radio>
|
<lion-radio label="brontosaurus" .choiceValue=${'brontosaurus'} checked></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
<lion-radio label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -82,19 +82,19 @@ You can disable a specific `lion-radio` option by adding the `disabled` attribut
|
||||||
|
|
||||||
<Story name="Disabled radio">
|
<Story name="Disabled radio">
|
||||||
{html`
|
{html`
|
||||||
<lion-radio-group name="dinosGroup" label="What are your favourite dinosaurs?">
|
<lion-radio-group name="dinos" label="What are your favourite dinosaurs?">
|
||||||
<lion-radio name="dinos[]" label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
<lion-radio label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="brontosaurus" .choiceValue=${'brontosaurus'} disabled></lion-radio>
|
<lion-radio label="brontosaurus" .choiceValue=${'brontosaurus'} disabled></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
<lion-radio label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
`}
|
`}
|
||||||
</Story>
|
</Story>
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<lion-radio-group name="dinosGroup" label="What are your favourite dinosaurs?">
|
<lion-radio-group name="dinos" label="What are your favourite dinosaurs?">
|
||||||
<lion-radio name="dinos[]" label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
<lion-radio label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="brontosaurus" .choiceValue=${'brontosaurus'} disabled></lion-radio>
|
<lion-radio label="brontosaurus" .choiceValue=${'brontosaurus'} disabled></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
<lion-radio label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -102,19 +102,19 @@ You can do the same thing for the entire group by setting the `disabled` attribu
|
||||||
|
|
||||||
<Story name="Disabled group">
|
<Story name="Disabled group">
|
||||||
{html`
|
{html`
|
||||||
<lion-radio-group name="dinosGroup" label="What are your favourite dinosaurs?" disabled>
|
<lion-radio-group name="dinos" label="What are your favourite dinosaurs?" disabled>
|
||||||
<lion-radio name="dinos[]" label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
<lion-radio label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
<lion-radio label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
<lion-radio label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
`}
|
`}
|
||||||
</Story>
|
</Story>
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<lion-radio-group name="dinosGroup" label="What are your favourite dinosaurs?">
|
<lion-radio-group name="dinos" label="What are your favourite dinosaurs?">
|
||||||
<lion-radio name="dinos[]" label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
<lion-radio label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="brontosaurus" .choiceValue=${'brontosaurus'} disabled></lion-radio>
|
<lion-radio label="brontosaurus" .choiceValue=${'brontosaurus'} disabled></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
<lion-radio label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -124,19 +124,19 @@ You can do the same thing for the entire group by setting the `disabled` attribu
|
||||||
{() => {
|
{() => {
|
||||||
loadDefaultFeedbackMessages();
|
loadDefaultFeedbackMessages();
|
||||||
const validate = () => {
|
const validate = () => {
|
||||||
const radioGroup = document.querySelector('#dinosGroup');
|
const radioGroup = document.querySelector('#dinos');
|
||||||
radioGroup.submitted = !radioGroup.submitted;
|
radioGroup.submitted = !radioGroup.submitted;
|
||||||
};
|
};
|
||||||
return html`
|
return html`
|
||||||
<lion-radio-group
|
<lion-radio-group
|
||||||
id="dinosGroup"
|
id="dinos"
|
||||||
name="dinosGroup"
|
name="dinos"
|
||||||
label="Favourite dinosaur"
|
label="Favourite dinosaur"
|
||||||
.validators=${[new Required()]}
|
.validators=${[new Required()]}
|
||||||
>
|
>
|
||||||
<lion-radio name="dinos[]" label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
<lion-radio label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
<lion-radio label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="diplodocus" .choiceValue="${'diplodocus'}"></lion-radio>
|
<lion-radio label="diplodocus" .choiceValue="${'diplodocus'}"></lion-radio>
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
<button @click="${() => validate()}">Validate</button>
|
<button @click="${() => validate()}">Validate</button>
|
||||||
`;
|
`;
|
||||||
|
|
@ -154,14 +154,14 @@ const validate = () => {
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<lion-radio-group
|
<lion-radio-group
|
||||||
id="dinosGroup"
|
id="dinos"
|
||||||
name="dinosGroup"
|
name="dinos"
|
||||||
label="Favourite dinosaur"
|
label="Favourite dinosaur"
|
||||||
.validators=${[new Required()]}
|
.validators=${[new Required()]}
|
||||||
>
|
>
|
||||||
<lion-radio name="dinos[]" label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
<lion-radio label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
<lion-radio label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="diplodocus" .choiceValue="${'diplodocus'}"></lion-radio>
|
<lion-radio label="diplodocus" .choiceValue="${'diplodocus'}"></lion-radio>
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
<button @click="${() => validate()}">Validate</button>
|
<button @click="${() => validate()}">Validate</button>
|
||||||
```
|
```
|
||||||
|
|
@ -176,28 +176,26 @@ You can also create a validator that validates whether a certain option is check
|
||||||
this.name = 'IsBrontosaurus';
|
this.name = 'IsBrontosaurus';
|
||||||
}
|
}
|
||||||
execute(value) {
|
execute(value) {
|
||||||
const selectedValue = value['dinos[]'].find(v => v.checked === true);
|
return value === 'brontosaurus';
|
||||||
const hasError = selectedValue ? selectedValue.value !== 'brontosaurus' : false;
|
|
||||||
return hasError;
|
|
||||||
}
|
}
|
||||||
static async getMessage() {
|
static async getMessage() {
|
||||||
return 'You need to select "brontosaurus"';
|
return 'You need to select "brontosaurus"';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const validate = () => {
|
const validate = () => {
|
||||||
const radioGroup = document.querySelector('#dinosGroupTwo');
|
const radioGroup = document.querySelector('#dinosTwo');
|
||||||
radioGroup.submitted = !radioGroup.submitted;
|
radioGroup.submitted = !radioGroup.submitted;
|
||||||
};
|
};
|
||||||
return html`
|
return html`
|
||||||
<lion-radio-group
|
<lion-radio-group
|
||||||
id="dinosGroupTwo"
|
id="dinosTwo"
|
||||||
name="dinosGroup"
|
name="dinos"
|
||||||
label="Favourite dinosaur"
|
label="Favourite dinosaur"
|
||||||
.validators=${[new Required(), new IsBrontosaurus()]}
|
.validators=${[new Required(), new IsBrontosaurus()]}
|
||||||
>
|
>
|
||||||
<lion-radio name="dinos[]" label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
<lion-radio label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
<lion-radio label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
<lion-radio label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
<button @click="${() => validate()}">Validate</button>
|
<button @click="${() => validate()}">Validate</button>
|
||||||
`;
|
`;
|
||||||
|
|
@ -213,9 +211,7 @@ class IsBrontosaurus extends Validator {
|
||||||
this.name = 'IsBrontosaurus';
|
this.name = 'IsBrontosaurus';
|
||||||
}
|
}
|
||||||
execute(value) {
|
execute(value) {
|
||||||
const selectedValue = value['dinos[]'].find(v => v.checked === true);
|
return value === 'brontosaurus';
|
||||||
const hasError = selectedValue ? selectedValue.value !== 'brontosaurus' : false;
|
|
||||||
return hasError;
|
|
||||||
}
|
}
|
||||||
static async getMessage() {
|
static async getMessage() {
|
||||||
return 'You need to select "brontosaurus"';
|
return 'You need to select "brontosaurus"';
|
||||||
|
|
@ -230,13 +226,13 @@ const validate = () => {
|
||||||
```html
|
```html
|
||||||
<lion-radio-group
|
<lion-radio-group
|
||||||
id="dinosGroupTwo"
|
id="dinosGroupTwo"
|
||||||
name="dinosGroup"
|
name="dinos"
|
||||||
label="Favourite dinosaur"
|
label="Favourite dinosaur"
|
||||||
.validators=${[new Required(), new IsBrontosaurus()]}
|
.validators=${[new Required(), new IsBrontosaurus()]}
|
||||||
>
|
>
|
||||||
<lion-radio name="dinos[]" label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
<lion-radio label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
<lion-radio label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
<lion-radio label="diplodocus" .choiceValue=${'diplodocus'}></lion-radio>
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
<button @click="${() => validate()}">Validate</button>
|
<button @click="${() => validate()}">Validate</button>
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,194 +1,45 @@
|
||||||
import { expect, fixture, nextFrame, html } from '@open-wc/testing';
|
|
||||||
import { Required } from '@lion/validate';
|
|
||||||
import '@lion/radio/lion-radio.js';
|
import '@lion/radio/lion-radio.js';
|
||||||
|
import { expect, fixture, html, nextFrame } from '@open-wc/testing';
|
||||||
import '../lion-radio-group.js';
|
import '../lion-radio-group.js';
|
||||||
|
|
||||||
describe('<lion-radio-group>', () => {
|
describe('<lion-radio-group>', () => {
|
||||||
it('has a single checkedValue representing the currently checked radio value', async () => {
|
it('should have role = radiogroup', async () => {
|
||||||
const el = await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-radio-group>
|
<lion-radio-group label="Gender" name="gender">
|
||||||
<lion-radio name="gender[]" .choiceValue=${'male'}></lion-radio>
|
<lion-radio label="male" value="male"></lion-radio>
|
||||||
<lion-radio name="gender[]" .choiceValue=${'female'} checked></lion-radio>
|
<lion-radio label="female" value="female" checked></lion-radio>
|
||||||
<lion-radio name="gender[]" .choiceValue=${'alien'}></lion-radio>
|
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
`);
|
`);
|
||||||
await nextFrame();
|
await nextFrame();
|
||||||
expect(el.checkedValue).to.equal('female');
|
expect(el.getAttribute('role')).to.equal('radiogroup');
|
||||||
el.formElementsArray[0].checked = true;
|
|
||||||
expect(el.checkedValue).to.equal('male');
|
|
||||||
el.formElementsArray[2].checked = true;
|
|
||||||
expect(el.checkedValue).to.equal('alien');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can set initial checkedValue on creation', async () => {
|
it(`becomes "touched" once a single element of the group changes`, async () => {
|
||||||
const checkedValue = 'alien';
|
|
||||||
const el = await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-radio-group .checkedValue="${checkedValue}">
|
<lion-radio-group name="myGroup">
|
||||||
<lion-radio name="gender[]" .choiceValue=${'male'}></lion-radio>
|
<lion-radio></lion-radio>
|
||||||
<lion-radio name="gender[]" .choiceValue=${'female'}></lion-radio>
|
<lion-radio></lion-radio>
|
||||||
<lion-radio name="gender[]" .choiceValue=${'alien'}></lion-radio>
|
|
||||||
</lion-radio-group>
|
|
||||||
`);
|
|
||||||
|
|
||||||
await nextFrame();
|
|
||||||
await el.registrationReady;
|
|
||||||
await el.updateComplete;
|
|
||||||
|
|
||||||
expect(el.checkedValue).to.equal('alien');
|
|
||||||
expect(el.formElementsArray[2].checked).to.be.true;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can handle complex data via choiceValue', async () => {
|
|
||||||
const date = new Date(2018, 11, 24, 10, 33, 30, 0);
|
|
||||||
|
|
||||||
const el = await fixture(html`
|
|
||||||
<lion-radio-group>
|
|
||||||
<lion-radio name="data[]" .choiceValue=${{ some: 'data' }}></lion-radio>
|
|
||||||
<lion-radio name="data[]" .choiceValue=${date} checked></lion-radio>
|
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
`);
|
`);
|
||||||
await nextFrame();
|
await nextFrame();
|
||||||
|
|
||||||
expect(el.checkedValue).to.equal(date);
|
el.children[1].focus();
|
||||||
el.formElementsArray[0].checked = true;
|
expect(el.touched).to.equal(false, 'initially, touched state is false');
|
||||||
expect(el.checkedValue).to.deep.equal({ some: 'data' });
|
el.children[1].checked = true;
|
||||||
});
|
expect(el.touched, `focused via a mouse click, group should be touched`).to.be.true;
|
||||||
|
|
||||||
it('can handle 0 and empty string as valid values ', async () => {
|
|
||||||
const el = await fixture(html`
|
|
||||||
<lion-radio-group>
|
|
||||||
<lion-radio name="data[]" .choiceValue=${0} checked></lion-radio>
|
|
||||||
<lion-radio name="data[]" .choiceValue=${''}></lion-radio>
|
|
||||||
</lion-radio-group>
|
|
||||||
`);
|
|
||||||
await nextFrame();
|
|
||||||
|
|
||||||
expect(el.checkedValue).to.equal(0);
|
|
||||||
el.formElementsArray[1].checked = true;
|
|
||||||
expect(el.checkedValue).to.equal('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('still has a full modelValue ', async () => {
|
|
||||||
const el = await fixture(html`
|
|
||||||
<lion-radio-group>
|
|
||||||
<lion-radio name="gender[]" .choiceValue=${'male'}></lion-radio>
|
|
||||||
<lion-radio name="gender[]" .choiceValue=${'female'} checked></lion-radio>
|
|
||||||
<lion-radio name="gender[]" .choiceValue=${'alien'}></lion-radio>
|
|
||||||
</lion-radio-group>
|
|
||||||
`);
|
|
||||||
await nextFrame();
|
|
||||||
expect(el.modelValue).to.deep.equal({
|
|
||||||
'gender[]': [
|
|
||||||
{ value: 'male', checked: false },
|
|
||||||
{ value: 'female', checked: true },
|
|
||||||
{ value: 'alien', checked: false },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can check a radio by supplying an available checkedValue', async () => {
|
|
||||||
const el = await fixture(html`
|
|
||||||
<lion-radio-group>
|
|
||||||
<lion-radio name="gender[]" .modelValue="${{ value: 'male', checked: false }}"></lion-radio>
|
|
||||||
<lion-radio
|
|
||||||
name="gender[]"
|
|
||||||
.modelValue="${{ value: 'female', checked: true }}"
|
|
||||||
></lion-radio>
|
|
||||||
<lion-radio
|
|
||||||
name="gender[]"
|
|
||||||
.modelValue="${{ value: 'alien', checked: false }}"
|
|
||||||
></lion-radio>
|
|
||||||
</lion-radio-group>
|
|
||||||
`);
|
|
||||||
await nextFrame();
|
|
||||||
expect(el.checkedValue).to.equal('female');
|
|
||||||
el.checkedValue = 'alien';
|
|
||||||
expect(el.formElementsArray[2].checked).to.be.true;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fires checked-value-changed event only once per checked change', async () => {
|
|
||||||
let counter = 0;
|
|
||||||
const el = await fixture(html`
|
|
||||||
<lion-radio-group
|
|
||||||
@checked-value-changed=${() => {
|
|
||||||
counter += 1;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<lion-radio name="gender[]" .choiceValue=${'male'}></lion-radio>
|
|
||||||
<lion-radio name="gender[]" .modelValue=${{ value: 'female', checked: true }}></lion-radio>
|
|
||||||
<lion-radio name="gender[]" .choiceValue=${'alien'}></lion-radio>
|
|
||||||
</lion-radio-group>
|
|
||||||
`);
|
|
||||||
await nextFrame();
|
|
||||||
expect(counter).to.equal(0);
|
|
||||||
|
|
||||||
el.formElementsArray[0].checked = true;
|
|
||||||
expect(counter).to.equal(1);
|
|
||||||
|
|
||||||
// not changed values trigger no event
|
|
||||||
el.formElementsArray[0].checked = true;
|
|
||||||
expect(counter).to.equal(1);
|
|
||||||
|
|
||||||
el.formElementsArray[2].checked = true;
|
|
||||||
expect(counter).to.equal(2);
|
|
||||||
|
|
||||||
// not found values trigger no event
|
|
||||||
el.checkedValue = 'foo';
|
|
||||||
expect(counter).to.equal(2);
|
|
||||||
|
|
||||||
el.checkedValue = 'male';
|
|
||||||
expect(counter).to.equal(3);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('expect child nodes to only fire one model-value-changed event per instance', async () => {
|
|
||||||
let counter = 0;
|
|
||||||
const el = await fixture(html`
|
|
||||||
<lion-radio-group
|
|
||||||
@model-value-changed=${() => {
|
|
||||||
counter += 1;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<lion-radio name="gender[]" .choiceValue=${'male'}></lion-radio>
|
|
||||||
<lion-radio name="gender[]" .modelValue=${{ value: 'female', checked: true }}></lion-radio>
|
|
||||||
<lion-radio name="gender[]" .choiceValue=${'alien'}></lion-radio>
|
|
||||||
</lion-radio-group>
|
|
||||||
`);
|
|
||||||
await nextFrame();
|
|
||||||
counter = 0; // reset after setup which may result in different results
|
|
||||||
|
|
||||||
el.formElementsArray[0].checked = true;
|
|
||||||
expect(counter).to.equal(2); // male becomes checked, female becomes unchecked
|
|
||||||
|
|
||||||
// not changed values trigger no event
|
|
||||||
el.formElementsArray[0].checked = true;
|
|
||||||
expect(counter).to.equal(2);
|
|
||||||
|
|
||||||
el.formElementsArray[2].checked = true;
|
|
||||||
expect(counter).to.equal(4); // alien becomes checked, male becomes unchecked
|
|
||||||
|
|
||||||
// not found values trigger no event
|
|
||||||
el.checkedValue = 'foo';
|
|
||||||
expect(counter).to.equal(4);
|
|
||||||
|
|
||||||
el.checkedValue = 'male';
|
|
||||||
expect(counter).to.equal(6); // male becomes checked, alien becomes unchecked
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows selection of only one radio in a named group', async () => {
|
it('allows selection of only one radio in a named group', async () => {
|
||||||
const el = await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-radio-group>
|
<lion-radio-group name="gender">
|
||||||
<lion-radio name="gender[]" .modelValue="${{ value: 'male', checked: false }}"></lion-radio>
|
<lion-radio .modelValue="${{ value: 'male', checked: false }}"></lion-radio>
|
||||||
<lion-radio
|
<lion-radio .modelValue="${{ value: 'female', checked: false }}"></lion-radio>
|
||||||
name="gender[]"
|
|
||||||
.modelValue="${{ value: 'female', checked: false }}"
|
|
||||||
></lion-radio>
|
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
`);
|
`);
|
||||||
await nextFrame();
|
await nextFrame();
|
||||||
const male = el.formElements['gender[]'][0];
|
const male = el.formElementsArray[0];
|
||||||
const maleInput = male.querySelector('input');
|
const maleInput = male.querySelector('input');
|
||||||
const female = el.formElements['gender[]'][1];
|
const female = el.formElementsArray[1];
|
||||||
const femaleInput = female.querySelector('input');
|
const femaleInput = female.querySelector('input');
|
||||||
|
|
||||||
expect(male.checked).to.equal(false);
|
expect(male.checked).to.equal(false);
|
||||||
|
|
@ -205,95 +56,11 @@ describe('<lion-radio-group>', () => {
|
||||||
expect(female.checked).to.equal(true);
|
expect(female.checked).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have role = radiogroup', async () => {
|
|
||||||
const el = await fixture(html`
|
|
||||||
<lion-radio-group>
|
|
||||||
<label slot="label">My group</label>
|
|
||||||
<lion-radio name="gender[]" value="male">
|
|
||||||
<label slot="label">male</label>
|
|
||||||
</lion-radio>
|
|
||||||
<lion-radio name="gender[]" value="female">
|
|
||||||
<label slot="label">female</label>
|
|
||||||
</lion-radio>
|
|
||||||
</lion-radio-group>
|
|
||||||
`);
|
|
||||||
await nextFrame();
|
|
||||||
expect(el.getAttribute('role')).to.equal('radiogroup');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can be required', async () => {
|
|
||||||
const el = await fixture(html`
|
|
||||||
<lion-radio-group .validators=${[new Required()]}>
|
|
||||||
<lion-radio name="gender[]" .choiceValue=${'male'}></lion-radio>
|
|
||||||
<lion-radio
|
|
||||||
name="gender[]"
|
|
||||||
.choiceValue=${{ subObject: 'satisfies required' }}
|
|
||||||
></lion-radio>
|
|
||||||
</lion-radio-group>
|
|
||||||
`);
|
|
||||||
expect(el.hasFeedbackFor).to.include('error');
|
|
||||||
expect(el.validationStates).to.have.a.property('error');
|
|
||||||
expect(el.validationStates.error).to.have.a.property('Required');
|
|
||||||
|
|
||||||
el.formElements['gender[]'][0].checked = true;
|
|
||||||
expect(el.hasFeedbackFor).not.to.include('error');
|
|
||||||
expect(el.validationStates).to.have.a.property('error');
|
|
||||||
expect(el.validationStates.error).not.to.have.a.property('Required');
|
|
||||||
|
|
||||||
el.formElements['gender[]'][1].checked = true;
|
|
||||||
expect(el.hasFeedbackFor).not.to.include('error');
|
|
||||||
expect(el.validationStates).to.have.a.property('error');
|
|
||||||
expect(el.validationStates.error).not.to.have.a.property('Required');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns serialized value', async () => {
|
|
||||||
const el = await fixture(html`
|
|
||||||
<lion-radio-group>
|
|
||||||
<lion-radio name="gender[]" .choiceValue=${'male'}></lion-radio>
|
|
||||||
<lion-radio name="gender[]" .choiceValue=${'female'}></lion-radio>
|
|
||||||
</lion-radio-group>
|
|
||||||
`);
|
|
||||||
el.formElements['gender[]'][0].checked = true;
|
|
||||||
expect(el.serializedValue).to.deep.equal({ checked: true, value: 'male' });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns serialized value on unchecked state', async () => {
|
|
||||||
const el = await fixture(html`
|
|
||||||
<lion-radio-group>
|
|
||||||
<lion-radio name="gender[]" .choiceValue=${'male'}></lion-radio>
|
|
||||||
<lion-radio name="gender[]" .choiceValue=${'female'}></lion-radio>
|
|
||||||
</lion-radio-group>
|
|
||||||
`);
|
|
||||||
await nextFrame();
|
|
||||||
|
|
||||||
expect(el.serializedValue).to.deep.equal('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`becomes "touched" once a single element of the group changes`, async () => {
|
|
||||||
const el = await fixture(html`
|
|
||||||
<lion-radio-group>
|
|
||||||
<lion-radio name="myGroup[]"></lion-radio>
|
|
||||||
<lion-radio name="myGroup[]"></lion-radio>
|
|
||||||
</lion-radio-group>
|
|
||||||
`);
|
|
||||||
await nextFrame();
|
|
||||||
|
|
||||||
el.children[1].focus();
|
|
||||||
expect(el.touched).to.equal(false, 'initially, touched state is false');
|
|
||||||
el.children[1].checked = true;
|
|
||||||
expect(el.touched, `focused via a mouse click, group should be touched`).to.be.true;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('is accessible', async () => {
|
it('is accessible', async () => {
|
||||||
const el = await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-radio-group>
|
<lion-radio-group label="My group" name="gender">
|
||||||
<label slot="label">My group</label>
|
<lion-radio label="male" value="male"></lion-radio>
|
||||||
<lion-radio name="gender[]" value="male">
|
<lion-radio label="female" value="female" checked></lion-radio>
|
||||||
<label slot="label">male</label>
|
|
||||||
</lion-radio>
|
|
||||||
<lion-radio name="gender[]" value="female" checked>
|
|
||||||
<label slot="label">female</label>
|
|
||||||
</lion-radio>
|
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
`);
|
`);
|
||||||
await nextFrame();
|
await nextFrame();
|
||||||
|
|
@ -302,14 +69,9 @@ describe('<lion-radio-group>', () => {
|
||||||
|
|
||||||
it('is accessible when the group is disabled', async () => {
|
it('is accessible when the group is disabled', async () => {
|
||||||
const el = await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-radio-group disabled>
|
<lion-radio-group label="My group" name="gender" disabled>
|
||||||
<label slot="label">My group</label>
|
<lion-radio label="male" value="male"></lion-radio>
|
||||||
<lion-radio name="gender[]" value="male">
|
<lion-radio label="female" value="female" checked></lion-radio>
|
||||||
<label slot="label">male</label>
|
|
||||||
</lion-radio>
|
|
||||||
<lion-radio name="gender[]" value="female">
|
|
||||||
<label slot="label">female</label>
|
|
||||||
</lion-radio>
|
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
`);
|
`);
|
||||||
await nextFrame();
|
await nextFrame();
|
||||||
|
|
@ -318,14 +80,9 @@ describe('<lion-radio-group>', () => {
|
||||||
|
|
||||||
it('is accessible when an option is disabled', async () => {
|
it('is accessible when an option is disabled', async () => {
|
||||||
const el = await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-radio-group>
|
<lion-radio-group label="My group" name="gender">
|
||||||
<label slot="label">My group</label>
|
<lion-radio label="male" value="male" disabled></lion-radio>
|
||||||
<lion-radio name="gender[]" value="male" disabled>
|
<lion-radio label="female" value="female" checked></lion-radio>
|
||||||
<label slot="label">male</label>
|
|
||||||
</lion-radio>
|
|
||||||
<lion-radio name="gender[]" value="female">
|
|
||||||
<label slot="label">female</label>
|
|
||||||
</lion-radio>
|
|
||||||
</lion-radio-group>
|
</lion-radio-group>
|
||||||
`);
|
`);
|
||||||
await nextFrame();
|
await nextFrame();
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,22 @@
|
||||||
import { LionInput } from '@lion/input';
|
|
||||||
import { ChoiceInputMixin } from '@lion/choice-input';
|
import { ChoiceInputMixin } from '@lion/choice-input';
|
||||||
|
import { LionInput } from '@lion/input';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lion-radio can be used inside a lion-radio-group.
|
* Lion-radio can be used inside a lion-radio-group.
|
||||||
*
|
*
|
||||||
* <lion-radio-group>
|
* <lion-radio-group name="radios">
|
||||||
* <label slot="label">My Radio</label>
|
* <label slot="label">My Radio</label>
|
||||||
* <lion-radio name="name[]">
|
* <lion-radio>
|
||||||
* <label slot="label">Male</label>
|
* <label slot="label">Male</label>
|
||||||
* </lion-radio>
|
* </lion-radio>
|
||||||
* <lion-radio name="name[]">
|
* <lion-radio>
|
||||||
* <label slot="label">Female</label>
|
* <label slot="label">Female</label>
|
||||||
* </lion-radio>
|
* </lion-radio>
|
||||||
* </lion-radio-group>
|
* </lion-radio-group>
|
||||||
*
|
*
|
||||||
* You can preselect an option by setting marking an lion-radio checked.
|
* You can preselect an option by setting marking an lion-radio checked.
|
||||||
* Example:
|
* Example:
|
||||||
* <lion-radio name="name[]" checked>
|
* <lion-radio checked>
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @customElement lion-radio
|
* @customElement lion-radio
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,19 @@ import '../lion-radio.js';
|
||||||
`lion-radio` component is a sub-element to be used in [lion-radio-group](?path=/docs/forms-radio-group--default-story) elements. Its purpose is to provide a way for users to check a **single** option amongst a set of choices.
|
`lion-radio` component is a sub-element to be used in [lion-radio-group](?path=/docs/forms-radio-group--default-story) elements. Its purpose is to provide a way for users to check a **single** option amongst a set of choices.
|
||||||
|
|
||||||
<Story name="Default">{html`
|
<Story name="Default">{html`
|
||||||
<lion-radio name="dinos[]" label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
<lion-radio-group name="dinos">
|
||||||
<lion-radio name="dinos[]" label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
<lion-radio label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="diplodocus" .choiceValue=${'diplodocus'} checked></lion-radio>
|
<lion-radio label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
||||||
|
<lion-radio label="diplodocus" .choiceValue=${'diplodocus'} checked></lion-radio>
|
||||||
|
</lion-radio-group>
|
||||||
`}</Story>
|
`}</Story>
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<lion-radio name="dinos[]" label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
<lion-radio-group name="dinos">
|
||||||
<lion-radio name="dinos[]" label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
<lion-radio label="allosaurus" .choiceValue=${'allosaurus'}></lion-radio>
|
||||||
<lion-radio name="dinos[]" label="diplodocus" .choiceValue=${'diplodocus'} checked></lion-radio>
|
<lion-radio label="brontosaurus" .choiceValue=${'brontosaurus'}></lion-radio>
|
||||||
|
<lion-radio label="diplodocus" .choiceValue=${'diplodocus'} checked></lion-radio>
|
||||||
|
</lion-radio-group>
|
||||||
```
|
```
|
||||||
|
|
||||||
- Use this component inside a [lion-radio-group](?path=/docs/forms-radio-group--default-story)
|
- Use this component inside a [lion-radio-group](?path=/docs/forms-radio-group--default-story)
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,12 @@
|
||||||
import { expect, fixture, nextFrame } from '@open-wc/testing';
|
import { expect, fixture, nextFrame } from '@open-wc/testing';
|
||||||
|
|
||||||
import '../lion-radio.js';
|
import '../lion-radio.js';
|
||||||
|
|
||||||
describe('<lion-radio>', () => {
|
describe('<lion-radio>', () => {
|
||||||
it('should have type = radio', async () => {
|
it('should have type = radio', async () => {
|
||||||
const el = await fixture(`
|
const el = await fixture(`
|
||||||
<lion-radio-group>
|
<lion-radio name="radio" value="male"></lion-radio>
|
||||||
<label slot="label">My group</label>
|
|
||||||
<lion-radio name="gender[]" value="male">
|
|
||||||
<label slot="label">male</label>
|
|
||||||
</lion-radio>
|
|
||||||
<lion-radio name="gender[]" value="female">
|
|
||||||
<label slot="label">female</label>
|
|
||||||
</lion-radio>
|
|
||||||
</lion-radio-group>
|
|
||||||
`);
|
`);
|
||||||
await nextFrame();
|
await nextFrame();
|
||||||
expect(el.children[1]._inputNode.getAttribute('type')).to.equal('radio');
|
expect(el.getAttribute('type')).to.equal('radio');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lion/button": "0.5.4",
|
"@lion/button": "0.5.4",
|
||||||
|
"@lion/choice-input": "0.5.9",
|
||||||
"@lion/core": "0.4.3",
|
"@lion/core": "0.4.3",
|
||||||
"@lion/field": "0.8.9",
|
"@lion/field": "0.8.9",
|
||||||
"@lion/option": "0.4.9",
|
"@lion/option": "0.4.9",
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import { html, css, LitElement, SlotMixin } from '@lion/core';
|
import { ChoiceGroupMixin } from '@lion/choice-input';
|
||||||
import { withDropdownConfig, OverlayMixin } from '@lion/overlays';
|
import { css, html, LitElement, SlotMixin } from '@lion/core';
|
||||||
import { FormControlMixin, InteractionStateMixin, FormRegistrarMixin } from '@lion/field';
|
import { FormControlMixin, FormRegistrarMixin, InteractionStateMixin } from '@lion/field';
|
||||||
|
import { formRegistrarManager } from '@lion/field/src/formRegistrarManager.js';
|
||||||
|
import { OverlayMixin, withDropdownConfig } from '@lion/overlays';
|
||||||
import { ValidateMixin } from '@lion/validate';
|
import { ValidateMixin } from '@lion/validate';
|
||||||
import './differentKeyNamesShimIE.js';
|
|
||||||
|
|
||||||
import '../lion-select-invoker.js';
|
import '../lion-select-invoker.js';
|
||||||
|
import './differentKeyNamesShimIE.js';
|
||||||
|
|
||||||
function uuid() {
|
function uuid() {
|
||||||
return Math.random()
|
return Math.random()
|
||||||
|
|
@ -45,15 +46,15 @@ function isInView(container, element, partial = false) {
|
||||||
* @customElement lion-select-rich
|
* @customElement lion-select-rich
|
||||||
* @extends {LitElement}
|
* @extends {LitElement}
|
||||||
*/
|
*/
|
||||||
export class LionSelectRich extends OverlayMixin(
|
export class LionSelectRich extends ChoiceGroupMixin(
|
||||||
FormRegistrarMixin(InteractionStateMixin(ValidateMixin(FormControlMixin(SlotMixin(LitElement))))),
|
OverlayMixin(
|
||||||
|
FormRegistrarMixin(
|
||||||
|
InteractionStateMixin(ValidateMixin(FormControlMixin(SlotMixin(LitElement)))),
|
||||||
|
),
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
checkedValue: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
|
|
||||||
disabled: {
|
disabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
reflect: true,
|
reflect: true,
|
||||||
|
|
@ -70,10 +71,6 @@ export class LionSelectRich extends OverlayMixin(
|
||||||
attribute: 'interaction-mode',
|
attribute: 'interaction-mode',
|
||||||
},
|
},
|
||||||
|
|
||||||
modelValue: {
|
|
||||||
type: Array,
|
|
||||||
},
|
|
||||||
|
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
|
|
@ -94,22 +91,6 @@ export class LionSelectRich extends OverlayMixin(
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @override
|
|
||||||
*/
|
|
||||||
static _isPrefilled(modelValue) {
|
|
||||||
if (!modelValue) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const checkedModelValue = modelValue.find(subModelValue => subModelValue.checked === true);
|
|
||||||
if (!checkedModelValue) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { value } = checkedModelValue;
|
|
||||||
return super._isPrefilled(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
get slots() {
|
get slots() {
|
||||||
return {
|
return {
|
||||||
...super.slots,
|
...super.slots,
|
||||||
|
|
@ -132,16 +113,38 @@ export class LionSelectRich extends OverlayMixin(
|
||||||
return this._listboxNode.querySelector(`#${this._listboxActiveDescendant}`);
|
return this._listboxNode.querySelector(`#${this._listboxActiveDescendant}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
get checkedIndex() {
|
get modelValue() {
|
||||||
if (this.modelValue) {
|
const el = this.formElements.find(option => option.checked);
|
||||||
return this.modelValue.findIndex(el => el.value === this.checkedValue);
|
return el ? el.modelValue.value : '';
|
||||||
}
|
}
|
||||||
return -1;
|
|
||||||
|
set modelValue(value) {
|
||||||
|
const el = this.formElements.find(option => option.modelValue.value === value);
|
||||||
|
|
||||||
|
if (el) {
|
||||||
|
el.checked = true;
|
||||||
|
} else {
|
||||||
|
// cache user set modelValue, and then try it again when registration is done
|
||||||
|
this.__cachedUserSetModelValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.__syncInvokerElement();
|
||||||
|
this.requestUpdate('modelValue');
|
||||||
|
}
|
||||||
|
|
||||||
|
get checkedIndex() {
|
||||||
|
let checkedIndex = -1;
|
||||||
|
this.formElements.forEach((option, i) => {
|
||||||
|
if (option.checked) {
|
||||||
|
checkedIndex = i;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return checkedIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
set checkedIndex(index) {
|
set checkedIndex(index) {
|
||||||
if (this.formElements[index]) {
|
if (this._listboxNode.children[index]) {
|
||||||
this.formElements[index].checked = true;
|
this._listboxNode.children[index].checked = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,8 +172,6 @@ export class LionSelectRich extends OverlayMixin(
|
||||||
this.interactionMode = 'auto';
|
this.interactionMode = 'auto';
|
||||||
this.disabled = false;
|
this.disabled = false;
|
||||||
// for interaction states
|
// for interaction states
|
||||||
// we use a different event as 'model-value-changed' would bubble up from all options
|
|
||||||
this._valueChangedEvent = 'select-model-value-changed';
|
|
||||||
this._listboxActiveDescendant = null;
|
this._listboxActiveDescendant = null;
|
||||||
this.__hasInitialSelectedFormElement = false;
|
this.__hasInitialSelectedFormElement = false;
|
||||||
|
|
||||||
|
|
@ -200,31 +201,19 @@ export class LionSelectRich extends OverlayMixin(
|
||||||
this.__setupInvokerNode();
|
this.__setupInvokerNode();
|
||||||
this.__setupListboxNode();
|
this.__setupListboxNode();
|
||||||
|
|
||||||
this._invokerNode.selectedElement = this.formElements[this.checkedIndex];
|
formRegistrarManager.addEventListener('all-forms-open-for-registration', () => {
|
||||||
|
// Now that we have rendered + registered our listbox, try setting the user defined modelValue again
|
||||||
|
if (this.__cachedUserSetModelValue) {
|
||||||
|
this.modelValue = this.__cachedUserSetModelValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._invokerNode.selectedElement = this.formElements[this.checkedIndex];
|
||||||
this.__toggleInvokerDisabled();
|
this.__toggleInvokerDisabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
_requestUpdate(name, oldValue) {
|
_requestUpdate(name, oldValue) {
|
||||||
super._requestUpdate(name, oldValue);
|
super._requestUpdate(name, oldValue);
|
||||||
if (
|
|
||||||
name === 'checkedValue' &&
|
|
||||||
!this.__isSyncingCheckedAndModelValue &&
|
|
||||||
this.modelValue &&
|
|
||||||
this.modelValue.length > 0
|
|
||||||
) {
|
|
||||||
if (this.checkedIndex) {
|
|
||||||
// Necessary to sync the checkedIndex through the getter/setter explicitly
|
|
||||||
// eslint-disable-next-line no-self-assign
|
|
||||||
this.checkedIndex = this.checkedIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'modelValue') {
|
|
||||||
this.dispatchEvent(new CustomEvent('select-model-value-changed'));
|
|
||||||
this.__onModelValueChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'interactionMode') {
|
if (name === 'interactionMode') {
|
||||||
if (this.interactionMode === 'auto') {
|
if (this.interactionMode === 'auto') {
|
||||||
this.interactionMode = detectInteractionMode();
|
this.interactionMode = detectInteractionMode();
|
||||||
|
|
@ -308,14 +297,11 @@ export class LionSelectRich extends OverlayMixin(
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
* @param {*} child
|
* @param {*} child
|
||||||
|
* @param {Number} indexToInsertAt
|
||||||
*/
|
*/
|
||||||
addFormElement(passedChild) {
|
addFormElement(child, indexToInsertAt) {
|
||||||
const child = passedChild;
|
super.addFormElement(child, indexToInsertAt);
|
||||||
|
|
||||||
// Set the name property on the option elements ourselves, for form serialization
|
|
||||||
child.name = `${this.name}[]`;
|
|
||||||
|
|
||||||
super.addFormElement(child);
|
|
||||||
// we need to adjust the elements being registered
|
// we need to adjust the elements being registered
|
||||||
/* eslint-disable no-param-reassign */
|
/* eslint-disable no-param-reassign */
|
||||||
child.id = child.id || `${this.localName}-option-${uuid()}`;
|
child.id = child.id || `${this.localName}-option-${uuid()}`;
|
||||||
|
|
@ -323,6 +309,7 @@ export class LionSelectRich extends OverlayMixin(
|
||||||
if (this.disabled) {
|
if (this.disabled) {
|
||||||
child.makeRequestToBeDisabled();
|
child.makeRequestToBeDisabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
// the first elements checked by default
|
// the first elements checked by default
|
||||||
if (!this.__hasInitialSelectedFormElement && (!child.disabled || this.disabled)) {
|
if (!this.__hasInitialSelectedFormElement && (!child.disabled || this.disabled)) {
|
||||||
child.active = true;
|
child.active = true;
|
||||||
|
|
@ -338,10 +325,6 @@ export class LionSelectRich extends OverlayMixin(
|
||||||
/* eslint-enable no-param-reassign */
|
/* eslint-enable no-param-reassign */
|
||||||
}
|
}
|
||||||
|
|
||||||
_getFromAllFormElements(property) {
|
|
||||||
return this.formElements.map(e => e[property]);
|
|
||||||
}
|
|
||||||
|
|
||||||
__setupEventListeners() {
|
__setupEventListeners() {
|
||||||
this.__onChildActiveChanged = this.__onChildActiveChanged.bind(this);
|
this.__onChildActiveChanged = this.__onChildActiveChanged.bind(this);
|
||||||
this.__onChildModelValueChanged = this.__onChildModelValueChanged.bind(this);
|
this.__onChildModelValueChanged = this.__onChildModelValueChanged.bind(this);
|
||||||
|
|
@ -391,24 +374,15 @@ export class LionSelectRich extends OverlayMixin(
|
||||||
formElement.checked = false;
|
formElement.checked = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.modelValue = target.value;
|
||||||
}
|
}
|
||||||
this.modelValue = this._getFromAllFormElements('modelValue');
|
|
||||||
}
|
|
||||||
|
|
||||||
__onModelValueChanged() {
|
|
||||||
this.__isSyncingCheckedAndModelValue = true;
|
|
||||||
|
|
||||||
const foundChecked = this.modelValue.find(subModelValue => subModelValue.checked);
|
|
||||||
if (foundChecked && foundChecked.value !== this.checkedValue) {
|
|
||||||
this.checkedValue = foundChecked.value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__syncInvokerElement() {
|
||||||
// sync to invoker
|
// sync to invoker
|
||||||
if (this._invokerNode) {
|
if (this._invokerNode) {
|
||||||
this._invokerNode.selectedElement = this.formElements[this.checkedIndex];
|
this._invokerNode.selectedElement = this.formElements[this.checkedIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.__isSyncingCheckedAndModelValue = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__getNextEnabledOption(currentIndex, offset = 1) {
|
__getNextEnabledOption(currentIndex, offset = 1) {
|
||||||
|
|
@ -659,17 +633,6 @@ export class LionSelectRich extends OverlayMixin(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_isEmpty() {
|
|
||||||
const value = this.checkedValue;
|
|
||||||
if (typeof value === 'string' && value === '') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (value === undefined || value === null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @override Configures OverlayMixin
|
* @override Configures OverlayMixin
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import { expect, fixture, html, triggerFocusFor, triggerBlurFor } from '@open-wc/testing';
|
|
||||||
import './keyboardEventShimIE.js';
|
|
||||||
|
|
||||||
import { Required } from '@lion/validate';
|
|
||||||
import '@lion/option/lion-option.js';
|
import '@lion/option/lion-option.js';
|
||||||
|
import { Required } from '@lion/validate';
|
||||||
|
import { expect, fixture, html, triggerBlurFor, triggerFocusFor } from '@open-wc/testing';
|
||||||
import '../lion-options.js';
|
import '../lion-options.js';
|
||||||
import '../lion-select-rich.js';
|
import '../lion-select-rich.js';
|
||||||
|
import './keyboardEventShimIE.js';
|
||||||
|
|
||||||
describe('lion-select-rich interactions', () => {
|
describe('lion-select-rich interactions', () => {
|
||||||
describe('values', () => {
|
describe('values', () => {
|
||||||
|
|
@ -36,11 +35,7 @@ describe('lion-select-rich interactions', () => {
|
||||||
|
|
||||||
expect(el.querySelector('lion-option').checked).to.be.true;
|
expect(el.querySelector('lion-option').checked).to.be.true;
|
||||||
expect(el.querySelector('lion-option').active).to.be.true;
|
expect(el.querySelector('lion-option').active).to.be.true;
|
||||||
expect(el.modelValue).to.deep.equal([
|
expect(el.modelValue).to.equal(10);
|
||||||
{ value: 10, checked: true },
|
|
||||||
{ value: 20, checked: false },
|
|
||||||
]);
|
|
||||||
expect(el.checkedValue).to.equal(10);
|
|
||||||
|
|
||||||
expect(el.checkedIndex).to.equal(0);
|
expect(el.checkedIndex).to.equal(0);
|
||||||
expect(el.activeIndex).to.equal(0);
|
expect(el.activeIndex).to.equal(0);
|
||||||
|
|
@ -55,11 +50,7 @@ describe('lion-select-rich interactions', () => {
|
||||||
</lion-options>
|
</lion-options>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`);
|
`);
|
||||||
expect(el.modelValue).to.deep.equal([
|
expect(el.modelValue).to.be.null;
|
||||||
{ value: null, checked: true },
|
|
||||||
{ value: 20, checked: false },
|
|
||||||
]);
|
|
||||||
expect(el.checkedValue).to.be.null;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has the checked option as modelValue', async () => {
|
it('has the checked option as modelValue', async () => {
|
||||||
|
|
@ -71,27 +62,7 @@ describe('lion-select-rich interactions', () => {
|
||||||
</lion-options>
|
</lion-options>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`);
|
`);
|
||||||
expect(el.modelValue).to.deep.equal([
|
expect(el.modelValue).to.equal(20);
|
||||||
{ value: 10, checked: false },
|
|
||||||
{ value: 20, checked: true },
|
|
||||||
]);
|
|
||||||
expect(el.checkedValue).to.equal(20);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('syncs checkedValue to modelValue', async () => {
|
|
||||||
const el = await fixture(html`
|
|
||||||
<lion-select-rich>
|
|
||||||
<lion-options slot="input">
|
|
||||||
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
|
||||||
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
|
||||||
</lion-options>
|
|
||||||
</lion-select-rich>
|
|
||||||
`);
|
|
||||||
el.checkedValue = 20;
|
|
||||||
expect(el.modelValue).to.deep.equal([
|
|
||||||
{ value: 10, checked: false },
|
|
||||||
{ value: 20, checked: true },
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has an activeIndex', async () => {
|
it('has an activeIndex', async () => {
|
||||||
|
|
@ -139,13 +110,13 @@ describe('lion-select-rich interactions', () => {
|
||||||
</lion-options>
|
</lion-options>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`);
|
`);
|
||||||
expect(el.checkedValue).to.equal(30);
|
expect(el.modelValue).to.equal(30);
|
||||||
|
|
||||||
el._listboxNode.dispatchEvent(new KeyboardEvent('keyup', { key: 'Home' }));
|
el._listboxNode.dispatchEvent(new KeyboardEvent('keyup', { key: 'Home' }));
|
||||||
expect(el.checkedValue).to.equal(10);
|
expect(el.modelValue).to.equal(10);
|
||||||
|
|
||||||
el._listboxNode.dispatchEvent(new KeyboardEvent('keyup', { key: 'End' }));
|
el._listboxNode.dispatchEvent(new KeyboardEvent('keyup', { key: 'End' }));
|
||||||
expect(el.checkedValue).to.equal(40);
|
expect(el.modelValue).to.equal(40);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: nice to have
|
// TODO: nice to have
|
||||||
|
|
@ -297,7 +268,7 @@ describe('lion-select-rich interactions', () => {
|
||||||
</lion-options>
|
</lion-options>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`);
|
`);
|
||||||
expect(el.checkedValue).to.equal(10);
|
expect(el.modelValue).to.equal(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('cannot be navigated with keyboard if disabled', async () => {
|
it('cannot be navigated with keyboard if disabled', async () => {
|
||||||
|
|
@ -310,7 +281,7 @@ describe('lion-select-rich interactions', () => {
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`);
|
`);
|
||||||
el._listboxNode.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowDown' }));
|
el._listboxNode.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowDown' }));
|
||||||
expect(el.checkedValue).to.equal(10);
|
expect(el.modelValue).to.equal(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('cannot be opened via click if disabled', async () => {
|
it('cannot be opened via click if disabled', async () => {
|
||||||
|
|
@ -477,10 +448,7 @@ describe('lion-select-rich interactions', () => {
|
||||||
`);
|
`);
|
||||||
const option = el.querySelectorAll('lion-option')[1];
|
const option = el.querySelectorAll('lion-option')[1];
|
||||||
option.checked = true;
|
option.checked = true;
|
||||||
expect(el.modelValue).to.deep.equal([
|
expect(el.modelValue).to.equal(20);
|
||||||
{ value: 10, checked: false },
|
|
||||||
{ value: 20, checked: true },
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not allow to set checkedIndex or activeIndex to be out of bound', async () => {
|
it('does not allow to set checkedIndex or activeIndex to be out of bound', async () => {
|
||||||
|
|
@ -543,7 +511,7 @@ describe('lion-select-rich interactions', () => {
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`);
|
`);
|
||||||
expect(el.dirty).to.be.false;
|
expect(el.dirty).to.be.false;
|
||||||
el.checkedValue = 20;
|
el.modelValue = 20;
|
||||||
expect(el.dirty).to.be.true;
|
expect(el.dirty).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -599,7 +567,7 @@ describe('lion-select-rich interactions', () => {
|
||||||
expect(el.validationStates).to.have.a.property('error');
|
expect(el.validationStates).to.have.a.property('error');
|
||||||
expect(el.validationStates.error).to.have.a.property('Required');
|
expect(el.validationStates.error).to.have.a.property('Required');
|
||||||
|
|
||||||
el.checkedValue = 20;
|
el.modelValue = 20;
|
||||||
expect(el.hasFeedbackFor).not.to.include('error');
|
expect(el.hasFeedbackFor).not.to.include('error');
|
||||||
expect(el.validationStates).to.have.a.property('error');
|
expect(el.validationStates).to.have.a.property('error');
|
||||||
expect(el.validationStates.error).not.to.have.a.property('Required');
|
expect(el.validationStates.error).not.to.have.a.property('Required');
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,116 @@
|
||||||
import {
|
|
||||||
expect,
|
|
||||||
fixture,
|
|
||||||
html,
|
|
||||||
aTimeout,
|
|
||||||
defineCE,
|
|
||||||
unsafeStatic,
|
|
||||||
nextFrame,
|
|
||||||
} from '@open-wc/testing';
|
|
||||||
import { LitElement } from '@lion/core';
|
import { LitElement } from '@lion/core';
|
||||||
import '@lion/option/lion-option.js';
|
import '@lion/option/lion-option.js';
|
||||||
import { OverlayController } from '@lion/overlays';
|
import { OverlayController } from '@lion/overlays';
|
||||||
|
import { Required } from '@lion/validate';
|
||||||
import './keyboardEventShimIE.js';
|
import {
|
||||||
|
aTimeout,
|
||||||
|
defineCE,
|
||||||
|
expect,
|
||||||
|
fixture,
|
||||||
|
html,
|
||||||
|
nextFrame,
|
||||||
|
unsafeStatic,
|
||||||
|
} from '@open-wc/testing';
|
||||||
|
import { LionSelectRich } from '../index.js';
|
||||||
import '../lion-options.js';
|
import '../lion-options.js';
|
||||||
import '../lion-select-rich.js';
|
import '../lion-select-rich.js';
|
||||||
import { LionSelectRich } from '../index.js';
|
import './keyboardEventShimIE.js';
|
||||||
|
|
||||||
describe('lion-select-rich', () => {
|
describe('lion-select-rich', () => {
|
||||||
|
it('has a single modelValue representing the currently checked option', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<lion-select-rich name="foo">
|
||||||
|
<lion-options slot="input">
|
||||||
|
<lion-option .choiceValue=${10} checked>Item 1</lion-option>
|
||||||
|
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
||||||
|
</lion-options>
|
||||||
|
</lion-select-rich>
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(el.modelValue).to.equal(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('automatically sets the name attribute of child checkboxes to its own name', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<lion-select-rich name="foo">
|
||||||
|
<lion-options slot="input">
|
||||||
|
<lion-option .choiceValue=${10} checked>Item 1</lion-option>
|
||||||
|
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
||||||
|
</lion-options>
|
||||||
|
</lion-select-rich>
|
||||||
|
`);
|
||||||
|
await nextFrame();
|
||||||
|
|
||||||
|
expect(el.formElementsArray[0].name).to.equal('foo');
|
||||||
|
expect(el.formElementsArray[1].name).to.equal('foo');
|
||||||
|
|
||||||
|
const validChild = await fixture(html`
|
||||||
|
<lion-option .choiceValue=${30}>Item 3</lion-option>
|
||||||
|
`);
|
||||||
|
el.appendChild(validChild);
|
||||||
|
|
||||||
|
expect(el.formElementsArray[2].name).to.equal('foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if a child element without a modelValue like { value: "foo", checked: false } tries to register', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<lion-select-rich name="foo">
|
||||||
|
<lion-options slot="input">
|
||||||
|
<lion-option .choiceValue=${10} checked>Item 1</lion-option>
|
||||||
|
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
||||||
|
</lion-options>
|
||||||
|
</lion-select-rich>
|
||||||
|
`);
|
||||||
|
await nextFrame();
|
||||||
|
|
||||||
|
const invalidChild = await fixture(html`
|
||||||
|
<lion-option .modelValue=${'Lara'}></lion-option>
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
el.addFormElement(invalidChild);
|
||||||
|
}).to.throw(
|
||||||
|
'The lion-select-rich name="foo" does not allow to register lion-option with .modelValue="Lara" - The modelValue should represent an Object { value: "foo", checked: false }',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if a child element with a different name than the group tries to register', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<lion-select-rich name="gender">
|
||||||
|
<lion-options slot="input">
|
||||||
|
<lion-option .choiceValue=${'female'} checked></lion-option>
|
||||||
|
<lion-option .choiceValue=${'other'}></lion-option>
|
||||||
|
</lion-options>
|
||||||
|
</lion-select-rich>
|
||||||
|
`);
|
||||||
|
await nextFrame();
|
||||||
|
|
||||||
|
const invalidChild = await fixture(html`
|
||||||
|
<lion-option name="foo" .choiceValue=${'male'}></lion-option>
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
el.addFormElement(invalidChild);
|
||||||
|
}).to.throw(
|
||||||
|
'The lion-select-rich name="gender" does not allow to register lion-option with custom names (name="foo" given)',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can set initial modelValue on creation', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<lion-select-rich name="gender" .modelValue=${'other'}>
|
||||||
|
<lion-options slot="input">
|
||||||
|
<lion-option .choiceValue=${'male'}></lion-option>
|
||||||
|
<lion-option .choiceValue=${'female'}></lion-option>
|
||||||
|
<lion-option .choiceValue=${'other'}></lion-option>
|
||||||
|
</lion-options>
|
||||||
|
</lion-select-rich>
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(el.modelValue).to.equal('other');
|
||||||
|
expect(el.formElementsArray[2].checked).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
it(`has a fieldName based on the label`, async () => {
|
it(`has a fieldName based on the label`, async () => {
|
||||||
const el1 = await fixture(
|
const el1 = await fixture(
|
||||||
html`
|
html`
|
||||||
|
|
@ -77,8 +171,41 @@ describe('lion-select-rich', () => {
|
||||||
const optOne = el.querySelectorAll('lion-option')[0];
|
const optOne = el.querySelectorAll('lion-option')[0];
|
||||||
const optTwo = el.querySelectorAll('lion-option')[1];
|
const optTwo = el.querySelectorAll('lion-option')[1];
|
||||||
|
|
||||||
expect(optOne.name).to.equal('foo[]');
|
expect(optOne.name).to.equal('foo');
|
||||||
expect(optTwo.name).to.equal('foo[]');
|
expect(optTwo.name).to.equal('foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports validation', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<lion-select-rich
|
||||||
|
id="color"
|
||||||
|
name="color"
|
||||||
|
label="Favorite color"
|
||||||
|
.validators="${[new Required()]}"
|
||||||
|
>
|
||||||
|
<lion-options slot="input">
|
||||||
|
<lion-option .choiceValue=${null}>select a color</lion-option>
|
||||||
|
<lion-option .choiceValue=${'red'}>Red</lion-option>
|
||||||
|
<lion-option .choiceValue=${'hotpink'} disabled>Hotpink</lion-option>
|
||||||
|
<lion-option .choiceValue=${'teal'}>Teal</lion-option>
|
||||||
|
</lion-options>
|
||||||
|
</lion-select-rich>
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(el.hasFeedbackFor.includes('error')).to.be.true;
|
||||||
|
expect(el.showsFeedbackFor.includes('error')).to.be.false;
|
||||||
|
|
||||||
|
el._listboxNode.children[1].checked = true;
|
||||||
|
// Set touched to true (needed for feedback show) because we simulate a user touching the select
|
||||||
|
el.touched = true;
|
||||||
|
await el.updateComplete;
|
||||||
|
expect(el.hasFeedbackFor.includes('error')).to.be.false;
|
||||||
|
expect(el.showsFeedbackFor.includes('error')).to.be.false;
|
||||||
|
|
||||||
|
el._listboxNode.children[0].checked = true;
|
||||||
|
await el.updateComplete;
|
||||||
|
expect(el.hasFeedbackFor.includes('error')).to.be.true;
|
||||||
|
expect(el.showsFeedbackFor.includes('error')).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Invoker', () => {
|
describe('Invoker', () => {
|
||||||
|
|
@ -93,9 +220,9 @@ describe('lion-select-rich', () => {
|
||||||
expect(el._invokerNode.tagName).to.equal('LION-SELECT-INVOKER');
|
expect(el._invokerNode.tagName).to.equal('LION-SELECT-INVOKER');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('syncs the selected element to the invoker', async () => {
|
it('sets the first option as the selectedElement if no option is checked', async () => {
|
||||||
const el = await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich>
|
<lion-select-rich name="foo">
|
||||||
<lion-options slot="input">
|
<lion-options slot="input">
|
||||||
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
||||||
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
||||||
|
|
@ -103,10 +230,23 @@ describe('lion-select-rich', () => {
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`);
|
`);
|
||||||
const options = Array.from(el.querySelectorAll('lion-option'));
|
const options = Array.from(el.querySelectorAll('lion-option'));
|
||||||
expect(el._invokerNode.selectedElement).to.equal(options[0]);
|
expect(el._invokerNode.selectedElement).dom.to.equal(options[0]);
|
||||||
|
});
|
||||||
|
|
||||||
el.checkedIndex = 1;
|
it('syncs the selected element to the invoker', async () => {
|
||||||
expect(el._invokerNode.selectedElement).to.equal(el.querySelectorAll('lion-option')[1]);
|
const el = await fixture(html`
|
||||||
|
<lion-select-rich name="foo">
|
||||||
|
<lion-options slot="input">
|
||||||
|
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
||||||
|
<lion-option .choiceValue=${20} checked>Item 2</lion-option>
|
||||||
|
</lion-options>
|
||||||
|
</lion-select-rich>
|
||||||
|
`);
|
||||||
|
const options = el.querySelectorAll('lion-option');
|
||||||
|
expect(el._invokerNode.selectedElement).dom.to.equal(options[1]);
|
||||||
|
|
||||||
|
el.checkedIndex = 0;
|
||||||
|
expect(el._invokerNode.selectedElement).dom.to.equal(options[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('delegates readonly to the invoker', async () => {
|
it('delegates readonly to the invoker', async () => {
|
||||||
|
|
@ -199,7 +339,7 @@ describe('lion-select-rich', () => {
|
||||||
expect(options[1].checked).to.be.true;
|
expect(options[1].checked).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('stays closed on click if it disabled or readonly', async () => {
|
it('stays closed on click if it is disabled or readonly', async () => {
|
||||||
const elReadOnly = await fixture(html`
|
const elReadOnly = await fixture(html`
|
||||||
<lion-select-rich readonly>
|
<lion-select-rich readonly>
|
||||||
<lion-options slot="input">
|
<lion-options slot="input">
|
||||||
|
|
@ -449,7 +589,7 @@ describe('lion-select-rich', () => {
|
||||||
</lion-options>
|
</lion-options>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`);
|
`);
|
||||||
expect(el.checkedValue).to.deep.equal({
|
expect(el.modelValue).to.deep.equal({
|
||||||
type: 'mastercard',
|
type: 'mastercard',
|
||||||
label: 'Master Card',
|
label: 'Master Card',
|
||||||
amount: 12000,
|
amount: 12000,
|
||||||
|
|
@ -457,7 +597,7 @@ describe('lion-select-rich', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
el.checkedIndex = 1;
|
el.checkedIndex = 1;
|
||||||
expect(el.checkedValue).to.deep.equal({
|
expect(el.modelValue).to.deep.equal({
|
||||||
type: 'visacard',
|
type: 'visacard',
|
||||||
label: 'Visa Card',
|
label: 'Visa Card',
|
||||||
amount: 0,
|
amount: 0,
|
||||||
|
|
@ -476,7 +616,23 @@ describe('lion-select-rich', () => {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.colorList = [];
|
this.colorList = [
|
||||||
|
{
|
||||||
|
label: 'Red',
|
||||||
|
value: 'red',
|
||||||
|
checked: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Hotpink',
|
||||||
|
value: 'hotpink',
|
||||||
|
checked: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Teal',
|
||||||
|
value: 'teal',
|
||||||
|
checked: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
@ -502,50 +658,21 @@ describe('lion-select-rich', () => {
|
||||||
<${mySelectContainerTag}></${mySelectContainerTag}>
|
<${mySelectContainerTag}></${mySelectContainerTag}>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const colorList = [
|
|
||||||
{
|
|
||||||
label: 'Red',
|
|
||||||
value: 'red',
|
|
||||||
checked: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Hotpink',
|
|
||||||
value: 'hotpink',
|
|
||||||
checked: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Teal',
|
|
||||||
value: 'teal',
|
|
||||||
checked: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
el.colorList = colorList;
|
|
||||||
el.requestUpdate();
|
|
||||||
await el.updateComplete;
|
|
||||||
|
|
||||||
const selectRich = el.shadowRoot.querySelector('lion-select-rich');
|
const selectRich = el.shadowRoot.querySelector('lion-select-rich');
|
||||||
const invoker = selectRich._invokerNode;
|
const invoker = selectRich._invokerNode;
|
||||||
|
|
||||||
// needed to properly set the checkedIndex and checkedValue
|
|
||||||
selectRich.requestUpdate();
|
|
||||||
await selectRich.updateComplete;
|
|
||||||
|
|
||||||
expect(selectRich.checkedIndex).to.equal(1);
|
expect(selectRich.checkedIndex).to.equal(1);
|
||||||
expect(selectRich.checkedValue).to.equal('hotpink');
|
expect(selectRich.modelValue).to.equal('hotpink');
|
||||||
expect(invoker.selectedElement.value).to.equal('hotpink');
|
expect(invoker.selectedElement.value).to.equal('hotpink');
|
||||||
|
|
||||||
colorList.splice(1, 0, {
|
const newOption = document.createElement('lion-option');
|
||||||
label: 'Blue',
|
newOption.modelValue = { checked: false, value: 'blue' };
|
||||||
value: 'blue',
|
newOption.textContent = 'Blue';
|
||||||
checked: false,
|
const hotpinkEl = selectRich._listboxNode.children[1];
|
||||||
});
|
hotpinkEl.insertAdjacentElement('beforebegin', newOption);
|
||||||
|
|
||||||
el.requestUpdate();
|
|
||||||
await el.updateComplete;
|
|
||||||
|
|
||||||
expect(selectRich.checkedIndex).to.equal(2);
|
expect(selectRich.checkedIndex).to.equal(2);
|
||||||
expect(selectRich.checkedValue).to.equal('hotpink');
|
expect(selectRich.modelValue).to.equal('hotpink');
|
||||||
expect(invoker.selectedElement.value).to.equal('hotpink');
|
expect(invoker.selectedElement.value).to.equal('hotpink');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue