From 2fb52d6e26dcd79a33692901a25026bc19c5fe79 Mon Sep 17 00:00:00 2001 From: John60676 Date: Mon, 26 Oct 2020 11:28:56 +0800 Subject: [PATCH] feat: textarea support word count (#3009) --- components/input/ClearableLabeledInput.jsx | 4 +- components/input/TextArea.jsx | 39 +++++++++++++++++-- .../__snapshots__/index.test.js.snap | 2 + components/input/__tests__/index.test.js | 13 ++++++- components/input/style/index.less | 9 +++++ types/input/textarea.d.ts | 1 + 6 files changed, 61 insertions(+), 7 deletions(-) diff --git a/components/input/ClearableLabeledInput.jsx b/components/input/ClearableLabeledInput.jsx index dfac0c595..0ede750d1 100644 --- a/components/input/ClearableLabeledInput.jsx +++ b/components/input/ClearableLabeledInput.jsx @@ -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 ( - + {prefix} {cloneElement(element, { style: null, diff --git a/components/input/TextArea.jsx b/components/input/TextArea.jsx index 870e0ff8a..6572161b9 100644 --- a/components/input/TextArea.jsx +++ b/components/input/TextArea.jsx @@ -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 ; + + let textareaNode = ( + + ); + + if (showCount) { + const valueLength = [...value].length; + const dataCount = `${valueLength}${hasMaxlength ? ` / ${maxlength}` : ''}`; + textareaNode = ( +
+ {textareaNode} +
+ ); + } + return textareaNode; }, }; diff --git a/components/input/__tests__/__snapshots__/index.test.js.snap b/components/input/__tests__/__snapshots__/index.test.js.snap index 9a9aba45c..9da98deac 100644 --- a/components/input/__tests__/__snapshots__/index.test.js.snap +++ b/components/input/__tests__/__snapshots__/index.test.js.snap @@ -7,3 +7,5 @@ exports[`Input.Search should support suffix 1`] = ``; exports[`TextArea should support maxlength 1`] = ``; + +exports[`TextArea should support showCount 1`] = `
`; diff --git a/components/input/__tests__/index.test.js b/components/input/__tests__/index.test.js index d70d261cf..684411905 100644 --- a/components/input/__tests__/index.test.js +++ b/components/input/__tests__/index.test.js @@ -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', () => { diff --git a/components/input/style/index.less b/components/input/style/index.less index 8acafd416..c41058460 100644 --- a/components/input/style/index.less +++ b/components/input/style/index.less @@ -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'; diff --git a/types/input/textarea.d.ts b/types/input/textarea.d.ts index a30389a27..0ea21fa15 100644 --- a/types/input/textarea.d.ts +++ b/types/input/textarea.d.ts @@ -31,5 +31,6 @@ export declare class TextArea extends AntdComponent { * @type boolean */ allowClear?: boolean; + showCount?: boolean; }; }