From 8d74ffcc0b00812a1beed7a46d694b6498b492ce Mon Sep 17 00:00:00 2001 From: MekalaNagarajan-Centrica Date: Wed, 15 Dec 2021 16:04:42 +0000 Subject: [PATCH 01/10] feat: mask and separator --- .../inputter/src/inputter-component.js | 72 ++++++++++++++----- .../components/inputter/src/styles.css | 26 +++++++ packages/library/components/inputter/story.js | 6 ++ .../controllers/mask-separator-controller.js | 39 ++++++++++ .../tests/controllers/mask-separator.test.js | 43 +++++++++++ 5 files changed, 170 insertions(+), 16 deletions(-) create mode 100644 packages/library/controllers/mask-separator-controller.js create mode 100644 packages/library/tests/controllers/mask-separator.test.js diff --git a/packages/library/components/inputter/src/inputter-component.js b/packages/library/components/inputter/src/inputter-component.js index 6632f054..d80951fa 100644 --- a/packages/library/components/inputter/src/inputter-component.js +++ b/packages/library/components/inputter/src/inputter-component.js @@ -1,8 +1,9 @@ -import { html, MuonElement, classMap } from '@muons/library'; +import { html, MuonElement, classMap, ifDefined } from '@muons/library'; import { INPUTTER_TYPE } from '@muons/library/build/tokens/es6/muon-tokens'; import { ValidationMixin } from '@muons/library/mixins/validation-mixin'; +import { MaskSeparatorController } from '@muons/library/controllers/mask-separator-controller'; import styles from './styles.css'; /** @@ -19,7 +20,9 @@ export class Inputter extends ValidationMixin(MuonElement) { helper: { type: String }, mask: { type: String }, separator: { type: String }, - isHelperOpen: { type: Boolean } + isHelperOpen: { type: Boolean }, + _maskDisplayValue: { type: String, state: true, reflect: true }, + _maskSeparatorController: { type: Object, state: true } }; } @@ -32,6 +35,48 @@ export class Inputter extends ValidationMixin(MuonElement) { this.type = INPUTTER_TYPE; this.isHelperOpen = false; + this.mask = ''; + this.separator = ''; + this._maskDisplayValue = ''; + } + + firstUpdated() { + if (ifDefined(this.mask)) { + this._maskSeparatorController = new MaskSeparatorController(this); + this.addController(this._maskSeparatorController); + + this._slottedInputs[0].addEventListener('input', this._onInput.bind(this)); + this._slottedInputs[0].setAttribute('maxlength', this.mask.length); + } + } + + _onInput(inputEvent) { + inputEvent.stopPropagation(); + const value = inputEvent.target.value; + this._maskDisplayValue = this._maskSeparatorController.updateMaskValue(value); + if (ifDefined(this.separator)) { + this._updateValue(value); + } + } + + _updateValue(value) { + const input = this._slottedInputs[0]; + let cursor = input.selectionStart; + const diff = this.value.length - value.length; + + if (diff > 0 && this.value.charAt(cursor) === this.separator) { + value = value.slice(0, cursor - 1) + (cursor < value.length ? value.slice(cursor) : ''); + cursor -= 1; + } + this.value = this._maskSeparatorController.formatWithMaskAndSeparator(value); + input.value = this.value; + + this.updateComplete.then(() => { + if (input.value.charAt(cursor) === this.separator) { + cursor += 1; + } + input.setSelectionRange(cursor, cursor); + }); } get validity() { @@ -43,30 +88,25 @@ export class Inputter extends ValidationMixin(MuonElement) { get standardTemplate() { const classes = { 'slotted-content': true, - 'select-arrow': this._inputType === this._isSelect + 'select-arrow': this._isSelect, + 'has-mask': ifDefined(this.mask) }; return html` ${this._labelTemplate}
${this._htmlFormElementTemplate} + ${this._maskTemplate}
${this._validationMessageTemplate} `; } - render() { - // const hasError = this._error && !this.inputType === 'select' ? 'invalid' : ''; // @TODO: it is not an error - const classes = { - 'input-holder': true, - 'is-pristine': this.pristine, - 'is-dirty': !this.pristine - }; - - return html` -
- ${super.render()} -
- `; + get _maskTemplate() { + if (ifDefined(this.mask)) { + const maskValue = this._maskDisplayValue || this.mask; + return html``; + } + return undefined; } } diff --git a/packages/library/components/inputter/src/styles.css b/packages/library/components/inputter/src/styles.css index 634bdec0..e0fd64ce 100644 --- a/packages/library/components/inputter/src/styles.css +++ b/packages/library/components/inputter/src/styles.css @@ -2,4 +2,30 @@ :host { display: block; + + & .has-mask { + position: relative; + + & .input-guide, + & ::slotted(*) { + padding: 0.25rem 1rem; + letter-spacing: 0.5rem; + font-size: 1.25rem; + max-width: calc(var(--maxlength) + 1rem); + } + + & .input-guide { + display: inline-block; + position: absolute; + left: 0; + color: lightslategray; + white-space: break-spaces; + z-index: -1; + text-align: end; + } + + & ::slotted(*) { + background-color: transparent; + } + } } diff --git a/packages/library/components/inputter/story.js b/packages/library/components/inputter/story.js index 866a5c22..caf3c6c5 100644 --- a/packages/library/components/inputter/story.js +++ b/packages/library/components/inputter/story.js @@ -63,3 +63,9 @@ const innerInputTel = (args) => ` export const Tel = (args) => details.template(args, innerInputTel); Tel.args = { label: 'A label', value: '', validation: '["isRequired"]' }; + +export const Mask = (args) => details.template(args, innerInputText); +Mask.args = { label: 'A label', value: '', mask: '000000' }; + +export const Separator = (args) => details.template(args, innerInputText); +Separator.args = { label: 'A label', value: '', separator: '-', mask: ' - - ' }; diff --git a/packages/library/controllers/mask-separator-controller.js b/packages/library/controllers/mask-separator-controller.js new file mode 100644 index 00000000..de823f22 --- /dev/null +++ b/packages/library/controllers/mask-separator-controller.js @@ -0,0 +1,39 @@ +export class MaskSeparatorController { + + constructor(host) { + this.host = host; + this.mask = host.mask; + this.separator = host.separator; + } + + updateMaskValue(value) { + const length = value.length; + let str = new Array(length + 1).join(' '); + str += this.mask.slice(length); + return str; + } + + formatWithMaskAndSeparator(value) { + const formattedValue = this.__formatInputWithoutSeparator(value); + const parts = this.mask.split(this.separator); + let processedValue = ''; + let length = 0; + let partsLength = 0; + for (let i = 0; i < parts.length && length < formattedValue.length; i++) { + partsLength += parts[i].length; + const remainingLength = formattedValue.length - length; + processedValue += formattedValue.substr( + length, remainingLength > parts[i].length ? parts[i].length : remainingLength); + length += parts[i].length; + if (i < (parts.length - 1) && processedValue.length === partsLength) { + processedValue += this.separator; + partsLength += 1; + } + } + return processedValue; + } + + __formatInputWithoutSeparator(value) { + return value.split(this.separator).join(''); + } +} diff --git a/packages/library/tests/controllers/mask-separator.test.js b/packages/library/tests/controllers/mask-separator.test.js new file mode 100644 index 00000000..b344d0f3 --- /dev/null +++ b/packages/library/tests/controllers/mask-separator.test.js @@ -0,0 +1,43 @@ +/* eslint-disable no-undef */ +import { expect } from '@open-wc/testing'; +import { MaskSeparatorController } from '@muons/library/controllers/mask-separator-controller'; + +describe('mask-separator', async () => { + describe('mask 00000', async () => { + const host = { + mask: '00000' + }; + const maskController = new MaskSeparatorController(host); + + it('controller setup mask', async () => { + expect(maskController.mask).to.be.equal('00000', 'controller has correct mask value'); + }); + + it('controller setup seprator', async () => { + expect(maskController.separator).to.be.equal(undefined); + }); + + it('update mask', async () => { + expect(maskController.updateMaskValue('12')).to.be.equal(' 000', 'mask value updated to correct'); + }); + }); + + describe('mask `xxxxx-`', async () => { + const host = { + mask: 'xxxxx-' + }; + const maskController = new MaskSeparatorController(host); + + it('controller setup mask', async () => { + expect(maskController.mask).to.be.equal('xxxxx-', 'controller has correct mask value'); + }); + + it('controller setup seprator', async () => { + expect(maskController.separator).to.be.equal(undefined); + }); + + it('update mask', async () => { + expect(maskController.updateMaskValue('quv')).to.be.equal(' xx-', 'mask value updated to correct'); + }); + }); +}); From 51a8a42d7aa4cd709c2dc33438919d840885123c Mon Sep 17 00:00:00 2001 From: MekalaNagarajan-Centrica Date: Tue, 21 Dec 2021 11:15:54 +0000 Subject: [PATCH 02/10] feat: separator spacing and tests --- .../inputter/src/inputter-component.js | 33 +++++++++----- .../components/inputter/src/styles.css | 14 +++--- .../controllers/mask-separator-controller.js | 6 ++- .../tests/controllers/mask-separator.test.js | 44 ++++++++++++++++++- 4 files changed, 78 insertions(+), 19 deletions(-) diff --git a/packages/library/components/inputter/src/inputter-component.js b/packages/library/components/inputter/src/inputter-component.js index d80951fa..e2b0d45f 100644 --- a/packages/library/components/inputter/src/inputter-component.js +++ b/packages/library/components/inputter/src/inputter-component.js @@ -1,4 +1,4 @@ -import { html, MuonElement, classMap, ifDefined } from '@muons/library'; +import { html, MuonElement, classMap, styleMap, ifDefined } from '@muons/library'; import { INPUTTER_TYPE } from '@muons/library/build/tokens/es6/muon-tokens'; @@ -21,7 +21,7 @@ export class Inputter extends ValidationMixin(MuonElement) { mask: { type: String }, separator: { type: String }, isHelperOpen: { type: Boolean }, - _maskDisplayValue: { type: String, state: true, reflect: true }, + _maskDisplayValue: { type: String, state: true }, _maskSeparatorController: { type: Object, state: true } }; } @@ -50,21 +50,26 @@ export class Inputter extends ValidationMixin(MuonElement) { } } + /** + * A method to handle `input` event when `mask` is provided. + * + * @param {Event} inputEvent - `input` event + * @returns {undefined} + */ _onInput(inputEvent) { inputEvent.stopPropagation(); - const value = inputEvent.target.value; - this._maskDisplayValue = this._maskSeparatorController.updateMaskValue(value); if (ifDefined(this.separator)) { - this._updateValue(value); + this.__updateValue(inputEvent.target); } + this._maskDisplayValue = this._maskSeparatorController.updateMaskValue(inputEvent.target.value); } - _updateValue(value) { - const input = this._slottedInputs[0]; + __updateValue(input) { + let value = input.value; let cursor = input.selectionStart; const diff = this.value.length - value.length; - if (diff > 0 && this.value.charAt(cursor) === this.separator) { + if (diff > 0 && this.mask.charAt(cursor) === this.separator) { value = value.slice(0, cursor - 1) + (cursor < value.length ? value.slice(cursor) : ''); cursor -= 1; } @@ -72,7 +77,7 @@ export class Inputter extends ValidationMixin(MuonElement) { input.value = this.value; this.updateComplete.then(() => { - if (input.value.charAt(cursor) === this.separator) { + if (this.mask.charAt(cursor) === this.separator) { cursor += 1; } input.setSelectionRange(cursor, cursor); @@ -92,9 +97,15 @@ export class Inputter extends ValidationMixin(MuonElement) { 'has-mask': ifDefined(this.mask) }; + let styles; + if (this.mask) { + styles = { + '--maxlength': this.mask.length + } + } return html` ${this._labelTemplate} -
+
${this._htmlFormElementTemplate} ${this._maskTemplate}
@@ -105,7 +116,7 @@ export class Inputter extends ValidationMixin(MuonElement) { get _maskTemplate() { if (ifDefined(this.mask)) { const maskValue = this._maskDisplayValue || this.mask; - return html``; + return html``; } return undefined; } diff --git a/packages/library/components/inputter/src/styles.css b/packages/library/components/inputter/src/styles.css index e0fd64ce..39eff07a 100644 --- a/packages/library/components/inputter/src/styles.css +++ b/packages/library/components/inputter/src/styles.css @@ -7,11 +7,10 @@ position: relative; & .input-guide, - & ::slotted(*) { + & ::slotted(input) { padding: 0.25rem 1rem; letter-spacing: 0.5rem; - font-size: 1.25rem; - max-width: calc(var(--maxlength) + 1rem); + max-width: calc((var(--maxlength) + 1) * 1rem); } & .input-guide { @@ -19,13 +18,16 @@ position: absolute; left: 0; color: lightslategray; - white-space: break-spaces; + white-space: pre; z-index: -1; - text-align: end; + text-align: start; + font-size: 1.5rem; } - & ::slotted(*) { + & ::slotted(input) { background-color: transparent; + font-size: 1.25rem; + padding-right: 1.25rem; } } } diff --git a/packages/library/controllers/mask-separator-controller.js b/packages/library/controllers/mask-separator-controller.js index de823f22..09b4a0a5 100644 --- a/packages/library/controllers/mask-separator-controller.js +++ b/packages/library/controllers/mask-separator-controller.js @@ -7,8 +7,12 @@ export class MaskSeparatorController { } updateMaskValue(value) { - const length = value.length; + let length = value.length; let str = new Array(length + 1).join(' '); + if (this.separator && this.mask.charAt(length) === this.separator) { + str += ' '; + length += 1; + } str += this.mask.slice(length); return str; } diff --git a/packages/library/tests/controllers/mask-separator.test.js b/packages/library/tests/controllers/mask-separator.test.js index b344d0f3..273a4c5a 100644 --- a/packages/library/tests/controllers/mask-separator.test.js +++ b/packages/library/tests/controllers/mask-separator.test.js @@ -2,7 +2,7 @@ import { expect } from '@open-wc/testing'; import { MaskSeparatorController } from '@muons/library/controllers/mask-separator-controller'; -describe('mask-separator', async () => { +describe('mask', async () => { describe('mask 00000', async () => { const host = { mask: '00000' @@ -41,3 +41,45 @@ describe('mask-separator', async () => { }); }); }); + +describe('mask-separator', async () => { + describe('mask ` - - `, separator `-`', async () => { + const host = { + mask: '00-00-00', + separator: '-' + }; + const maskController = new MaskSeparatorController(host); + + it('controller setup mask', async () => { + expect(maskController.mask).to.be.equal('00-00-00', 'controller has correct mask value'); + }); + + it('controller setup seprator', async () => { + expect(maskController.separator).to.be.equal('-', 'controller has correct separator value'); + }); + + it('update mask', async () => { + expect(maskController.updateMaskValue('1')).to.be.equal(' 0-00-00', 'mask value updated to correct'); + }); + + it('update value', async () => { + expect(maskController.formatWithMaskAndSeparator('1')).to.be.equal('1', 'processed value has correct value'); + }); + + it('update mask without separator', async () => { + expect(maskController.updateMaskValue('12')).to.be.equal(' 00-00', 'mask value updated to correct'); + }); + + it('update value without separator', async () => { + expect(maskController.formatWithMaskAndSeparator('12')).to.be.equal('12-', 'processed value has correct value'); + }); + + it('update value with separator', async () => { + expect(maskController.formatWithMaskAndSeparator('1-23')).to.be.equal('12-3', 'processed value has correct value'); + }); + + it('update mask with separator', async () => { + expect(maskController.updateMaskValue('1-23')).to.be.equal(' 0-00', 'mask value updated to correct'); + }); + }); +}); From 4372c68f97288a896462fb0541f2aa4144f7fad8 Mon Sep 17 00:00:00 2001 From: MekalaNagarajan-Centrica Date: Thu, 23 Dec 2021 14:42:40 +0000 Subject: [PATCH 03/10] feat: mask separator controller and directive --- .../inputter/src/inputter-component.js | 68 +++----------- .../components/inputter/src/styles.css | 7 +- packages/library/components/inputter/story.js | 20 +++-- .../controllers/mask-separator-controller.js | 50 +++++++++-- packages/library/directives/mask-input.js | 22 +++++ packages/library/mixins/form-element-mixin.js | 1 + packages/library/mixins/validation-mixin.js | 2 +- .../components/inputter/inputter.test.js | 90 +++++++++++++++++++ .../tests/controllers/mask-separator.test.js | 52 ----------- .../tests/directives/mask-input.test.js | 74 +++++++++++++++ packages/library/tests/helpers/index.js | 12 ++- .../library/tests/mixins/form-element.test.js | 6 +- 12 files changed, 265 insertions(+), 139 deletions(-) create mode 100644 packages/library/directives/mask-input.js create mode 100644 packages/library/tests/components/inputter/inputter.test.js create mode 100644 packages/library/tests/directives/mask-input.test.js diff --git a/packages/library/components/inputter/src/inputter-component.js b/packages/library/components/inputter/src/inputter-component.js index e2b0d45f..597447bf 100644 --- a/packages/library/components/inputter/src/inputter-component.js +++ b/packages/library/components/inputter/src/inputter-component.js @@ -4,6 +4,7 @@ import { } from '@muons/library/build/tokens/es6/muon-tokens'; import { ValidationMixin } from '@muons/library/mixins/validation-mixin'; import { MaskSeparatorController } from '@muons/library/controllers/mask-separator-controller'; +import { maskInput } from '@muons/library/directives/mask-input'; import styles from './styles.css'; /** @@ -20,9 +21,7 @@ export class Inputter extends ValidationMixin(MuonElement) { helper: { type: String }, mask: { type: String }, separator: { type: String }, - isHelperOpen: { type: Boolean }, - _maskDisplayValue: { type: String, state: true }, - _maskSeparatorController: { type: Object, state: true } + isHelperOpen: { type: Boolean } }; } @@ -37,51 +36,13 @@ export class Inputter extends ValidationMixin(MuonElement) { this.isHelperOpen = false; this.mask = ''; this.separator = ''; - this._maskDisplayValue = ''; } firstUpdated() { + super.firstUpdated(); if (ifDefined(this.mask)) { - this._maskSeparatorController = new MaskSeparatorController(this); - this.addController(this._maskSeparatorController); - - this._slottedInputs[0].addEventListener('input', this._onInput.bind(this)); - this._slottedInputs[0].setAttribute('maxlength', this.mask.length); - } - } - - /** - * A method to handle `input` event when `mask` is provided. - * - * @param {Event} inputEvent - `input` event - * @returns {undefined} - */ - _onInput(inputEvent) { - inputEvent.stopPropagation(); - if (ifDefined(this.separator)) { - this.__updateValue(inputEvent.target); + this.addController(new MaskSeparatorController(this, this._slottedInputs[0])); } - this._maskDisplayValue = this._maskSeparatorController.updateMaskValue(inputEvent.target.value); - } - - __updateValue(input) { - let value = input.value; - let cursor = input.selectionStart; - const diff = this.value.length - value.length; - - if (diff > 0 && this.mask.charAt(cursor) === this.separator) { - value = value.slice(0, cursor - 1) + (cursor < value.length ? value.slice(cursor) : ''); - cursor -= 1; - } - this.value = this._maskSeparatorController.formatWithMaskAndSeparator(value); - input.value = this.value; - - this.updateComplete.then(() => { - if (this.mask.charAt(cursor) === this.separator) { - cursor += 1; - } - input.setSelectionRange(cursor, cursor); - }); } get validity() { @@ -94,30 +55,21 @@ export class Inputter extends ValidationMixin(MuonElement) { const classes = { 'slotted-content': true, 'select-arrow': this._isSelect, - 'has-mask': ifDefined(this.mask) + 'has-mask': this.mask }; - let styles; + let styles = {}; if (this.mask) { styles = { '--maxlength': this.mask.length - } + }; } return html` - ${this._labelTemplate} + ${this._isMultiple ? this._headingTemplate : this._labelTemplate}
${this._htmlFormElementTemplate} - ${this._maskTemplate} + ${this.mask ? maskInput(this.mask, this.separator, this.value) : ``}
- ${this._validationMessageTemplate} - `; - } - - get _maskTemplate() { - if (ifDefined(this.mask)) { - const maskValue = this._maskDisplayValue || this.mask; - return html``; - } - return undefined; + ${this._validationMessageTemplate}`; } } diff --git a/packages/library/components/inputter/src/styles.css b/packages/library/components/inputter/src/styles.css index 39eff07a..48fe05a3 100644 --- a/packages/library/components/inputter/src/styles.css +++ b/packages/library/components/inputter/src/styles.css @@ -6,14 +6,15 @@ & .has-mask { position: relative; - & .input-guide, + & .input-mask, & ::slotted(input) { - padding: 0.25rem 1rem; + padding: 0.5rem 1rem; + margin: 0.75rem 0; letter-spacing: 0.5rem; max-width: calc((var(--maxlength) + 1) * 1rem); } - & .input-guide { + & .input-mask { display: inline-block; position: absolute; left: 0; diff --git a/packages/library/components/inputter/story.js b/packages/library/components/inputter/story.js index caf3c6c5..727300c0 100644 --- a/packages/library/components/inputter/story.js +++ b/packages/library/components/inputter/story.js @@ -14,7 +14,6 @@ export const Standard = (args) => details.template(args, innerInputText); Standard.args = { label: 'A label', value: 'this is a test', validation: '["isRequired","minLength(6)"]' }; const choiceInputText = (args) => ` -

What is your heating source?

more about this

@@ -22,10 +21,10 @@ const choiceInputText = (args) => ` `; export const Radio = (args) => details.template(args, choiceInputText); -Radio.args = { inputtype: 'radio', label: 'A label', value: '', validation: '["isRequired"]' }; +Radio.args = { inputtype: 'radio', label: 'A label', value: '', heading: 'What is your heating source?', validation: '["isRequired"]' }; export const Checkbox = (args) => details.template(args, choiceInputText); -Checkbox.args = { inputtype: 'checkbox', label: 'A label', value: '', validation: '["isRequired"]' }; +Checkbox.args = { inputtype: 'checkbox', label: 'A label', value: '', heading: 'What is your heating source?', validation: '["isRequired"]' }; const selectInputText = (args) => ` @@ -49,6 +48,12 @@ const textareaInputText = (args) => ` export const Textarea = (args) => details.template(args, textareaInputText); Textarea.args = { label: 'A label', value: 'gas' }; +export const Mask = (args) => details.template(args, innerInputText); +Mask.args = { label: 'A label', value: '', mask: '000000' }; + +export const Separator = (args) => details.template(args, innerInputText); +Separator.args = { label: 'A label', value: '', separator: '-', mask: ' - - ' }; + const innerInputDate = (args) => ` @@ -56,6 +61,9 @@ const innerInputDate = (args) => ` export const Date = (args) => details.template(args, innerInputDate); Date.args = { label: 'A label', value: '', validation: '["isRequired","minDate(\'11/11/2021\')"]' }; +export const DateMask = (args) => details.template(args, innerInputDate); +DateMask.args = { label: 'A label', value: '', mask: 'dd/mm/yyyy', separator: '/', validation: '["isRequired","minDate(\'11/11/2021\')"]' }; + const innerInputTel = (args) => ` @@ -63,9 +71,3 @@ const innerInputTel = (args) => ` export const Tel = (args) => details.template(args, innerInputTel); Tel.args = { label: 'A label', value: '', validation: '["isRequired"]' }; - -export const Mask = (args) => details.template(args, innerInputText); -Mask.args = { label: 'A label', value: '', mask: '000000' }; - -export const Separator = (args) => details.template(args, innerInputText); -Separator.args = { label: 'A label', value: '', separator: '-', mask: ' - - ' }; diff --git a/packages/library/controllers/mask-separator-controller.js b/packages/library/controllers/mask-separator-controller.js index 09b4a0a5..9a2ca52d 100644 --- a/packages/library/controllers/mask-separator-controller.js +++ b/packages/library/controllers/mask-separator-controller.js @@ -1,20 +1,52 @@ +import { ifDefined } from '@muons/library'; export class MaskSeparatorController { - constructor(host) { + constructor(host, input) { this.host = host; this.mask = host.mask; this.separator = host.separator; + this.input = input; } - updateMaskValue(value) { - let length = value.length; - let str = new Array(length + 1).join(' '); - if (this.separator && this.mask.charAt(length) === this.separator) { - str += ' '; - length += 1; + hostConnected() { + this.input.addEventListener('input', this.__onInput.bind(this)); + this.input.setAttribute('maxlength', this.mask.length); + } + + /** + * A method to handle `input` event when `mask` is provided. + * @param {Event} inputEvent - event while 'input. + * @returns {undefined} + */ + __onInput(inputEvent) { + inputEvent.stopPropagation(); + inputEvent.preventDefault(); + if (ifDefined(this.separator)) { + this.updateValue(); + } else { + this.host.value = this.input.value; + } + } + + updateValue() { + let value = this.input.value; + let cursor = this.input.selectionStart; + const diff = this.host.value.length - value.length; + + if (diff > 0 && this.mask.charAt(cursor) === this.separator) { + value = value.slice(0, cursor - 1) + (cursor < value.length ? value.slice(cursor) : ''); + cursor -= 1; + } + const formattedValue = this.formatWithMaskAndSeparator(value); + this.input.value = formattedValue; + this.host.value = formattedValue; + + if (this.mask.charAt(cursor) === this.separator) { + cursor += 1; } - str += this.mask.slice(length); - return str; + this.host.updateComplete.then(() => { + this.input.setSelectionRange(cursor, cursor); + }); } formatWithMaskAndSeparator(value) { diff --git a/packages/library/directives/mask-input.js b/packages/library/directives/mask-input.js new file mode 100644 index 00000000..c5247c03 --- /dev/null +++ b/packages/library/directives/mask-input.js @@ -0,0 +1,22 @@ +import { Directive, directive, html } from '@muons/library'; + +class MaskInputDirective extends Directive { + + constructor(partInfo) { + super(partInfo); + } + + render(mask, separator, value) { + let length = value ? value.length : 0; + let updatedMask = new Array(length + 1).join(' '); + if (separator && mask.charAt(length) === separator) { + updatedMask += ' '; + length += 1; + } + updatedMask += mask.slice(length); + return html``; + } + +} + +export const maskInput = directive(MaskInputDirective); diff --git a/packages/library/mixins/form-element-mixin.js b/packages/library/mixins/form-element-mixin.js index e4c96f6f..7d3b146a 100644 --- a/packages/library/mixins/form-element-mixin.js +++ b/packages/library/mixins/form-element-mixin.js @@ -88,6 +88,7 @@ export const FormElementMixin = (superClass) => } firstUpdated() { + super.firstUpdated(); this._slottedInputs.map((input) => { input.addEventListener('change', this._onChange.bind(this)); input.addEventListener('blur', this._onBlur.bind(this)); diff --git a/packages/library/mixins/validation-mixin.js b/packages/library/mixins/validation-mixin.js index 6fc4aec7..8b40e9a8 100644 --- a/packages/library/mixins/validation-mixin.js +++ b/packages/library/mixins/validation-mixin.js @@ -48,7 +48,7 @@ export const ValidationMixin = (superClass) => this.disableNative = false; this.showMessage = true; this._pristine = true; - this._validationState = {}; + this._validationState = []; this._customValidation = {}; } diff --git a/packages/library/tests/components/inputter/inputter.test.js b/packages/library/tests/components/inputter/inputter.test.js new file mode 100644 index 00000000..35ac1ca6 --- /dev/null +++ b/packages/library/tests/components/inputter/inputter.test.js @@ -0,0 +1,90 @@ +/* eslint-disable no-undef */ +import { expect, fixture, html, defineCE, unsafeStatic } from '@open-wc/testing'; +import { Inputter } from '@muons/library/components/inputter'; +import { defaultChecks, fillIn } from '../../helpers'; + +const tagName = defineCE(Inputter); +const tag = unsafeStatic(tagName); + +describe('Inputter', () => { + + describe('mask', async () => { + let inputter; + let shadowRoot; + let maskedInput; + let inputElement; + before(async () => { + inputter = await fixture(html` + <${tag} mask="00000"> + + + `); + shadowRoot = inputter.shadowRoot; + maskedInput = shadowRoot.querySelector('.input-mask'); + inputElement = inputter.querySelector('input'); + }); + + it('default checks', async () => { + await defaultChecks(inputter); + }); + + it('masked input check', async () => { + expect(maskedInput).to.be.not.null; // eslint-disable-line no-unused-expressions + expect(maskedInput.textContent).to.be.equal('00000', '`input-mask` has correct value'); + }); + + it('input value `1`', async () => { + await fillIn(inputElement, '1', 'input'); + maskedInput = shadowRoot.querySelector('.input-mask'); + expect(inputter.value).to.be.equal('1', 'Inputter has correct value'); + expect(maskedInput.textContent).to.be.equal(' 0000', '`input-mask` has correct value'); + }); + + it('input value `12`', async () => { + await fillIn(inputElement, '12', 'input'); + maskedInput = shadowRoot.querySelector('.input-mask'); + expect(inputter.value).to.be.equal('12', 'Inputter has correct value'); + expect(maskedInput.textContent).to.be.equal(' 000', '`input-mask` has correct value'); + }); + }); + + describe('mask separator', async () => { + let inputter; + let shadowRoot; + let maskedInput; + let inputElement; + before(async () => { + inputter = await fixture(html` + <${tag} mask="00-00-00" separator="-"> + + + `); + shadowRoot = inputter.shadowRoot; + maskedInput = shadowRoot.querySelector('.input-mask'); + inputElement = inputter.querySelector('input'); + }); + + it('default checks', async () => { + await defaultChecks(inputter); + }); + + it('masked input check', async () => { + expect(maskedInput).to.be.not.null; // eslint-disable-line no-unused-expressions + expect(maskedInput.textContent).to.be.equal('00-00-00', '`input-mask` has correct value'); + }); + + it('input value `1`', async () => { + await fillIn(inputElement, '1', 'input'); + maskedInput = inputter.shadowRoot.querySelector('.input-mask'); + expect(inputter.value).to.be.equal('1', 'Inputter has correct value'); + expect(maskedInput.textContent).to.be.equal(' 0-00-00', '`input-mask` has correct value'); + }); + + it('input value `12`', async () => { + await fillIn(inputElement, '12', 'input'); + maskedInput = inputter.shadowRoot.querySelector('.input-mask'); + expect(inputter.value).to.be.equal('12-', 'Inputter has correct value'); + expect(maskedInput.textContent).to.be.equal(' 00-00', '`input-mask` has correct value'); + }); + }); +}); diff --git a/packages/library/tests/controllers/mask-separator.test.js b/packages/library/tests/controllers/mask-separator.test.js index 273a4c5a..a21adec7 100644 --- a/packages/library/tests/controllers/mask-separator.test.js +++ b/packages/library/tests/controllers/mask-separator.test.js @@ -2,46 +2,6 @@ import { expect } from '@open-wc/testing'; import { MaskSeparatorController } from '@muons/library/controllers/mask-separator-controller'; -describe('mask', async () => { - describe('mask 00000', async () => { - const host = { - mask: '00000' - }; - const maskController = new MaskSeparatorController(host); - - it('controller setup mask', async () => { - expect(maskController.mask).to.be.equal('00000', 'controller has correct mask value'); - }); - - it('controller setup seprator', async () => { - expect(maskController.separator).to.be.equal(undefined); - }); - - it('update mask', async () => { - expect(maskController.updateMaskValue('12')).to.be.equal(' 000', 'mask value updated to correct'); - }); - }); - - describe('mask `xxxxx-`', async () => { - const host = { - mask: 'xxxxx-' - }; - const maskController = new MaskSeparatorController(host); - - it('controller setup mask', async () => { - expect(maskController.mask).to.be.equal('xxxxx-', 'controller has correct mask value'); - }); - - it('controller setup seprator', async () => { - expect(maskController.separator).to.be.equal(undefined); - }); - - it('update mask', async () => { - expect(maskController.updateMaskValue('quv')).to.be.equal(' xx-', 'mask value updated to correct'); - }); - }); -}); - describe('mask-separator', async () => { describe('mask ` - - `, separator `-`', async () => { const host = { @@ -58,18 +18,10 @@ describe('mask-separator', async () => { expect(maskController.separator).to.be.equal('-', 'controller has correct separator value'); }); - it('update mask', async () => { - expect(maskController.updateMaskValue('1')).to.be.equal(' 0-00-00', 'mask value updated to correct'); - }); - it('update value', async () => { expect(maskController.formatWithMaskAndSeparator('1')).to.be.equal('1', 'processed value has correct value'); }); - it('update mask without separator', async () => { - expect(maskController.updateMaskValue('12')).to.be.equal(' 00-00', 'mask value updated to correct'); - }); - it('update value without separator', async () => { expect(maskController.formatWithMaskAndSeparator('12')).to.be.equal('12-', 'processed value has correct value'); }); @@ -77,9 +29,5 @@ describe('mask-separator', async () => { it('update value with separator', async () => { expect(maskController.formatWithMaskAndSeparator('1-23')).to.be.equal('12-3', 'processed value has correct value'); }); - - it('update mask with separator', async () => { - expect(maskController.updateMaskValue('1-23')).to.be.equal(' 0-00', 'mask value updated to correct'); - }); }); }); diff --git a/packages/library/tests/directives/mask-input.test.js b/packages/library/tests/directives/mask-input.test.js new file mode 100644 index 00000000..4fab6707 --- /dev/null +++ b/packages/library/tests/directives/mask-input.test.js @@ -0,0 +1,74 @@ +/* eslint-disable no-undef */ +import { expect, html } from '@open-wc/testing'; +import { render } from 'lit'; +import { maskInput } from '@muons/library/directives/mask-input'; + +describe('mask', async () => { + describe('mask initial 00000', async () => { + let maskedInput; + before(async () => { + maskedInput = document.createElement('div'); + render(html`${maskInput('00000', '', '')}`, maskedInput); + }); + + it('directive element present check', async () => { + expect(maskedInput).to.be.not.null; // eslint-disable-line no-unused-expressions + }); + + it('directive masked value', async () => { + const maskedValue = maskedInput.querySelector('.input-mask'); + expect(maskedValue.textContent).to.be.equal('00000', 'directive has correct mask value'); + }); + }); + + describe('mask with value 00000', async () => { + let maskedInput; + before(async () => { + maskedInput = document.createElement('div'); + render(html`${maskInput('00000', '', '12')}`, maskedInput); + }); + + it('directive element present check', async () => { + expect(maskedInput).to.be.not.null; // eslint-disable-line no-unused-expressions + }); + + it('directive masked value', async () => { + const maskedValue = maskedInput.querySelector('.input-mask'); + expect(maskedValue.textContent).to.be.equal(' 000', 'directive has correct mask value'); + }); + }); + + describe('mask with separator 00-000', async () => { + let maskedInput; + before(async () => { + maskedInput = document.createElement('div'); + render(html`${maskInput('00-000', '-', '')}`, maskedInput); + }); + + it('directive element present check', async () => { + expect(maskedInput).to.be.not.null; // eslint-disable-line no-unused-expressions + }); + + it('directive masked value', async () => { + const maskedValue = maskedInput.querySelector('.input-mask'); + expect(maskedValue.textContent).to.be.equal('00-000', 'directive has correct mask value'); + }); + }); + + describe('mask with separator & value 00-000', async () => { + let maskedInput; + before(async () => { + maskedInput = document.createElement('div'); + render(html`${maskInput('00-000', '-', '12')}`, maskedInput); + }); + + it('directive element present check', async () => { + expect(maskedInput).to.be.not.null; // eslint-disable-line no-unused-expressions + }); + + it('directive masked value', async () => { + const maskedValue = maskedInput.querySelector('.input-mask'); + expect(maskedValue.textContent).to.be.equal(' 000', 'directive has correct mask value'); + }); + }); +}); diff --git a/packages/library/tests/helpers/index.js b/packages/library/tests/helpers/index.js index 7567e0df..1cc474d8 100644 --- a/packages/library/tests/helpers/index.js +++ b/packages/library/tests/helpers/index.js @@ -9,14 +9,18 @@ export const defaultChecks = async (el) => { await expect(el).to.be.accessible(); }; +export const fireEvent = async (element, event) => { + const customEvent = new CustomEvent(event, { bubbles: true }); + await element.dispatchEvent(customEvent); +}; + const fireChangeEvent = async (element) => { - const event = new CustomEvent('change', { bubbles: true }); - await element.dispatchEvent(event); + await fireEvent(element, 'change'); }; -export const fillIn = async (element, content) => { +export const fillIn = async (element, content, event = 'change') => { element.value = content; - await fireChangeEvent(element); + await fireEvent(element, event); }; export const selectEvent = async (element, value) => { diff --git a/packages/library/tests/mixins/form-element.test.js b/packages/library/tests/mixins/form-element.test.js index 82737320..7fc72adb 100644 --- a/packages/library/tests/mixins/form-element.test.js +++ b/packages/library/tests/mixins/form-element.test.js @@ -10,7 +10,7 @@ const MuonFormElement = class extends FormElementMixin(MuonElement) { get standardTemplate() { const classes = { 'slotted-content': true, - 'select-arrow': this._inputType === this._isSelect + 'select-arrow': this._isSelect }; return html ` @@ -62,12 +62,12 @@ const MuonFormElement = class extends FormElementMixin(MuonElement) { if (this._isSelect) { const classes = { 'slotted-content': true, - 'select-arrow': this._inputType === this._isSelect + 'select-arrow': this._isSelect }; return html `
- ${this._isMultiple ? this._headingTemplate : this._labelTemplate} + ${this._labelTemplate}
${super.standardTemplate}
From d82ab59384f3b138b38adced40142c51dd02cec0 Mon Sep 17 00:00:00 2001 From: MekalaNagarajan-Centrica Date: Wed, 5 Jan 2022 13:28:15 +0000 Subject: [PATCH 04/10] feat: mask mixin --- .../inputter/src/inputter-component.js | 21 +-- packages/library/mixins/mask-mixin.js | 130 ++++++++++++++++++ 2 files changed, 135 insertions(+), 16 deletions(-) create mode 100644 packages/library/mixins/mask-mixin.js diff --git a/packages/library/components/inputter/src/inputter-component.js b/packages/library/components/inputter/src/inputter-component.js index 597447bf..8b7440c1 100644 --- a/packages/library/components/inputter/src/inputter-component.js +++ b/packages/library/components/inputter/src/inputter-component.js @@ -1,10 +1,10 @@ -import { html, MuonElement, classMap, styleMap, ifDefined } from '@muons/library'; +import { html, MuonElement, classMap, styleMap } from '@muons/library'; import { INPUTTER_TYPE } from '@muons/library/build/tokens/es6/muon-tokens'; import { ValidationMixin } from '@muons/library/mixins/validation-mixin'; -import { MaskSeparatorController } from '@muons/library/controllers/mask-separator-controller'; -import { maskInput } from '@muons/library/directives/mask-input'; +import { MaskMixin } from '@muons/library/mixins/mask-mixin'; + import styles from './styles.css'; /** @@ -14,13 +14,11 @@ import styles from './styles.css'; * */ -export class Inputter extends ValidationMixin(MuonElement) { +export class Inputter extends MaskMixin(ValidationMixin(MuonElement)) { static get properties() { return { helper: { type: String }, - mask: { type: String }, - separator: { type: String }, isHelperOpen: { type: Boolean } }; } @@ -34,15 +32,6 @@ export class Inputter extends ValidationMixin(MuonElement) { this.type = INPUTTER_TYPE; this.isHelperOpen = false; - this.mask = ''; - this.separator = ''; - } - - firstUpdated() { - super.firstUpdated(); - if (ifDefined(this.mask)) { - this.addController(new MaskSeparatorController(this, this._slottedInputs[0])); - } } get validity() { @@ -68,7 +57,7 @@ export class Inputter extends ValidationMixin(MuonElement) { ${this._isMultiple ? this._headingTemplate : this._labelTemplate}
${this._htmlFormElementTemplate} - ${this.mask ? maskInput(this.mask, this.separator, this.value) : ``} + ${this._maskTemplate}
${this._validationMessageTemplate}`; } diff --git a/packages/library/mixins/mask-mixin.js b/packages/library/mixins/mask-mixin.js new file mode 100644 index 00000000..52550123 --- /dev/null +++ b/packages/library/mixins/mask-mixin.js @@ -0,0 +1,130 @@ +import { html, ifDefined } from '@muons/library'; +import { dedupeMixin } from '@open-wc/dedupe-mixin'; +import { FormElementMixin } from './form-element-mixin'; + +export const MaskMixin = dedupeMixin((superclass) => + class MaskMixinClass extends FormElementMixin(superclass) { + static get properties() { + return { + mask: { + type: String + }, + + separator: { + type: String + } + }; + } + + constructor() { + super(); + + this.mask = ''; + this.separator = ''; + } + + firstUpdated() { + super.firstUpdated(); + + if (this.mask) { + this._slottedInputs.map((input) => { + input.addEventListener('input', this._onInput.bind(this)()); + input.setAttribute('maxlength', this.mask.length); + }); + } + } + + _onInput(inputEvent) { + const args = inputEvent; + let timeout; + return () => { + clearTimeout(timeout); + timeout = setTimeout(() => { + this.__inputHandler.apply(this, args); + }, 300); + }; + } + + /** + * A method to handle `input` event when `mask` is provided. + * @param {Event} inputEvent - event while 'input. + * @returns {undefined} + */ + __inputHandler() { + const inputElement = this._slottedInputs[0]; + if (ifDefined(this.separator)) { + this.updateValue(inputElement); + } else { + this.value = inputElement.value; + } + } + + _processValue(value) { + value = super._processValue(value); + if (ifDefined(this.separator)) { + value = this.formatWithMaskAndSeparator(value); + } + return value; + } + + updateValue(input) { + let value = input.value; + let cursor = input.selectionStart; + const diff = this.value.length - value.length; + + if (diff > 0 && this.mask.charAt(cursor) === this.separator) { + value = value.slice(0, cursor - 1) + (cursor < value.length ? value.slice(cursor) : ''); + cursor -= 1; + } + const formattedValue = this.formatWithMaskAndSeparator(value); + input.value = formattedValue; + this.value = formattedValue; + + if (this.mask.charAt(cursor) === this.separator) { + cursor += 1; + } + this.updateComplete.then(() => { + input.setSelectionRange(cursor, cursor); + }); + } + + formatWithMaskAndSeparator(value) { + const formattedValue = this.__formatInputWithoutSeparator(value); + const parts = this.mask.split(this.separator); + let processedValue = ''; + let length = 0; + let partsLength = 0; + for (let i = 0; i < parts.length && length < formattedValue.length; i++) { + partsLength += parts[i].length; + const remainingLength = formattedValue.length - length; + processedValue += formattedValue.substr( + length, remainingLength > parts[i].length ? parts[i].length : remainingLength); + length += parts[i].length; + if (i < (parts.length - 1) && processedValue.length === partsLength) { + processedValue += this.separator; + partsLength += 1; + } + } + return processedValue; + } + + __formatInputWithoutSeparator(value) { + return value.split(this.separator).join(''); + } + + get _maskTemplate() { + if (this.mask) { + let length = this.value ? this.value.length : 0; + let updatedMask = new Array(length + 1).join(' '); + if (this.separator && this.mask.charAt(length) === this.separator) { + updatedMask += ' '; + length += 1; + } + updatedMask += this.mask.slice(length); + return html``; + } else { + return undefined; + } + } + } +); From b51e46e5924719b90ab160dc657138ab216bc47a Mon Sep 17 00:00:00 2001 From: MekalaNagarajan-Centrica Date: Wed, 5 Jan 2022 15:04:51 +0000 Subject: [PATCH 05/10] fix: test failures --- .../controllers/mask-separator-controller.js | 75 ------------------- packages/library/directives/mask-input.js | 22 ------ .../components/inputter/inputter.test.js | 30 +++++++- .../tests/controllers/mask-separator.test.js | 33 -------- .../tests/directives/mask-input.test.js | 74 ------------------ 5 files changed, 29 insertions(+), 205 deletions(-) delete mode 100644 packages/library/controllers/mask-separator-controller.js delete mode 100644 packages/library/directives/mask-input.js delete mode 100644 packages/library/tests/controllers/mask-separator.test.js delete mode 100644 packages/library/tests/directives/mask-input.test.js diff --git a/packages/library/controllers/mask-separator-controller.js b/packages/library/controllers/mask-separator-controller.js deleted file mode 100644 index 9a2ca52d..00000000 --- a/packages/library/controllers/mask-separator-controller.js +++ /dev/null @@ -1,75 +0,0 @@ -import { ifDefined } from '@muons/library'; -export class MaskSeparatorController { - - constructor(host, input) { - this.host = host; - this.mask = host.mask; - this.separator = host.separator; - this.input = input; - } - - hostConnected() { - this.input.addEventListener('input', this.__onInput.bind(this)); - this.input.setAttribute('maxlength', this.mask.length); - } - - /** - * A method to handle `input` event when `mask` is provided. - * @param {Event} inputEvent - event while 'input. - * @returns {undefined} - */ - __onInput(inputEvent) { - inputEvent.stopPropagation(); - inputEvent.preventDefault(); - if (ifDefined(this.separator)) { - this.updateValue(); - } else { - this.host.value = this.input.value; - } - } - - updateValue() { - let value = this.input.value; - let cursor = this.input.selectionStart; - const diff = this.host.value.length - value.length; - - if (diff > 0 && this.mask.charAt(cursor) === this.separator) { - value = value.slice(0, cursor - 1) + (cursor < value.length ? value.slice(cursor) : ''); - cursor -= 1; - } - const formattedValue = this.formatWithMaskAndSeparator(value); - this.input.value = formattedValue; - this.host.value = formattedValue; - - if (this.mask.charAt(cursor) === this.separator) { - cursor += 1; - } - this.host.updateComplete.then(() => { - this.input.setSelectionRange(cursor, cursor); - }); - } - - formatWithMaskAndSeparator(value) { - const formattedValue = this.__formatInputWithoutSeparator(value); - const parts = this.mask.split(this.separator); - let processedValue = ''; - let length = 0; - let partsLength = 0; - for (let i = 0; i < parts.length && length < formattedValue.length; i++) { - partsLength += parts[i].length; - const remainingLength = formattedValue.length - length; - processedValue += formattedValue.substr( - length, remainingLength > parts[i].length ? parts[i].length : remainingLength); - length += parts[i].length; - if (i < (parts.length - 1) && processedValue.length === partsLength) { - processedValue += this.separator; - partsLength += 1; - } - } - return processedValue; - } - - __formatInputWithoutSeparator(value) { - return value.split(this.separator).join(''); - } -} diff --git a/packages/library/directives/mask-input.js b/packages/library/directives/mask-input.js deleted file mode 100644 index c5247c03..00000000 --- a/packages/library/directives/mask-input.js +++ /dev/null @@ -1,22 +0,0 @@ -import { Directive, directive, html } from '@muons/library'; - -class MaskInputDirective extends Directive { - - constructor(partInfo) { - super(partInfo); - } - - render(mask, separator, value) { - let length = value ? value.length : 0; - let updatedMask = new Array(length + 1).join(' '); - if (separator && mask.charAt(length) === separator) { - updatedMask += ' '; - length += 1; - } - updatedMask += mask.slice(length); - return html``; - } - -} - -export const maskInput = directive(MaskInputDirective); diff --git a/packages/library/tests/components/inputter/inputter.test.js b/packages/library/tests/components/inputter/inputter.test.js index 35ac1ca6..485368c0 100644 --- a/packages/library/tests/components/inputter/inputter.test.js +++ b/packages/library/tests/components/inputter/inputter.test.js @@ -1,5 +1,5 @@ /* eslint-disable no-undef */ -import { expect, fixture, html, defineCE, unsafeStatic } from '@open-wc/testing'; +import { expect, fixture, html, defineCE, unsafeStatic, waitUntil } from '@open-wc/testing'; import { Inputter } from '@muons/library/components/inputter'; import { defaultChecks, fillIn } from '../../helpers'; @@ -35,6 +35,7 @@ describe('Inputter', () => { it('input value `1`', async () => { await fillIn(inputElement, '1', 'input'); + await waitUntil(() => inputter.value === '1'); maskedInput = shadowRoot.querySelector('.input-mask'); expect(inputter.value).to.be.equal('1', 'Inputter has correct value'); expect(maskedInput.textContent).to.be.equal(' 0000', '`input-mask` has correct value'); @@ -42,6 +43,7 @@ describe('Inputter', () => { it('input value `12`', async () => { await fillIn(inputElement, '12', 'input'); + await waitUntil(() => inputter.value === '12'); maskedInput = shadowRoot.querySelector('.input-mask'); expect(inputter.value).to.be.equal('12', 'Inputter has correct value'); expect(maskedInput.textContent).to.be.equal(' 000', '`input-mask` has correct value'); @@ -75,6 +77,7 @@ describe('Inputter', () => { it('input value `1`', async () => { await fillIn(inputElement, '1', 'input'); + await waitUntil(() => inputter.value === '1'); maskedInput = inputter.shadowRoot.querySelector('.input-mask'); expect(inputter.value).to.be.equal('1', 'Inputter has correct value'); expect(maskedInput.textContent).to.be.equal(' 0-00-00', '`input-mask` has correct value'); @@ -82,9 +85,34 @@ describe('Inputter', () => { it('input value `12`', async () => { await fillIn(inputElement, '12', 'input'); + await waitUntil(() => inputter.value === '12-'); maskedInput = inputter.shadowRoot.querySelector('.input-mask'); expect(inputter.value).to.be.equal('12-', 'Inputter has correct value'); expect(maskedInput.textContent).to.be.equal(' 00-00', '`input-mask` has correct value'); }); + + it('input value `123`', async () => { + await fillIn(inputElement, '123', 'input'); + await waitUntil(() => inputter.value === '12-3'); + maskedInput = inputter.shadowRoot.querySelector('.input-mask'); + expect(inputter.value).to.be.equal('12-3', 'Inputter has correct value'); + expect(maskedInput.textContent).to.be.equal(' 0-00', '`input-mask` has correct value'); + }); + + it('delete value `3`', async () => { + await fillIn(inputElement, '1', 'input'); + await waitUntil(() => inputter.value === '1'); + maskedInput = inputter.shadowRoot.querySelector('.input-mask'); + expect(inputter.value).to.be.equal('1', 'Inputter has correct value'); + expect(maskedInput.textContent).to.be.equal(' 0-00-00', '`input-mask` has correct value'); + }); + + it('input value `123`', async () => { + await fillIn(inputElement, '123'); + await waitUntil(() => inputter.value === '12-3'); + maskedInput = inputter.shadowRoot.querySelector('.input-mask'); + expect(inputter.value).to.be.equal('12-3', 'Inputter has correct value'); + expect(maskedInput.textContent).to.be.equal(' 0-00', '`input-mask` has correct value'); + }); }); }); diff --git a/packages/library/tests/controllers/mask-separator.test.js b/packages/library/tests/controllers/mask-separator.test.js deleted file mode 100644 index a21adec7..00000000 --- a/packages/library/tests/controllers/mask-separator.test.js +++ /dev/null @@ -1,33 +0,0 @@ -/* eslint-disable no-undef */ -import { expect } from '@open-wc/testing'; -import { MaskSeparatorController } from '@muons/library/controllers/mask-separator-controller'; - -describe('mask-separator', async () => { - describe('mask ` - - `, separator `-`', async () => { - const host = { - mask: '00-00-00', - separator: '-' - }; - const maskController = new MaskSeparatorController(host); - - it('controller setup mask', async () => { - expect(maskController.mask).to.be.equal('00-00-00', 'controller has correct mask value'); - }); - - it('controller setup seprator', async () => { - expect(maskController.separator).to.be.equal('-', 'controller has correct separator value'); - }); - - it('update value', async () => { - expect(maskController.formatWithMaskAndSeparator('1')).to.be.equal('1', 'processed value has correct value'); - }); - - it('update value without separator', async () => { - expect(maskController.formatWithMaskAndSeparator('12')).to.be.equal('12-', 'processed value has correct value'); - }); - - it('update value with separator', async () => { - expect(maskController.formatWithMaskAndSeparator('1-23')).to.be.equal('12-3', 'processed value has correct value'); - }); - }); -}); diff --git a/packages/library/tests/directives/mask-input.test.js b/packages/library/tests/directives/mask-input.test.js deleted file mode 100644 index 4fab6707..00000000 --- a/packages/library/tests/directives/mask-input.test.js +++ /dev/null @@ -1,74 +0,0 @@ -/* eslint-disable no-undef */ -import { expect, html } from '@open-wc/testing'; -import { render } from 'lit'; -import { maskInput } from '@muons/library/directives/mask-input'; - -describe('mask', async () => { - describe('mask initial 00000', async () => { - let maskedInput; - before(async () => { - maskedInput = document.createElement('div'); - render(html`${maskInput('00000', '', '')}`, maskedInput); - }); - - it('directive element present check', async () => { - expect(maskedInput).to.be.not.null; // eslint-disable-line no-unused-expressions - }); - - it('directive masked value', async () => { - const maskedValue = maskedInput.querySelector('.input-mask'); - expect(maskedValue.textContent).to.be.equal('00000', 'directive has correct mask value'); - }); - }); - - describe('mask with value 00000', async () => { - let maskedInput; - before(async () => { - maskedInput = document.createElement('div'); - render(html`${maskInput('00000', '', '12')}`, maskedInput); - }); - - it('directive element present check', async () => { - expect(maskedInput).to.be.not.null; // eslint-disable-line no-unused-expressions - }); - - it('directive masked value', async () => { - const maskedValue = maskedInput.querySelector('.input-mask'); - expect(maskedValue.textContent).to.be.equal(' 000', 'directive has correct mask value'); - }); - }); - - describe('mask with separator 00-000', async () => { - let maskedInput; - before(async () => { - maskedInput = document.createElement('div'); - render(html`${maskInput('00-000', '-', '')}`, maskedInput); - }); - - it('directive element present check', async () => { - expect(maskedInput).to.be.not.null; // eslint-disable-line no-unused-expressions - }); - - it('directive masked value', async () => { - const maskedValue = maskedInput.querySelector('.input-mask'); - expect(maskedValue.textContent).to.be.equal('00-000', 'directive has correct mask value'); - }); - }); - - describe('mask with separator & value 00-000', async () => { - let maskedInput; - before(async () => { - maskedInput = document.createElement('div'); - render(html`${maskInput('00-000', '-', '12')}`, maskedInput); - }); - - it('directive element present check', async () => { - expect(maskedInput).to.be.not.null; // eslint-disable-line no-unused-expressions - }); - - it('directive masked value', async () => { - const maskedValue = maskedInput.querySelector('.input-mask'); - expect(maskedValue.textContent).to.be.equal(' 000', 'directive has correct mask value'); - }); - }); -}); From 28a15d335d858f5ee00a38c3e476ac4597551a45 Mon Sep 17 00:00:00 2001 From: jholt1 Date: Thu, 6 Jan 2022 14:15:58 +0000 Subject: [PATCH 06/10] refactor: make format easier to read --- packages/library/mixins/mask-mixin.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/library/mixins/mask-mixin.js b/packages/library/mixins/mask-mixin.js index 52550123..8097d66b 100644 --- a/packages/library/mixins/mask-mixin.js +++ b/packages/library/mixins/mask-mixin.js @@ -93,18 +93,23 @@ export const MaskMixin = dedupeMixin((superclass) => const parts = this.mask.split(this.separator); let processedValue = ''; let length = 0; - let partsLength = 0; + let currentLength = 0; + for (let i = 0; i < parts.length && length < formattedValue.length; i++) { - partsLength += parts[i].length; const remainingLength = formattedValue.length - length; - processedValue += formattedValue.substr( - length, remainingLength > parts[i].length ? parts[i].length : remainingLength); - length += parts[i].length; - if (i < (parts.length - 1) && processedValue.length === partsLength) { + const splitPoint = remainingLength > parts[i].length ? parts[i].length : remainingLength; + + processedValue += formattedValue.substr(length, splitPoint); + currentLength += parts[i].length; + + if (i < (parts.length - 1) && processedValue.length === currentLength) { processedValue += this.separator; - partsLength += 1; + currentLength += 1; } + + length += parts[i].length; } + return processedValue; } From 0f18c41ecedab92d8c81d15c457bd39956bf1e67 Mon Sep 17 00:00:00 2001 From: MekalaNagarajan-Centrica Date: Fri, 7 Jan 2022 16:41:09 +0000 Subject: [PATCH 07/10] fix: test fixtures --- .../inputter/src/inputter-component.js | 10 +- packages/library/components/inputter/story.js | 9 +- packages/library/mixins/mask-mixin.js | 52 +++-- .../components/inputter/inputter.test.js | 140 +++++-------- packages/library/tests/mixins/mask.test.js | 187 ++++++++++++++++++ .../library/tests/mixins/validation.test.js | 2 +- 6 files changed, 282 insertions(+), 118 deletions(-) create mode 100644 packages/library/tests/mixins/mask.test.js diff --git a/packages/library/components/inputter/src/inputter-component.js b/packages/library/components/inputter/src/inputter-component.js index 8b7440c1..d55da49d 100644 --- a/packages/library/components/inputter/src/inputter-component.js +++ b/packages/library/components/inputter/src/inputter-component.js @@ -8,10 +8,10 @@ import { MaskMixin } from '@muons/library/mixins/mask-mixin'; import styles from './styles.css'; /** - * Allow for inputs + * A component to allow for user inputs of type text, radio, checkbox, select, + * date, tel, number, textarea, search. * * @element inputter - * */ export class Inputter extends MaskMixin(ValidationMixin(MuonElement)) { @@ -34,12 +34,6 @@ export class Inputter extends MaskMixin(ValidationMixin(MuonElement)) { this.isHelperOpen = false; } - get validity() { - this.pristine = false; - this.validate(); - return this._validity; - } - get standardTemplate() { const classes = { 'slotted-content': true, diff --git a/packages/library/components/inputter/story.js b/packages/library/components/inputter/story.js index 727300c0..c8d05616 100644 --- a/packages/library/components/inputter/story.js +++ b/packages/library/components/inputter/story.js @@ -70,4 +70,11 @@ const innerInputTel = (args) => ` `; export const Tel = (args) => details.template(args, innerInputTel); -Tel.args = { label: 'A label', value: '', validation: '["isRequired"]' }; +Tel.args = { label: 'A label', value: '', validation: '["isRequired"]', mask: '000-000-0000', separator: '-' }; + +const innerInputNumber = (args) => ` + + +`; +export const Number = (args) => details.template(args, innerInputNumber); +Number.args = { label: 'A label', value: '', validation: '["isRequired"]' }; diff --git a/packages/library/mixins/mask-mixin.js b/packages/library/mixins/mask-mixin.js index 8097d66b..00fee662 100644 --- a/packages/library/mixins/mask-mixin.js +++ b/packages/library/mixins/mask-mixin.js @@ -2,6 +2,13 @@ import { html, ifDefined } from '@muons/library'; import { dedupeMixin } from '@open-wc/dedupe-mixin'; import { FormElementMixin } from './form-element-mixin'; +/** + * A mixin to enable mask and separator features to a form element. + * `mask` property is supported for input of type text, date, tel. + * `separator` property is supported for input of type text, date, tel. + * @mixin + */ + export const MaskMixin = dedupeMixin((superclass) => class MaskMixinClass extends FormElementMixin(superclass) { static get properties() { @@ -28,29 +35,22 @@ export const MaskMixin = dedupeMixin((superclass) => if (this.mask) { this._slottedInputs.map((input) => { - input.addEventListener('input', this._onInput.bind(this)()); + input.addEventListener('input', this._onInput.bind(this)); input.setAttribute('maxlength', this.mask.length); }); } } - _onInput(inputEvent) { - const args = inputEvent; - let timeout; - return () => { - clearTimeout(timeout); - timeout = setTimeout(() => { - this.__inputHandler.apply(this, args); - }, 300); - }; - } - /** * A method to handle `input` event when `mask` is provided. * @param {Event} inputEvent - event while 'input. * @returns {undefined} + * @protected + * @override */ - __inputHandler() { + _onInput(inputEvent) { + inputEvent.stopPropagation(); + inputEvent.preventDefault(); const inputElement = this._slottedInputs[0]; if (ifDefined(this.separator)) { this.updateValue(inputElement); @@ -63,10 +63,17 @@ export const MaskMixin = dedupeMixin((superclass) => value = super._processValue(value); if (ifDefined(this.separator)) { value = this.formatWithMaskAndSeparator(value); + this._slottedInputs[0].value = value; } return value; } + /** + * A method to update the form element value with separator in adjusted indices and cursor position. + * + * @param {HTMLInputElement} input - HTMLInputElement value to be updated with seperators + * @returns {undefined} + */ updateValue(input) { let value = input.value; let cursor = input.selectionStart; @@ -88,6 +95,13 @@ export const MaskMixin = dedupeMixin((superclass) => }); } + /** + * A method to format the form element value with separator adjusted to correct indices + * after editing the form element value. + * + * @param {String} value - value of the form element. + * @return {String} - value with adjusted separator in correct indices. + */ formatWithMaskAndSeparator(value) { const formattedValue = this.__formatInputWithoutSeparator(value); const parts = this.mask.split(this.separator); @@ -113,18 +127,20 @@ export const MaskMixin = dedupeMixin((superclass) => return processedValue; } + /** + * A method to remove separator from the value of the form element. + * + * @param {String} value - form element value. + * @return {String} - value with separator removed. + */ __formatInputWithoutSeparator(value) { return value.split(this.separator).join(''); } get _maskTemplate() { if (this.mask) { - let length = this.value ? this.value.length : 0; + const length = this.value ? this.value.length : 0; let updatedMask = new Array(length + 1).join(' '); - if (this.separator && this.mask.charAt(length) === this.separator) { - updatedMask += ' '; - length += 1; - } updatedMask += this.mask.slice(length); return html``; } else { diff --git a/packages/library/tests/components/inputter/inputter.test.js b/packages/library/tests/components/inputter/inputter.test.js index 485368c0..4e79d64e 100644 --- a/packages/library/tests/components/inputter/inputter.test.js +++ b/packages/library/tests/components/inputter/inputter.test.js @@ -1,118 +1,78 @@ /* eslint-disable no-undef */ -import { expect, fixture, html, defineCE, unsafeStatic, waitUntil } from '@open-wc/testing'; +import { expect, fixture, html, defineCE, unsafeStatic } from '@open-wc/testing'; import { Inputter } from '@muons/library/components/inputter'; -import { defaultChecks, fillIn } from '../../helpers'; +import { defaultChecks } from '../../helpers'; const tagName = defineCE(Inputter); const tag = unsafeStatic(tagName); describe('Inputter', () => { - - describe('mask', async () => { + describe('standard default', async () => { let inputter; - let shadowRoot; - let maskedInput; - let inputElement; before(async () => { inputter = await fixture(html` - <${tag} mask="00000"> - - + <${tag}> `); - shadowRoot = inputter.shadowRoot; - maskedInput = shadowRoot.querySelector('.input-mask'); - inputElement = inputter.querySelector('input'); }); it('default checks', async () => { await defaultChecks(inputter); }); - it('masked input check', async () => { - expect(maskedInput).to.be.not.null; // eslint-disable-line no-unused-expressions - expect(maskedInput.textContent).to.be.equal('00000', '`input-mask` has correct value'); - }); - - it('input value `1`', async () => { - await fillIn(inputElement, '1', 'input'); - await waitUntil(() => inputter.value === '1'); - maskedInput = shadowRoot.querySelector('.input-mask'); - expect(inputter.value).to.be.equal('1', 'Inputter has correct value'); - expect(maskedInput.textContent).to.be.equal(' 0000', '`input-mask` has correct value'); - }); - - it('input value `12`', async () => { - await fillIn(inputElement, '12', 'input'); - await waitUntil(() => inputter.value === '12'); - maskedInput = shadowRoot.querySelector('.input-mask'); - expect(inputter.value).to.be.equal('12', 'Inputter has correct value'); - expect(maskedInput.textContent).to.be.equal(' 000', '`input-mask` has correct value'); + it('default properties', async() => { + expect(inputter.type).to.equal('standard', 'default type is set'); + expect(inputter.id).to.not.be.null; // eslint-disable-line no-unused-expressions }); }); + describe('text input', async () => { + describe('mask text', async () => { + let inputter; + let shadowRoot; + before(async () => { + inputter = await fixture(html` + <${tag} mask="0000"> + + + `); + shadowRoot = inputter.shadowRoot; + }); - describe('mask separator', async () => { - let inputter; - let shadowRoot; - let maskedInput; - let inputElement; - before(async () => { - inputter = await fixture(html` - <${tag} mask="00-00-00" separator="-"> - - - `); - shadowRoot = inputter.shadowRoot; - maskedInput = shadowRoot.querySelector('.input-mask'); - inputElement = inputter.querySelector('input'); - }); + it('default checks', async () => { + await defaultChecks(inputter); + }); - it('default checks', async () => { - await defaultChecks(inputter); - }); - - it('masked input check', async () => { - expect(maskedInput).to.be.not.null; // eslint-disable-line no-unused-expressions - expect(maskedInput.textContent).to.be.equal('00-00-00', '`input-mask` has correct value'); - }); - - it('input value `1`', async () => { - await fillIn(inputElement, '1', 'input'); - await waitUntil(() => inputter.value === '1'); - maskedInput = inputter.shadowRoot.querySelector('.input-mask'); - expect(inputter.value).to.be.equal('1', 'Inputter has correct value'); - expect(maskedInput.textContent).to.be.equal(' 0-00-00', '`input-mask` has correct value'); + it('default properties', async() => { + expect(inputter.type).to.equal('standard', 'default type is set'); + expect(inputter.id).to.not.be.null; // eslint-disable-line no-unused-expressions + expect(shadowRoot.querySelector('.has-mask')).to.not.be.null; // eslint-disable-line no-unused-expressions + }); }); + }); - it('input value `12`', async () => { - await fillIn(inputElement, '12', 'input'); - await waitUntil(() => inputter.value === '12-'); - maskedInput = inputter.shadowRoot.querySelector('.input-mask'); - expect(inputter.value).to.be.equal('12-', 'Inputter has correct value'); - expect(maskedInput.textContent).to.be.equal(' 00-00', '`input-mask` has correct value'); - }); + describe('radio input', async () => { + describe('standard radio', async () => { + let inputter; + let shadowRoot; + before(async () => { + inputter = await fixture(html` + <${tag} heading="What is your heating source?"> + + + + + `); + shadowRoot = inputter.shadowRoot; + }); - it('input value `123`', async () => { - await fillIn(inputElement, '123', 'input'); - await waitUntil(() => inputter.value === '12-3'); - maskedInput = inputter.shadowRoot.querySelector('.input-mask'); - expect(inputter.value).to.be.equal('12-3', 'Inputter has correct value'); - expect(maskedInput.textContent).to.be.equal(' 0-00', '`input-mask` has correct value'); - }); - - it('delete value `3`', async () => { - await fillIn(inputElement, '1', 'input'); - await waitUntil(() => inputter.value === '1'); - maskedInput = inputter.shadowRoot.querySelector('.input-mask'); - expect(inputter.value).to.be.equal('1', 'Inputter has correct value'); - expect(maskedInput.textContent).to.be.equal(' 0-00-00', '`input-mask` has correct value'); - }); + it('default checks', async () => { + await defaultChecks(inputter); + }); - it('input value `123`', async () => { - await fillIn(inputElement, '123'); - await waitUntil(() => inputter.value === '12-3'); - maskedInput = inputter.shadowRoot.querySelector('.input-mask'); - expect(inputter.value).to.be.equal('12-3', 'Inputter has correct value'); - expect(maskedInput.textContent).to.be.equal(' 0-00', '`input-mask` has correct value'); + it('default properties', async() => { + expect(inputter.type).to.equal('standard', 'default type is set'); + expect(inputter.id).to.not.be.null; // eslint-disable-line no-unused-expressions + expect(shadowRoot.querySelector('.input-heading')).to.not.be.null; // eslint-disable-line no-unused-expressions + }); }); }); }); diff --git a/packages/library/tests/mixins/mask.test.js b/packages/library/tests/mixins/mask.test.js new file mode 100644 index 00000000..efff6abb --- /dev/null +++ b/packages/library/tests/mixins/mask.test.js @@ -0,0 +1,187 @@ +/* eslint-disable no-undef */ +import { expect, fixture, html, defineCE, unsafeStatic, waitUntil } from '@open-wc/testing'; +import { MuonElement } from '@muons/library'; +import { MaskMixin } from '@muons/library/mixins/mask-mixin'; +import { defaultChecks, fillIn } from '../helpers'; +// import { Inputter } from '@muons/library/components/inputter'; + +const Inputter = class extends MaskMixin(MuonElement) { + get standardTemplate() { + return html` + ${this._labelTemplate} + ${this._htmlFormElementTemplate} + ${this._maskTemplate} + `; + } +}; +const tagName = defineCE(Inputter); +const tag = unsafeStatic(tagName); + +describe('mask & separator', () => { + describe('mask', async () => { + let inputter; + let shadowRoot; + let maskedInput; + let inputElement; + before(async () => { + inputter = await fixture(html` + <${tag} mask="00000" > + + + `); + shadowRoot = inputter.shadowRoot; + maskedInput = shadowRoot.querySelector('.input-mask'); + inputElement = inputter.querySelector('input'); + }); + + it('default checks', async () => { + await defaultChecks(inputter); + }); + + it('masked input check', async () => { + expect(maskedInput).to.be.not.null; // eslint-disable-line no-unused-expressions + expect(maskedInput.textContent).to.be.equal('00000', '`input-mask` has correct value'); + }); + + it('input value `1`', async () => { + await fillIn(inputElement, '1', 'input'); + await waitUntil(() => inputter.value === '1'); + maskedInput = shadowRoot.querySelector('.input-mask'); + expect(inputElement.value).to.be.equal('1', 'Input has correct value'); + expect(inputter.value).to.be.equal('1', 'Inputter has correct value'); + expect(maskedInput.textContent).to.be.equal(' 0000', '`input-mask` has correct value'); + }); + + it('input value `12`', async () => { + await fillIn(inputElement, '12', 'input'); + await waitUntil(() => inputter.value === '12'); + maskedInput = shadowRoot.querySelector('.input-mask'); + expect(inputElement.value).to.be.equal('12', 'Input has correct value'); + expect(inputter.value).to.be.equal('12', 'Inputter has correct value'); + expect(maskedInput.textContent).to.be.equal(' 000', '`input-mask` has correct value'); + }); + }); + + describe('mask separator', async () => { + let inputter; + let shadowRoot; + let maskedInput; + let inputElement; + before(async () => { + inputter = await fixture(html` + <${tag} mask="00-00-00" separator="-"> + + + `); + shadowRoot = inputter.shadowRoot; + maskedInput = shadowRoot.querySelector('.input-mask'); + inputElement = inputter.querySelector('input'); + }); + + it('default checks', async () => { + await defaultChecks(inputter); + }); + + it('masked input check', async () => { + expect(maskedInput).to.be.not.null; // eslint-disable-line no-unused-expressions + expect(maskedInput.textContent).to.be.equal('00-00-00', '`input-mask` has correct value'); + }); + + it('input value `1`', async () => { + await fillIn(inputElement, '1', 'input'); + await waitUntil(() => inputter.value === '1'); + maskedInput = inputter.shadowRoot.querySelector('.input-mask'); + expect(inputElement.value).to.be.equal('1', 'Input has correct value'); + expect(inputter.value).to.be.equal('1', 'Inputter has correct value'); + expect(maskedInput.textContent).to.be.equal(' 0-00-00', '`input-mask` has correct value'); + }); + + it('input value `12`', async () => { + await fillIn(inputElement, '12', 'input'); + await waitUntil(() => inputter.value === '12-'); + maskedInput = inputter.shadowRoot.querySelector('.input-mask'); + expect(inputElement.value).to.be.equal('12-', 'Input has correct value'); + expect(inputter.value).to.be.equal('12-', 'Inputter has correct value'); + expect(maskedInput.textContent).to.be.equal(' 00-00', '`input-mask` has correct value'); + }); + + it('input value `123`', async () => { + await fillIn(inputElement, '123', 'input'); + await waitUntil(() => inputter.value === '12-3'); + maskedInput = inputter.shadowRoot.querySelector('.input-mask'); + expect(inputElement.value).to.be.equal('12-3', 'Input has correct value'); + expect(inputter.value).to.be.equal('12-3', 'Inputter has correct value'); + expect(maskedInput.textContent).to.be.equal(' 0-00', '`input-mask` has correct value'); + }); + + it('delete value `3`', async () => { + await fillIn(inputElement, '12-', 'input'); + await waitUntil(() => inputter.value === '12-'); + maskedInput = inputter.shadowRoot.querySelector('.input-mask'); + expect(inputElement.value).to.be.equal('12-', 'Input has correct value'); + expect(inputter.value).to.be.equal('12-', 'Inputter has correct value'); + expect(maskedInput.textContent).to.be.equal(' 00-00', '`input-mask` has correct value'); + }); + + it('delete value `2`', async () => { + await fillIn(inputElement, '12', 'input'); + await waitUntil(() => inputter.value === '1'); + maskedInput = inputter.shadowRoot.querySelector('.input-mask'); + expect(inputElement.value).to.be.equal('1', 'Input has correct value'); + expect(inputter.value).to.be.equal('1', 'Inputter has correct value'); + expect(maskedInput.textContent).to.be.equal(' 0-00-00', '`input-mask` has correct value'); + }); + + it('input value `123`', async () => { + await fillIn(inputElement, '123'); + await waitUntil(() => inputter.value === '12-3'); + maskedInput = inputter.shadowRoot.querySelector('.input-mask'); + expect(inputElement.value).to.be.equal('12-3', 'Input has correct value'); + expect(inputter.value).to.be.equal('12-3', 'Inputter has correct value'); + expect(maskedInput.textContent).to.be.equal(' 0-00', '`input-mask` has correct value'); + }); + }); + + describe('tel mask seprator', async () => { + let inputter; + let shadowRoot; + let maskedInput; + let inputElement; + before(async () => { + inputter = await fixture(html` + <${tag} mask="00-00-00" separator="-"> + + + `); + shadowRoot = inputter.shadowRoot; + maskedInput = shadowRoot.querySelector('.input-mask'); + inputElement = inputter.querySelector('input'); + }); + + it('default checks', async () => { + await defaultChecks(inputter); + }); + + it('masked input check', async () => { + expect(maskedInput).to.be.not.null; // eslint-disable-line no-unused-expressions + expect(maskedInput.textContent).to.be.equal('00-00-00', '`input-mask` has correct value'); + }); + + it('input value `1`', async () => { + await fillIn(inputElement, '1', 'input'); + await waitUntil(() => inputter.value === '1'); + maskedInput = inputter.shadowRoot.querySelector('.input-mask'); + expect(inputter.value).to.be.equal('1', 'Inputter has correct value'); + expect(maskedInput.textContent).to.be.equal(' 0-00-00', '`input-mask` has correct value'); + }); + + it('input value `12`', async () => { + await fillIn(inputElement, '12', 'input'); + await waitUntil(() => inputter.value === '12-'); + maskedInput = inputter.shadowRoot.querySelector('.input-mask'); + expect(inputElement.value).to.be.equal('12-', 'Input has correct value'); + expect(inputter.value).to.be.equal('12-', 'Inputter has correct value'); + expect(maskedInput.textContent).to.be.equal(' 00-00', '`input-mask` has correct value'); + }); + }); +}); diff --git a/packages/library/tests/mixins/validation.test.js b/packages/library/tests/mixins/validation.test.js index 4266cf1a..cd5fdac5 100644 --- a/packages/library/tests/mixins/validation.test.js +++ b/packages/library/tests/mixins/validation.test.js @@ -3,7 +3,7 @@ import { expect, fixture, html, defineCE, unsafeStatic } from '@open-wc/testing' import { MuonElement } from '@muons/library'; import sinon from 'sinon'; import { defaultChecks, fillIn, selectEvent } from '../helpers'; -import { ValidationMixin } from '../../mixins/validation-mixin'; +import { ValidationMixin } from '@muons/library/mixins/validation-mixin'; const isFirstName = (inputter, value) => { const isName = /^[A-Za-zÀ-ÖØ-öø-ÿ\-\s]{1,24}$/i.test(value); From a9a06d49b53389e15b054379008e3ae1324c42ed Mon Sep 17 00:00:00 2001 From: MekalaNagarajan-Centrica Date: Mon, 10 Jan 2022 12:40:21 +0000 Subject: [PATCH 08/10] fix: mask template merge issue --- packages/library/components/inputter/src/inputter-component.js | 1 + packages/library/tests/components/inputter/inputter.test.js | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/library/components/inputter/src/inputter-component.js b/packages/library/components/inputter/src/inputter-component.js index 3680e685..9957f90c 100644 --- a/packages/library/components/inputter/src/inputter-component.js +++ b/packages/library/components/inputter/src/inputter-component.js @@ -105,6 +105,7 @@ export class Inputter extends ScopedElementsMixin(MaskMixin(ValidationMixin(Muon ${this._helperTemplate}
${super.standardTemplate} + ${this._maskTemplate}
${this._validationMessageTemplate}`; diff --git a/packages/library/tests/components/inputter/inputter.test.js b/packages/library/tests/components/inputter/inputter.test.js index 23e43cb6..4db85509 100644 --- a/packages/library/tests/components/inputter/inputter.test.js +++ b/packages/library/tests/components/inputter/inputter.test.js @@ -72,6 +72,7 @@ describe('Inputter', () => { expect(inputter.type).to.equal('standard', 'default type is set'); expect(inputter.id).to.not.be.null; // eslint-disable-line no-unused-expressions expect(shadowRoot.querySelector('.input-heading')).to.not.be.null; // eslint-disable-line no-unused-expressions + expect(shadowRoot.querySelector('.input-mask')).to.be.null; // eslint-disable-line no-unused-expressions }); }); }); From 3155828e4b1c8ee7c1e856809f4e1d649b0d755c Mon Sep 17 00:00:00 2001 From: MekalaNagarajan-Centrica Date: Thu, 13 Jan 2022 12:37:53 +0000 Subject: [PATCH 09/10] feat: update snapshot --- .../__snapshots__/inputter.test.snap.js | 55 +++++++++++++++++++ .../mixins/__snapshots__/mask.test.snap.js | 45 +++++++++++++++ packages/library/tests/mixins/mask.test.js | 1 - 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 packages/library/tests/components/inputter/__snapshots__/inputter.test.snap.js create mode 100644 packages/library/tests/mixins/__snapshots__/mask.test.snap.js diff --git a/packages/library/tests/components/inputter/__snapshots__/inputter.test.snap.js b/packages/library/tests/components/inputter/__snapshots__/inputter.test.snap.js new file mode 100644 index 00000000..1752d2dc --- /dev/null +++ b/packages/library/tests/components/inputter/__snapshots__/inputter.test.snap.js @@ -0,0 +1,55 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["Inputter standard default default checks"] = +`
+ + +
+ + +
+
+`; +/* end snapshot Inputter standard default default checks */ + +snapshots["Inputter text input mask text default checks"] = +`
+ + +
+ + + +
+
+`; +/* end snapshot Inputter text input mask text default checks */ + +snapshots["Inputter radio input standard radio default checks"] = +`
+ + What is your heating source? + +
+ + +
+
+`; +/* end snapshot Inputter radio input standard radio default checks */ + diff --git a/packages/library/tests/mixins/__snapshots__/mask.test.snap.js b/packages/library/tests/mixins/__snapshots__/mask.test.snap.js new file mode 100644 index 00000000..fe7e4d17 --- /dev/null +++ b/packages/library/tests/mixins/__snapshots__/mask.test.snap.js @@ -0,0 +1,45 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["mask & separator mask default checks"] = +` + + + + +`; +/* end snapshot mask & separator mask default checks */ + +snapshots["mask & separator mask separator default checks"] = +` + + + + +`; +/* end snapshot mask & separator mask separator default checks */ + +snapshots["mask & separator tel mask seprator default checks"] = +` + + + + +`; +/* end snapshot mask & separator tel mask seprator default checks */ + diff --git a/packages/library/tests/mixins/mask.test.js b/packages/library/tests/mixins/mask.test.js index efff6abb..12f68279 100644 --- a/packages/library/tests/mixins/mask.test.js +++ b/packages/library/tests/mixins/mask.test.js @@ -3,7 +3,6 @@ import { expect, fixture, html, defineCE, unsafeStatic, waitUntil } from '@open- import { MuonElement } from '@muons/library'; import { MaskMixin } from '@muons/library/mixins/mask-mixin'; import { defaultChecks, fillIn } from '../helpers'; -// import { Inputter } from '@muons/library/components/inputter'; const Inputter = class extends MaskMixin(MuonElement) { get standardTemplate() { From 48221c24df1622842f7bc1d7c80a0d34ccbebae3 Mon Sep 17 00:00:00 2001 From: MekalaNagarajan-Centrica Date: Wed, 19 Jan 2022 12:21:36 +0000 Subject: [PATCH 10/10] fix: form element snapshot changes --- .../tests/mixins/__snapshots__/form-element.test.snap.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/library/tests/mixins/__snapshots__/form-element.test.snap.js b/packages/library/tests/mixins/__snapshots__/form-element.test.snap.js index a2fe50d4..f1a5a4cc 100644 --- a/packages/library/tests/mixins/__snapshots__/form-element.test.snap.js +++ b/packages/library/tests/mixins/__snapshots__/form-element.test.snap.js @@ -52,7 +52,7 @@ snapshots["form-element standard checkbox input"] = /* end snapshot form-element standard checkbox input */ snapshots["form-element standard select input"] = -`
+`