diff --git a/packages/input/__tests__/input.spec.ts b/packages/input/__tests__/input.spec.ts index cdb639f1d3..a62b97a263 100644 --- a/packages/input/__tests__/input.spec.ts +++ b/packages/input/__tests__/input.spec.ts @@ -45,7 +45,7 @@ describe('Input.vue', () => { expect(nativeInput.placeholder).toBe('请输入内容') expect(nativeInput.value).toBe('input') expect(nativeInput.minLength).toBe(3) - expect(nativeInput.maxLength).toBe(5) + // expect(nativeInput.maxLength).toBe(5) // The maxlength attribute is no longer a native attribute vm.input = 'text' await sleep() @@ -68,6 +68,61 @@ describe('Input.vue', () => { expect(inputElm.element.disabled).not.toBeNull() }) + describe('test emoji',()=>{ + test('el-input should minimize value between emoji length and maxLength', async () => { + const wrapper = _mount({ + template: ``, + setup() { + const inputVal = ref('12🌚') + return { inputVal } + }, + }) + const vm = wrapper.vm + const inputElm = wrapper.find('input') + const nativeInput = inputElm.element + expect(nativeInput.value).toBe('12🌚') + + const elCount = wrapper.find('.el-input__count-inner') + expect(elCount.exists()).toBe(true) + expect(elCount.text()).toBe('3/4') + + vm.inputVal = '1👌3😄' + await sleep() + expect(nativeInput.value).toBe('1👌3😄') + expect(elCount.text()).toBe('4/4') + + vm.inputVal = '哈哈1👌3😄' + await sleep() + expect(nativeInput.value).toBe('哈哈1👌3😄') + expect(elCount.text()).toBe('6/4') + expect(vm.$el.classList.contains('is-exceed')).toBe(true) + }) + + test('textarea should minimize value between emoji length and maxLength', async () => { + const wrapper = _mount({ + template: ``, + setup() { + const inputVal = ref('啊好😄') + return { inputVal } + }, + }) + const vm = wrapper.vm + const inputElm = wrapper.find('textarea') + const nativeInput = inputElm.element + expect(nativeInput.value).toBe('啊好😄') + + const elCount = wrapper.find('.el-input__count') + expect(elCount.exists()).toBe(true) + expect(elCount.text()).toBe('3/4') + + vm.inputVal = '哈哈1👌3😄' + await sleep() + expect(nativeInput.value).toBe('哈哈1👌3😄') + expect(elCount.text()).toBe('6/4') + expect(vm.$el.classList.contains('is-exceed')).toBe(true) + }) + }) + test('suffixIcon', () => { const wrapper = _mount({ template: ``, diff --git a/packages/input/src/index.vue b/packages/input/src/index.vue index 35d6bb6ebe..22263c909f 100644 --- a/packages/input/src/index.vue +++ b/packages/input/src/index.vue @@ -70,7 +70,7 @@ - {{ textLength }}/{{ upperLimit }} + {{ textLength }}/{{ maxlength }} @@ -103,7 +103,7 @@ @keydown="handleKeydown" > - {{ textLength }}/{{ upperLimit }} + {{ textLength }}/{{ maxlength }} @@ -222,6 +222,9 @@ export default defineComponent({ type: Object, default: () => ({}), }, + maxlength: { + type: [Number, String], + }, }, emits: [UPDATE_MODEL_EVENT, 'input', 'change', 'focus', 'blur', 'clear', @@ -254,7 +257,6 @@ export default defineComponent({ })) const inputDisabled = computed(() => props.disabled || elForm.disabled) const nativeInputValue = computed(() => (props.modelValue === null || props.modelValue === undefined) ? '' : String(props.modelValue)) - const upperLimit = computed(() => ctx.attrs.maxlength) const showClear = computed(() => { return props.clearable && !inputDisabled.value && @@ -270,18 +272,18 @@ export default defineComponent({ }) const isWordLimitVisible = computed(() => { return props.showWordLimit && - ctx.attrs.maxlength && + props.maxlength && (props.type === 'text' || props.type === 'textarea') && !inputDisabled.value && !props.readonly && !props.showPassword }) const textLength = computed(() => { - return typeof props.modelValue === 'number' ? String(props.modelValue).length : (props.modelValue || '').length + return Array.from(nativeInputValue.value).length }) const inputExceed = computed(() => { // show exceed style if length of initial value greater then maxlength - return isWordLimitVisible.value && (textLength.value > upperLimit.value) + return isWordLimitVisible.value && (textLength.value > Number(props.maxlength)) }) const resizeTextarea = () => { @@ -332,7 +334,7 @@ export default defineComponent({ } const handleInput = event => { - const { value } = event.target + let { value } = event.target // should not emit input during composition // see: https://github.com/ElemeFE/element/issues/10516 @@ -340,7 +342,14 @@ export default defineComponent({ // hack for https://github.com/ElemeFE/element/issues/8548 // should remove the following line when we don't support IE - if (value === nativeInputValue.value) return + if (value === nativeInputValue.value ) return + + // if set maxlength + if (props.maxlength) { + const sliceIndex = inputExceed.value ? textLength.value : props.maxlength + // Convert value to an array for get a right lenght + value = Array.from(value).slice(0, Number(sliceIndex)).join('') + } ctx.emit(UPDATE_MODEL_EVENT, value) ctx.emit('input', value) @@ -481,7 +490,6 @@ export default defineComponent({ showClear, showPwdVisible, isWordLimitVisible, - upperLimit, textLength, hovering, inputExceed, diff --git a/website/docs/en-US/input.md b/website/docs/en-US/input.md index 0f35c55882..b9cb1af250 100644 --- a/website/docs/en-US/input.md +++ b/website/docs/en-US/input.md @@ -568,7 +568,7 @@ export default defineComponent({ ### Limit length -:::demo `maxlength` and `minlength` are attributes of native input, they declare a limit on the number of characters a user can input. The "number of characters" is measured using JavaScript string length.Setting the `maxlength` prop for a text or textarea type of Input can limit the length of input value, allows you to show word count by setting `show-word-limit` to `true` at the same time. +:::demo `maxlength` and `minlength` attributes of input, they declare a limit on the number of characters a user can input. The "number of characters" is measured using JavaScript string length.Setting the `maxlength` prop for a text or textarea type of Input can limit the length of input value, allows you to show word count by setting `show-word-limit` to `true` at the same time. ```html