Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(input): support clearTrigger #1481

Merged
merged 11 commits into from
Jul 11, 2024
Merged
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
Loading