From 0d64792ff514df9fc2e401898c7470fdd8fd2273 Mon Sep 17 00:00:00 2001 From: Thomas Allmer Date: Thu, 25 Jul 2019 16:00:29 +0200 Subject: [PATCH] fix(core): add DisabledMixin to manage disabled --- packages/core/index.js | 1 + packages/core/src/DisabledMixin.js | 64 ++++++++++++++++++ packages/core/test/DisabledMixin.test.js | 86 ++++++++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 packages/core/src/DisabledMixin.js create mode 100644 packages/core/test/DisabledMixin.test.js diff --git a/packages/core/index.js b/packages/core/index.js index 599274df4..06781fe61 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -51,3 +51,4 @@ export { DelegateMixin } from './src/DelegateMixin.js'; export { DomHelpersMixin } from './src/DomHelpersMixin.js'; export { LionSingleton } from './src/LionSingleton.js'; export { SlotMixin } from './src/SlotMixin.js'; +export { DisabledMixin } from './src/DisabledMixin.js'; diff --git a/packages/core/src/DisabledMixin.js b/packages/core/src/DisabledMixin.js new file mode 100644 index 000000000..b279695b3 --- /dev/null +++ b/packages/core/src/DisabledMixin.js @@ -0,0 +1,64 @@ +import { dedupeMixin } from './dedupeMixin.js'; + +/** + * #DisabledMixin + * + * @polymerMixin + * @mixinFunction + */ +export const DisabledMixin = dedupeMixin( + superclass => + // eslint-disable-next-line no-shadow + class DisabledMixin extends superclass { + static get properties() { + return { + disabled: { + type: Boolean, + reflect: true, + }, + }; + } + + constructor() { + super(); + this.__requestedToBeDisabled = false; + this.__isUserSettingDisabled = true; + + this.__restoreDisabledTo = false; + this.disabled = false; + } + + makeRequestToBeDisabled() { + if (this.__requestedToBeDisabled === false) { + this.__requestedToBeDisabled = true; + this.__restoreDisabledTo = this.disabled; + this.__internalSetDisabled(true); + } + } + + retractRequestToBeDisabled() { + if (this.__requestedToBeDisabled === true) { + this.__requestedToBeDisabled = false; + this.__internalSetDisabled(this.__restoreDisabledTo); + } + } + + __internalSetDisabled(value) { + this.__isUserSettingDisabled = false; + this.disabled = value; + this.__isUserSettingDisabled = true; + } + + _requestUpdate(name, oldValue) { + super._requestUpdate(name, oldValue); + if (name === 'disabled') { + if (this.__isUserSettingDisabled) { + this.__restoreDisabledTo = this.disabled; + } + if (this.disabled === false && this.__requestedToBeDisabled === true) { + this.__internalSetDisabled(true); + } + } + } + }, +); diff --git a/packages/core/test/DisabledMixin.test.js b/packages/core/test/DisabledMixin.test.js new file mode 100644 index 000000000..cbd4389d5 --- /dev/null +++ b/packages/core/test/DisabledMixin.test.js @@ -0,0 +1,86 @@ +import { expect, fixture, html } from '@open-wc/testing'; + +import { LitElement } from '../index.js'; +import { DisabledMixin } from '../src/DisabledMixin.js'; + +describe('DisabledMixin', () => { + before(() => { + class CanBeDisabled extends DisabledMixin(LitElement) {} + customElements.define('can-be-disabled', CanBeDisabled); + }); + + it('reflects disabled to attribute', async () => { + const el = await fixture(html` + + `); + expect(el.hasAttribute('disabled')).to.be.false; + el.disabled = true; + await el.updateComplete; + expect(el.hasAttribute('disabled')).to.be.true; + }); + + it('can be requested to be disabled', async () => { + const el = await fixture(html` + + `); + el.makeRequestToBeDisabled(); + expect(el.disabled).to.be.true; + await el.updateComplete; + expect(el.hasAttribute('disabled')).to.be.true; + }); + + it('will not allow to become enabled after makeRequestToBeDisabled()', async () => { + const el = await fixture(html` + + `); + el.makeRequestToBeDisabled(); + expect(el.disabled).to.be.true; + + el.disabled = false; + expect(el.disabled).to.be.true; + }); + + it('will stay disabled after retractRequestToBeDisabled() if it was disabled before', async () => { + const el = await fixture(html` + + `); + el.makeRequestToBeDisabled(); + el.retractRequestToBeDisabled(); + expect(el.disabled).to.be.true; + }); + + it('will become enabled after retractRequestToBeDisabled() if it was enabled before', async () => { + const el = await fixture(html` + + `); + el.makeRequestToBeDisabled(); + expect(el.disabled).to.be.true; + el.retractRequestToBeDisabled(); + expect(el.disabled).to.be.false; + }); + + it('may allow multiple calls to makeRequestToBeDisabled()', async () => { + const el = await fixture(html` + + `); + el.makeRequestToBeDisabled(); + el.makeRequestToBeDisabled(); + el.retractRequestToBeDisabled(); + expect(el.disabled).to.be.false; + }); + + it('will restore last state after retractRequestToBeDisabled()', async () => { + const el = await fixture(html` + + `); + el.makeRequestToBeDisabled(); + el.disabled = true; + el.retractRequestToBeDisabled(); + expect(el.disabled).to.be.true; + + el.makeRequestToBeDisabled(); + el.disabled = false; + el.retractRequestToBeDisabled(); + expect(el.disabled).to.be.false; + }); +});