fix(checkbox-group): indeterminate checkbox respects disabled states (#1698)

Co-authored-by: Danny Moerkerke <danny.moerkerke@ing.com>
This commit is contained in:
Joren Broekema 2022-05-18 12:08:34 +02:00 committed by GitHub
parent 6c0b0201e6
commit 05e17d69e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 145 additions and 7 deletions

View file

@ -0,0 +1,5 @@
---
'@lion/checkbox-group': patch
---
Checkbox indeterminate now properly handles disabled states on child checkboxes.

View file

@ -13,7 +13,6 @@ export class LionCheckboxIndeterminate extends LionCheckbox {
:host .choice-field__nested-checkboxes {
display: block;
}
::slotted([slot='checkbox']) {
padding-left: 8px;
}
@ -86,6 +85,7 @@ export class LionCheckboxIndeterminate extends LionCheckbox {
this.__settingOwnChecked = true;
const checkedElements = subCheckboxes.filter(checkbox => checkbox.checked);
switch (subCheckboxes.length - checkedElements.length) {
// all checked
case 0:
@ -147,16 +147,32 @@ export class LionCheckboxIndeterminate extends LionCheckbox {
}
this.__settingOwnSubs = true;
if (this.indeterminate && this.mixedState) {
const subCheckboxes = this._subCheckboxes;
const checkedElements = subCheckboxes.filter(checkbox => checkbox.checked);
const disabledElements = subCheckboxes.filter(checkbox => checkbox.disabled);
const allChecked =
subCheckboxes.length > 0 && subCheckboxes.length === checkedElements.length;
const allDisabled =
subCheckboxes.length > 0 && subCheckboxes.length === disabledElements.length;
const hasDisabledElements = disabledElements.length > 0;
if (allDisabled) {
this.checked = allChecked;
}
if (this.indeterminate && (this.mixedState || hasDisabledElements)) {
this._subCheckboxes.forEach((checkbox, i) => {
// eslint-disable-next-line no-param-reassign
checkbox.checked = this._indeterminateSubStates[i];
});
} else {
this._subCheckboxes.forEach(checkbox => {
// eslint-disable-next-line no-param-reassign
checkbox.checked = this._inputNode.checked;
});
this._subCheckboxes
.filter(checkbox => !checkbox.disabled)
.forEach(checkbox => {
// eslint-disable-next-line no-param-reassign
checkbox.checked = this._inputNode.checked;
});
}
this.updateComplete.then(() => {
this.__settingOwnSubs = false;

View file

@ -153,6 +153,34 @@ describe('<lion-checkbox-indeterminate>', () => {
expect(elIndeterminate?.checked).to.be.true;
});
it('should become indeterminate if all children except disabled ones are checked', async () => {
// Arrange
const el = /** @type {LionCheckboxGroup} */ (
await fixture(html`
<lion-checkbox-group name="scientists[]">
<lion-checkbox-indeterminate label="Favorite scientists">
<lion-checkbox slot="checkbox" label="Archimedes"></lion-checkbox>
<lion-checkbox slot="checkbox" label="Francis Bacon" disabled></lion-checkbox>
<lion-checkbox slot="checkbox" label="Marie Curie"></lion-checkbox>
</lion-checkbox-indeterminate>
</lion-checkbox-group>
`)
);
const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ (
el.querySelector('lion-checkbox-indeterminate')
);
const { _subCheckboxes } = getCheckboxIndeterminateMembers(elIndeterminate);
// Act
_subCheckboxes[0].checked = true;
_subCheckboxes[2].checked = true;
await el.updateComplete;
// Assert
expect(elIndeterminate?.hasAttribute('indeterminate')).to.be.true;
expect(elIndeterminate?.checked).to.be.false;
});
it('should sync all children when parent is checked (from indeterminate to checked)', async () => {
// Arrange
const el = /** @type {LionCheckboxGroup} */ (
@ -182,6 +210,95 @@ describe('<lion-checkbox-indeterminate>', () => {
expect(_subCheckboxes[2].hasAttribute('checked')).to.be.true;
});
it('should not sync any disabled children when parent is checked (from indeterminate to checked)', async () => {
// Arrange
const el = /** @type {LionCheckboxGroup} */ (
await fixture(html`
<lion-checkbox-group name="scientists[]">
<lion-checkbox-indeterminate label="Favorite scientists">
<lion-checkbox slot="checkbox" label="Archimedes"></lion-checkbox>
<lion-checkbox slot="checkbox" label="Francis Bacon" disabled></lion-checkbox>
<lion-checkbox slot="checkbox" label="Marie Curie"></lion-checkbox>
</lion-checkbox-indeterminate>
</lion-checkbox-group>
`)
);
const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ (
el.querySelector('lion-checkbox-indeterminate')
);
const { _subCheckboxes, _inputNode } = getCheckboxIndeterminateMembers(elIndeterminate);
// Act
_inputNode.click();
await elIndeterminate.updateComplete;
// Assert
expect(elIndeterminate.hasAttribute('indeterminate')).to.be.true;
expect(_subCheckboxes[0].hasAttribute('checked')).to.be.true;
expect(_subCheckboxes[1].hasAttribute('checked')).to.be.false;
expect(_subCheckboxes[2].hasAttribute('checked')).to.be.true;
});
it('should remain unchecked when parent is clicked and all children are disabled', async () => {
// Arrange
const el = /** @type {LionCheckboxGroup} */ (
await fixture(html`
<lion-checkbox-group name="scientists[]">
<lion-checkbox-indeterminate label="Favorite scientists">
<lion-checkbox slot="checkbox" label="Archimedes" disabled></lion-checkbox>
<lion-checkbox slot="checkbox" label="Francis Bacon" disabled></lion-checkbox>
<lion-checkbox slot="checkbox" label="Marie Curie" disabled></lion-checkbox>
</lion-checkbox-indeterminate>
</lion-checkbox-group>
`)
);
const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ (
el.querySelector('lion-checkbox-indeterminate')
);
const { _subCheckboxes, _inputNode } = getCheckboxIndeterminateMembers(elIndeterminate);
// Act
_inputNode.click();
await elIndeterminate.updateComplete;
// Assert
expect(elIndeterminate.hasAttribute('indeterminate')).to.be.false;
expect(elIndeterminate.hasAttribute('checked')).to.be.false;
expect(_subCheckboxes[0].hasAttribute('checked')).to.be.false;
expect(_subCheckboxes[1].hasAttribute('checked')).to.be.false;
expect(_subCheckboxes[2].hasAttribute('checked')).to.be.false;
});
it('should remain checked when parent is clicked and all children are disabled and checked', async () => {
// Arrange
const el = /** @type {LionCheckboxGroup} */ (
await fixture(html`
<lion-checkbox-group name="scientists[]">
<lion-checkbox-indeterminate label="Favorite scientists">
<lion-checkbox slot="checkbox" label="Archimedes" disabled checked></lion-checkbox>
<lion-checkbox slot="checkbox" label="Francis Bacon" disabled checked></lion-checkbox>
<lion-checkbox slot="checkbox" label="Marie Curie" disabled checked></lion-checkbox>
</lion-checkbox-indeterminate>
</lion-checkbox-group>
`)
);
const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ (
el.querySelector('lion-checkbox-indeterminate')
);
const { _subCheckboxes, _inputNode } = getCheckboxIndeterminateMembers(elIndeterminate);
// Act
_inputNode.click();
await elIndeterminate.updateComplete;
// Assert
expect(elIndeterminate.hasAttribute('indeterminate')).to.be.false;
expect(elIndeterminate.hasAttribute('checked')).to.be.true;
expect(_subCheckboxes[0].hasAttribute('checked')).to.be.true;
expect(_subCheckboxes[1].hasAttribute('checked')).to.be.true;
expect(_subCheckboxes[2].hasAttribute('checked')).to.be.true;
});
it('should sync all children when parent is checked (from unchecked to checked)', async () => {
// Arrange
const el = /** @type {LionCheckboxGroup} */ (

View file

@ -3430,7 +3430,7 @@ autosize@4.0.2:
resolved "https://registry.yarnpkg.com/autosize/-/autosize-4.0.2.tgz#073cfd07c8bf45da4b9fd153437f5bafbba1e4c9"
integrity sha512-jnSyH2d+qdfPGpWlcuhGiHmqBJ6g3X+8T+iRwFrHPLVcdoGJE/x6Qicm6aDHfTsbgZKxyV8UU/YB2p4cjKDRRA==
awesome-phonenumber@3.0.1:
awesome-phonenumber@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/awesome-phonenumber/-/awesome-phonenumber-3.0.1.tgz#8d73aaa1c2b0a660b117567b0d9797623457e1d0"
integrity sha512-H5rqjTJ1+HxmyuSKDoPgvHUgP+RBRhtWQ25ccy4BmSLQL5UVg3K+yo2QCX4IlkxiVNst3suGMArV9TH7B1KEPw==