Skip to content

Commit

Permalink
Merge pull request #492 from probil/fix/#303--enable-disable-mask-on-…
Browse files Browse the repository at this point in the history
…condition

Fix/#303  enable disable mask on condition
  • Loading branch information
probil committed Jul 21, 2020
2 parents 29a106d + d43dfc8 commit 95a9abf
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 11 deletions.
59 changes: 59 additions & 0 deletions src/__tests__/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,63 @@ describe('directive usage', () => {
});
expect(wrapper.vm.$el.value).toBe('$1,000,000.00');
});

it.each([
false, null, '', undefined,
])('allows input any value for `%p` mask', async (mask) => {
const wrapper = mountWithMask({
data: () => ({ mask, value: undefined }),
template: '<input v-mask="mask" v-model="value"/>',
});
wrapper.vm.$el.value = '11112011BC';
wrapper.trigger('input');
await wrapper.vm.$nextTick();
expect(wrapper.vm.$el.value).toBe('11112011BC');
});

it('should conform to mask when mask changes from falsy value back to real mask', async () => {
const wrapper = mountWithMask({
data: () => ({ mask: false, value: undefined }),
template: '<input v-mask="mask" v-model="value"/>',
});
wrapper.vm.$el.value = '11112011BC';
wrapper.trigger('input');
await wrapper.vm.$nextTick();
expect(wrapper.vm.$el.value).toBe('11112011BC');
wrapper.setData({ mask: '##:##' });
wrapper.vm.$el.value = '11112011BC';
wrapper.trigger('input');
await wrapper.vm.$nextTick();
expect(wrapper.vm.$el.value).toBe('11:11');
});

it('should allow any value when mask changes from real mask to falsy value', async () => {
const wrapper = mountWithMask({
data: () => ({ mask: '##:##', value: undefined }),
template: '<input v-mask="mask" v-model="value"/>',
});
wrapper.vm.$el.value = '11112011BC';
wrapper.trigger('input');
await wrapper.vm.$nextTick();
expect(wrapper.vm.$el.value).toBe('11:11');
wrapper.setData({ mask: '' });
wrapper.vm.$el.value = '11112011BC';
wrapper.trigger('input');
await wrapper.vm.$nextTick();
expect(wrapper.vm.$el.value).toBe('11112011BC');
});

it('should preserve entered value after setting mask to falsy value', async () => {
const wrapper = mountWithMask({
data: () => ({ mask: '##:##', value: undefined }),
template: '<input v-mask="mask" v-model="value"/>',
});
wrapper.vm.$el.value = '19323V';
wrapper.trigger('input');
await wrapper.vm.$nextTick();
wrapper.setData({ mask: '' });
wrapper.trigger('input');
await wrapper.vm.$nextTick();
expect(wrapper.vm.$el.value).toBe('19:32');
});
});
24 changes: 13 additions & 11 deletions src/directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import conformToMask from 'text-mask-core/src/conformToMask';
import { stringMaskToRegExpMask, arrayMaskToRegExpMask } from './maskToRegExpMask';
import { trigger, queryInputElementInside } from './utils';
import {
trigger, queryInputElementInside, isFunction, isString, isRegexp,
} from './utils';
import createOptions from './createOptions';
import { defaultMaskReplacers } from './constants';

Expand Down Expand Up @@ -31,7 +33,7 @@ function updateValue(el, force = false) {
const isLengthIncreased = value.length > previousValue.length;
const isUpdateNeeded = value && isValueChanged && isLengthIncreased;

if (force || isUpdateNeeded) {
if ((force || isUpdateNeeded) && mask) {
const { conformedValue } = conformToMask(value, mask, { guide: false });
el.value = conformedValue;
triggerInputUpdate(el);
Expand All @@ -42,18 +44,20 @@ function updateValue(el, force = false) {

/**
* Fires on handler update
* @param {HTMLInputElement} el
* @param {String|Array.<String|RegExp>|Function} inputMask
* @param {HTMLInputElement} el
* @param {String|Array.<String|RegExp>|Function|null} inputMask
*/
function updateMask(el, inputMask, maskReplacers) {
let mask = null;
let mask;

if (Array.isArray(inputMask)) {
mask = arrayMaskToRegExpMask(inputMask, maskReplacers);
} else if (typeof inputMask === 'function') {
} else if (isFunction(inputMask)) {
mask = inputMask;
} else {
} else if (isString(inputMask) && inputMask.length > 0) {
mask = stringMaskToRegExpMask(inputMask, maskReplacers);
} else {
mask = inputMask;
}

options.partiallyUpdate(el, { mask });
Expand Down Expand Up @@ -87,7 +91,7 @@ function extendMaskReplacers(maskReplacers, baseMaskReplacers = defaultMaskRepla
*/
function maskToString(mask) {
const maskArray = Array.isArray(mask) ? mask : [mask];
const filteredMaskArray = maskArray.filter((part) => typeof part === 'string' || part instanceof RegExp);
const filteredMaskArray = maskArray.filter((part) => isString(part) || isRegexp(part));
return filteredMaskArray.toString();
}

Expand Down Expand Up @@ -135,15 +139,13 @@ export function createDirective(directiveOptions = {}) {
componentUpdated(el, { value, oldValue }) {
el = queryInputElementInside(el);

const isMaskChanged = typeof value === 'function'
const isMaskChanged = isFunction(value)
|| maskToString(oldValue) !== maskToString(value);

// update mask first if changed
if (isMaskChanged) {
updateMask(el, value, instanceMaskReplacers);
}

// update value
updateValue(el, isMaskChanged);
},

Expand Down
21 changes: 21 additions & 0 deletions src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,24 @@ export const queryInputElementInside = (el) => (
? el
: el.querySelector('input') || el
);

/**
* Determines whether the passed value is a function
* @param {*} val
* @returns {boolean}
*/
export const isFunction = (val) => typeof val === 'function';

/**
* Determines whether the passed value is a string
* @param {*} val
* @returns {boolean}
*/
export const isString = (val) => typeof val === 'string';

/**
* Determines whether the passed value is a string
* @param {*} val
* @returns {boolean}
*/
export const isRegexp = (val) => val instanceof RegExp;
9 changes: 9 additions & 0 deletions tests/e2e/spec/real-word-masks.html
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@
v-mask="currencyMask"
v-model="models.currency"
/>
<hr>
<input
id="empty-mask"
type="text"
placeholder="anything"
v-mask="false"
v-model="models.emptyMask"
/>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/text-mask-addons@3.8.0/dist/textMaskAddons.min.js"></script>
Expand Down Expand Up @@ -130,6 +138,7 @@
socialSecurityNumber: '',
timeRange: '',
currency: '',
emptyMask: ''
}
}
},
Expand Down
8 changes: 8 additions & 0 deletions tests/e2e/spec/real-word-masks.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,11 @@ test('Currency number input', async (t) => {
.typeText(el, '1000')
.expect(el.value).eql('$1,000');
});

test('Empty mask', async (t) => {
const el = Selector('input#empty-mask');

await t
.typeText(el, 'Anything can be entered here, $1,000!')
.expect(el.value).eql('Anything can be entered here, $1,000!');
});

0 comments on commit 95a9abf

Please sign in to comment.