Skip to content

Commit

Permalink
feat(input): support clearTrigger (#1481)
Browse files Browse the repository at this point in the history
* feat(input): support clearTrigger

* fix: clear

* fix: test clearable

* chore: remove focused

* fix: showClear

* fix: focused test not work

* test: add test case

* feat: adjust autofocus

* fix: show passwd

* feat: clear event mousedown change to touchend
  • Loading branch information
liweijie0812 authored Jul 11, 2024
1 parent aadfa21 commit 19a2c68
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 62 deletions.
159 changes: 137 additions & 22 deletions src/input/__test__/index.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ describe('Input.vue', async () => {
const wrapper = mount(<Input label="标题" v-model={value.value} clearable onClear={handleClear} />);
const closeIcon = wrapper.findComponent(CloseCircleFilledIcon);
expect(closeIcon.exists()).toBeTruthy();
await closeIcon.trigger('click');
await closeIcon.trigger('touchend');
expect(value.value).toBe('');
expect(handleClear).toBeCalled();
});
Expand Down Expand Up @@ -128,6 +128,7 @@ describe('Input.vue', async () => {

it(': type=password', async () => {
const wrapper = mount(<Input label="标题" type="password" />);
expect(wrapper.find('.t-icon-browse-off').exists()).toBeTruthy();
wrapper.find('.t-icon-browse-off').trigger('click');
await wrapper.vm.$nextTick();
expect(wrapper.find('.t-icon-browse').exists()).toBeTruthy();
Expand All @@ -140,32 +141,119 @@ describe('Input.vue', async () => {
expect(attrDom1.attributes('type')).toBe('password');
});

it(': onBlur', async () => {
const onBlur = vi.fn();
const wrapper = mount(<Input label="标题" onBlur={onBlur} />);
await nextTick();
const input = wrapper.find('.t-input__wrap input');
await input.trigger('blur');
expect(onBlur).toBeCalled();
it(': type=password and disabled', async () => {
const wrapper = mount(<Input label="标题" type="password" disabled/>);
expect(wrapper.find('.t-icon-browse-off').exists()).toBeTruthy();
wrapper.find('.t-icon-browse-off').trigger('click');
await wrapper.vm.$nextTick();
expect(wrapper.find('.t-icon-browse-off').exists()).toBeTruthy();
await wrapper.setProps({
disabled: false,
});
wrapper.find('.t-icon-browse-off').trigger('click');
await wrapper.vm.$nextTick();
const attrDom = wrapper.find('input');
expect(attrDom.attributes('type')).toBe('text');
wrapper.find('.t-icon-browse').trigger('click');
await wrapper.vm.$nextTick();
expect(wrapper.find('.t-icon-browse-off').exists()).toBeTruthy();
const attrDom1 = wrapper.find('input');
expect(attrDom1.attributes('type')).toBe('password');
});

it(': onFocus', async () => {
const onFocus = vi.fn();
const wrapper = mount(<Input label="标题" onFocus={onFocus} />);
const input = wrapper.find('.t-input__wrap input');
await input.trigger('focus');
expect(onFocus).toBeCalled();
it(': autofocus', async () => {
const value = ref('123');
const wrapper = mount(<Input label="标题" v-model={value.value} clearable clearTrigger="focus" autofocus/>);
await wrapper.vm.$nextTick();
expect(wrapper.find('.t-icon-close-circle-filled').exists()).toBeTruthy();
wrapper.vm.blur();
await wrapper.vm.$nextTick();
expect(wrapper.find('.t-icon-close-circle-filled').exists()).toBeFalsy();
wrapper.vm.focus();
await wrapper.vm.$nextTick();
expect(wrapper.find('.t-icon-close-circle-filled').exists()).toBeTruthy();

});

it(': onChange', async () => {
const value = ref('');
const onChange = vi.fn();
const wrapper = mount(<Input label="标题" v-model={value.value} onChange={onChange} />);
const el = wrapper.find('.t-input__wrap input').element;
await simulateEvent(el, '文本', 'input');
expect(onChange).toBeCalledTimes(1);
expect(onChange).toHaveBeenCalledWith('文本');
it(': clearTrigger=always', async () => {
const value = ref('123');
const handleClear = vi.fn();
const wrapper = mount(<Input label="标题" v-model={value.value} clearable clearTrigger="always" onClear={handleClear} />);
expect(wrapper.find('.t-icon-close-circle-filled').exists()).toBeTruthy();
await wrapper.find('.t-icon-close-circle-filled').trigger('touchend');
expect(value.value).toBe('');
expect(handleClear).toBeCalled();
});

it(': clearTrigger=always and disabled', async () => {
const value = ref('123');
const handleClear = vi.fn();
const wrapper = mount(<Input label="标题" v-model={value.value} disabled clearable clearTrigger="always" onClear={handleClear} />);
expect(wrapper.find('.t-icon-close-circle-filled').exists()).toBeFalsy();
await wrapper.setProps({
disabled: false,
});
expect(wrapper.find('.t-icon-close-circle-filled').exists()).toBeTruthy();
});

it(': clearTrigger=always and readonly', async () => {
const value = ref('123');
const handleClear = vi.fn();
const wrapper = mount(<Input label="标题" v-model={value.value} readonly clearable clearTrigger="always" onClear={handleClear} />);
expect(wrapper.find('.t-icon-close-circle-filled').exists()).toBeFalsy();
await wrapper.setProps({
readonly: false,
});
expect(wrapper.find('.t-icon-close-circle-filled').exists()).toBeTruthy();
});

it(': clearTrigger=focus', async () => {
const value = ref('123');
const handleClear = vi.fn();
const wrapper = mount(<Input label="标题" v-model={value.value} clearable clearTrigger="focus" onClear={handleClear} />);
expect(wrapper.find('.t-icon-close-circle-filled').exists()).toBeFalsy();
wrapper.vm.focus();
await wrapper.vm.$nextTick();
expect(wrapper.find('.t-icon-close-circle-filled').exists()).toBeTruthy();

await wrapper.find('.t-icon-close-circle-filled').trigger('touchend');
expect(value.value).toBe('');
expect(handleClear).toBeCalled();
});

it(': clearTrigger=focus and disabled', async () => {
const value = ref('123');
const handleClear = vi.fn();
const wrapper = mount(<Input label="标题" v-model={value.value} disabled clearable clearTrigger="focus" onClear={handleClear} />);
expect(wrapper.find('.t-icon-close-circle-filled').exists()).toBeFalsy();
wrapper.vm.focus();
await wrapper.vm.$nextTick();
expect(wrapper.find('.t-icon-close-circle-filled').exists()).toBeFalsy();
await wrapper.setProps({
disabled: false,
});
wrapper.vm.focus();
await wrapper.vm.$nextTick();
expect(wrapper.find('.t-icon-close-circle-filled').exists()).toBeTruthy();
});

it(': clearTrigger=focus and readonly', async () => {
const value = ref('123');
const handleClear = vi.fn();
const wrapper = mount(<Input label="标题" v-model={value.value} readonly clearable clearTrigger="focus" onClear={handleClear} />);
expect(wrapper.find('.t-icon-close-circle-filled').exists()).toBeFalsy();
wrapper.vm.focus();
await wrapper.vm.$nextTick();
expect(wrapper.find('.t-icon-close-circle-filled').exists()).toBeFalsy();
await wrapper.setProps({
readonly: false,
});
wrapper.vm.focus();
await wrapper.vm.$nextTick();
expect(wrapper.find('.t-icon-close-circle-filled').exists()).toBeTruthy();
});


});
describe('event', async () => {
it(': focus && blur', async () => {
Expand Down Expand Up @@ -200,6 +288,33 @@ describe('Input.vue', async () => {
await $input.trigger('compositionend');
expect(onCompositionend).toBeCalled();
});

it(': onBlur', async () => {
const onBlur = vi.fn();
const wrapper = mount(<Input label="标题" onBlur={onBlur} />);
await nextTick();
const input = wrapper.find('.t-input__wrap input');
await input.trigger('blur');
expect(onBlur).toBeCalled();
});

it(': onFocus', async () => {
const onFocus = vi.fn();
const wrapper = mount(<Input label="标题" onFocus={onFocus} />);
const input = wrapper.find('.t-input__wrap input');
await input.trigger('focus');
expect(onFocus).toBeCalled();
});

it(': onChange', async () => {
const value = ref('');
const onChange = vi.fn();
const wrapper = mount(<Input label="标题" v-model={value.value} onChange={onChange} />);
const el = wrapper.find('.t-input__wrap input').element;
await simulateEvent(el, '文本', 'input');
expect(onChange).toBeCalledTimes(1);
expect(onChange).toHaveBeenCalledWith('文本');
});
});

describe('slots', async () => {
Expand Down
7 changes: 4 additions & 3 deletions src/input/input.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ allowInputOverMax | Boolean | false | allow to continue input on value length is
autocomplete | String | undefined | attribute of input element, [see here](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) | N
autofocus | Boolean | false | autofocus on first rendered | N
borderless | Boolean | false | input without border | N
clearTrigger | String | always | show clear icon, clicked to clear input value。options: always / focus | N
clearable | Boolean | false | show clear icon, clicked to clear input value | N
disabled | Boolean | undefined | make input to be disabled | N
format | Function | - | input value formatter, `type=number` does not work. if you need to format number, `InputNumber` Component might be better。Typescript:`InputFormatType` `type InputFormatType = (value: InputValue) => string`[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/input/type.ts) | N
Expand All @@ -22,7 +23,7 @@ maxlength | String / Number | - | \- | N
name | String | - | \- | N
placeholder | String | undefined | \- | N
prefixIcon | Slot / Function | - | Typescript:`TNode`[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
readonly | Boolean | false | \- | N
readonly | Boolean | undefined | \- | N
size | String | small | `deprecated`。options: small/medium。Typescript:`'medium' \| 'small'` | N
spellCheck | Boolean | false | attribute of input element, [see here](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/spellcheck) | N
status | String | undefined | options: default/success/warning/error | N
Expand All @@ -34,7 +35,7 @@ value | String / Number | - | input value。`v-model` and `v-model:value` is sup
defaultValue | String / Number | - | input value。uncontrolled property。Typescript:`InputValue` `type InputValue = string \| number`[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/input/type.ts) | N
onBlur | Function | | Typescript:`(value: InputValue, context: { e: FocusEvent }) => void`<br/> | N
onChange | Function | | Typescript:`(value: InputValue, context?: { e?: InputEvent \| MouseEvent \| CompositionEvent; trigger: 'input' \| 'initial' \| 'clear' }) => void`<br/>trigger on input value changed | N
onClear | Function | | Typescript:`(context: { e: MouseEvent }) => void`<br/> | N
onClear | Function | | Typescript:`(context: { e: TouchEvent }) => void`<br/> | N
onFocus | Function | | Typescript:`(value: InputValue, context: { e: FocusEvent }) => void`<br/> | N
onValidate | Function | | Typescript:`(context: { error?: 'exceed-maximum' \| 'below-minimum' }) => void`<br/>trigger on text length being over max length or max character | N

Expand All @@ -44,7 +45,7 @@ name | params | description
-- | -- | --
blur | `(value: InputValue, context: { e: FocusEvent })` | \-
change | `(value: InputValue, context?: { e?: InputEvent \| MouseEvent \| CompositionEvent; trigger: 'input' \| 'initial' \| 'clear' })` | trigger on input value changed
clear | `(context: { e: MouseEvent })` | \-
clear | `(context: { e: TouchEvent })` | \-
focus | `(value: InputValue, context: { e: FocusEvent })` | \-
validate | `(context: { error?: 'exceed-maximum' \| 'below-minimum' })` | trigger on text length being over max length or max character

Expand Down
7 changes: 4 additions & 3 deletions src/input/input.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ allowInputOverMax | Boolean | false | 超出 `maxlength` 或 `maxcharacter` 之
autocomplete | String | undefined | 是否开启自动填充功能,HTML5 原生属性,[点击查看详情](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) | N
autofocus | Boolean | false | 自动聚焦 | N
borderless | Boolean | false | 是否开启无边框模式 | N
clearTrigger | String | always | 清空图标触发方式,仅在输入框有值时有效。可选项:always / focus | N
clearable | Boolean | false | 是否可清空 | N
disabled | Boolean | undefined | 是否禁用输入框 | N
format | Function | - | 【开发中】指定输入框展示值的格式。TS 类型:`InputFormatType` `type InputFormatType = (value: InputValue) => string`[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/input/type.ts) | N
Expand All @@ -21,7 +22,7 @@ maxlength | String / Number | - | 用户最多可以输入的文本长度,一
name | String | - | 名称 | N
placeholder | String | undefined | 占位符 | N
prefixIcon | Slot / Function | - | 组件前置图标。TS 类型:`TNode`[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
readonly | Boolean | false | 只读状态 | N
readonly | Boolean | undefined | 只读状态 | N
size | String | small | 已废弃。输入框尺寸。可选项:small/medium。TS 类型:`'medium' \| 'small'` | N
spellCheck | Boolean | false | 是否开启拼写检查,HTML5 原生属性,[点击查看详情](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/spellcheck) | N
status | String | undefined | 输入框状态。默认情况会由组件内部根据实际情况呈现,如果文本过长引起的状态变化。可选项:default/success/warning/error | N
Expand All @@ -33,7 +34,7 @@ value | String / Number | - | 输入框的值。支持语法糖 `v-model` 或 `v
defaultValue | String / Number | - | 输入框的值。非受控属性。TS 类型:`InputValue` `type InputValue = string \| number`[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/input/type.ts) | N
onBlur | Function | | TS 类型:`(value: InputValue, context: { e: FocusEvent }) => void`<br/>失去焦点时触发 | N
onChange | Function | | TS 类型:`(value: InputValue, context?: { e?: InputEvent \| MouseEvent \| CompositionEvent; trigger: 'input' \| 'initial' \| 'clear' }) => void`<br/>输入框值发生变化时触发。`trigger=initial` 表示传入的数据不符合预期,组件自动处理后触发 change 告知父组件。如:初始值长度超过 `maxlength` 限制 | N
onClear | Function | | TS 类型:`(context: { e: MouseEvent }) => void`<br/>清空按钮点击时触发 | N
onClear | Function | | TS 类型:`(context: { e: TouchEvent }) => void`<br/>清空按钮点击时触发 | N
onFocus | Function | | TS 类型:`(value: InputValue, context: { e: FocusEvent }) => void`<br/>获得焦点时触发 | N
onValidate | Function | | TS 类型:`(context: { error?: 'exceed-maximum' \| 'below-minimum' }) => void`<br/>【暂不支持】字数超出限制时触发 | N

Expand All @@ -43,7 +44,7 @@ onValidate | Function | | TS 类型:`(context: { error?: 'exceed-maximum' \|
-- | -- | --
blur | `(value: InputValue, context: { e: FocusEvent })` | 失去焦点时触发
change | `(value: InputValue, context?: { e?: InputEvent \| MouseEvent \| CompositionEvent; trigger: 'input' \| 'initial' \| 'clear' })` | 输入框值发生变化时触发。`trigger=initial` 表示传入的数据不符合预期,组件自动处理后触发 change 告知父组件。如:初始值长度超过 `maxlength` 限制
clear | `(context: { e: MouseEvent })` | 清空按钮点击时触发
clear | `(context: { e: TouchEvent })` | 清空按钮点击时触发
focus | `(value: InputValue, context: { e: FocusEvent })` | 获得焦点时触发
validate | `(context: { error?: 'exceed-maximum' \| 'below-minimum' })` | 【暂不支持】字数超出限制时触发

Expand Down
Loading

0 comments on commit 19a2c68

Please sign in to comment.