feat: textarea support word count (#3009)
parent
9ac18c5e52
commit
2fb52d6e26
|
@ -34,7 +34,6 @@ const ClearableLabeledInput = {
|
|||
addonAfter: PropTypes.any,
|
||||
readonly: PropTypes.bool,
|
||||
isFocused: PropTypes.bool,
|
||||
style: PropTypes.object,
|
||||
},
|
||||
methods: {
|
||||
renderClearIcon(prefixCls) {
|
||||
|
@ -74,6 +73,7 @@ const ClearableLabeledInput = {
|
|||
|
||||
renderLabeledIcon(prefixCls, element) {
|
||||
const props = this.$props;
|
||||
const { style } = this.$attrs;
|
||||
const suffix = this.renderSuffix(prefixCls);
|
||||
if (!hasPrefixSuffix(this)) {
|
||||
return cloneElement(element, {
|
||||
|
@ -94,7 +94,7 @@ const ClearableLabeledInput = {
|
|||
props.suffix && props.allowClear && this.$props.value,
|
||||
});
|
||||
return (
|
||||
<span class={affixWrapperCls} style={props.style}>
|
||||
<span class={affixWrapperCls} style={style}>
|
||||
{prefix}
|
||||
{cloneElement(element, {
|
||||
style: null,
|
||||
|
|
|
@ -6,11 +6,13 @@ import { hasProp, getOptionProps } from '../_util/props-util';
|
|||
import { ConfigConsumerProps } from '../config-provider';
|
||||
import { fixControlledValue, resolveOnChange } from './Input';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import classNames from '../_util/classNames';
|
||||
|
||||
const TextAreaProps = {
|
||||
...inputProps,
|
||||
autosize: PropTypes.oneOfType([Object, Boolean]),
|
||||
autoSize: PropTypes.oneOfType([Object, Boolean]),
|
||||
showCount: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default {
|
||||
|
@ -100,9 +102,13 @@ export default {
|
|||
|
||||
renderTextArea(prefixCls) {
|
||||
const props = getOptionProps(this);
|
||||
const { style, class: customClass } = this.$attrs;
|
||||
const resizeProps = {
|
||||
...props,
|
||||
...this.$attrs,
|
||||
style: style && !props.showCount,
|
||||
class: customClass && !props.showCount,
|
||||
showCount: null,
|
||||
prefixCls,
|
||||
onInput: this.handleChange,
|
||||
onChange: this.handleChange,
|
||||
|
@ -112,19 +118,44 @@ export default {
|
|||
},
|
||||
},
|
||||
render() {
|
||||
const { stateValue, prefixCls: customizePrefixCls } = this;
|
||||
const { stateValue, prefixCls: customizePrefixCls, maxlength, showCount } = this;
|
||||
const { style, class: customClass } = this.$attrs;
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('input', customizePrefixCls);
|
||||
|
||||
let value = fixControlledValue(stateValue);
|
||||
// Max length value
|
||||
const hasMaxlength = Number(maxlength) > 0;
|
||||
value = hasMaxlength ? value.slice(0, maxlength) : value;
|
||||
const props = {
|
||||
...getOptionProps(this),
|
||||
...this.$attrs,
|
||||
prefixCls,
|
||||
inputType: 'text',
|
||||
value: fixControlledValue(stateValue),
|
||||
element: this.renderTextArea(prefixCls),
|
||||
handleReset: this.handleReset,
|
||||
};
|
||||
return <ClearableLabeledInput {...props} ref={this.saveClearableInput} />;
|
||||
|
||||
let textareaNode = (
|
||||
<ClearableLabeledInput {...props} value={value} ref={this.saveClearableInput} />
|
||||
);
|
||||
|
||||
if (showCount) {
|
||||
const valueLength = [...value].length;
|
||||
const dataCount = `${valueLength}${hasMaxlength ? ` / ${maxlength}` : ''}`;
|
||||
textareaNode = (
|
||||
<div
|
||||
className={classNames(
|
||||
`${prefixCls}-textarea`,
|
||||
`${prefixCls}-textarea-show-count`,
|
||||
customClass,
|
||||
)}
|
||||
style={style}
|
||||
data-count={dataCount}
|
||||
>
|
||||
{textareaNode}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return textareaNode;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -7,3 +7,5 @@ exports[`Input.Search should support suffix 1`] = `<span class="ant-input-search
|
|||
exports[`TextArea should support disabled 1`] = `<textarea disabled="" class="ant-input ant-input-disabled"></textarea>`;
|
||||
|
||||
exports[`TextArea should support maxlength 1`] = `<textarea maxlength="10" class="ant-input"></textarea>`;
|
||||
|
||||
exports[`TextArea should support showCount 1`] = `<div class="ant-input-textarea ant-input-textarea-show-count" data-count="3 / 10"><textarea maxlength="10" class="ant-input"></textarea></div>`;
|
||||
|
|
|
@ -29,7 +29,7 @@ describe('Input', () => {
|
|||
props: { allowClear: true, defaultValue: '111', disabled: true },
|
||||
sync: false,
|
||||
});
|
||||
expect(wrapper.findAll('.ant-input-clear-icon').length).toBe(0);
|
||||
expect(wrapper.findAll('.ant-input-clear-icon-hidden').length).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -68,6 +68,17 @@ describe('TextArea', () => {
|
|||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
it('should support showCount', async () => {
|
||||
const wrapper = mount(TextArea, {
|
||||
props: { showCount: true, defaultValue: '111', maxlength: 10 },
|
||||
sync: false,
|
||||
});
|
||||
expect(wrapper.find('.ant-input-textarea-show-count')).toBeTruthy();
|
||||
await asyncExpect(() => {
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// describe('As Form Control', () => {
|
||||
|
|
|
@ -57,4 +57,13 @@
|
|||
margin: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
.@{ant-prefix}-input-textarea {
|
||||
&-show-count::after {
|
||||
display: block;
|
||||
color: @text-color-secondary;
|
||||
text-align: right;
|
||||
content: attr(data-count);
|
||||
}
|
||||
}
|
||||
|
||||
@import './search-input';
|
||||
|
|
|
@ -31,5 +31,6 @@ export declare class TextArea extends AntdComponent {
|
|||
* @type boolean
|
||||
*/
|
||||
allowClear?: boolean;
|
||||
showCount?: boolean;
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue