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 deprecate bordered for variant #46337

Merged
merged 5 commits into from Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 21 additions & 0 deletions components/_util/hooks/useVariants.ts
@@ -0,0 +1,21 @@
type DefaultVariant = 'outlined' | 'borderless';

/**
* Compatible for legacy `bordered` prop, safe to remove after `bordered` is removed.
* @param variant
* @param legacyBordered
*/
const useVariant = <T extends DefaultVariant>(
variant: T | undefined,
legacyBordered: boolean | undefined,
): T | DefaultVariant => {
if (typeof variant !== 'undefined') {
return variant;
}
if (legacyBordered === false) {
return 'borderless';
}
return 'outlined';
};

export default useVariant;
25 changes: 22 additions & 3 deletions components/input/Input.tsx
@@ -1,7 +1,7 @@
import React, { forwardRef, useContext, useEffect, useRef } from 'react';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import classNames from 'classnames';
import type { InputRef, InputProps as RcInputProps } from 'rc-input';
import type { InputProps as RcInputProps, InputRef } from 'rc-input';
import RcInput from 'rc-input';
import type { BaseInputProps } from 'rc-input/lib/interface';
import { composeRef } from 'rc-util/lib/ref';
Expand All @@ -19,6 +19,7 @@ import useRemovePasswordTimeout from './hooks/useRemovePasswordTimeout';
import useStyle from './style';
import { hasPrefixSuffix } from './utils';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import useVariant from '../_util/hooks/useVariants';

export interface InputFocusOptions extends FocusOptions {
cursor?: 'start' | 'end' | 'all';
Expand Down Expand Up @@ -55,6 +56,9 @@ export function triggerFocus(
}
}

export const inputVariants = ['outlined', 'borderless'] as const;
export type InputVariant = (typeof inputVariants)[number];

export interface InputProps
extends Omit<
RcInputProps,
Expand All @@ -64,7 +68,12 @@ export interface InputProps
size?: SizeType;
disabled?: boolean;
status?: InputStatus;
/** @deprecated Use `variant="borderless"` instead. */
bordered?: boolean;
/**
* @default 'outlined'
*/
variant?: InputVariant;
[key: `data-${string}`]: string | undefined;
}

Expand All @@ -87,8 +96,15 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
rootClassName,
onChange,
classNames: classes,
variant: customVariant,
...rest
} = props;

if (process.env.NODE_ENV !== 'production') {
const { deprecated } = devUseWarning('Input');
deprecated(!('bordered' in props), 'bordered', 'variant');
}

const { getPrefixCls, direction, input } = React.useContext(ConfigContext);

const prefixCls = getPrefixCls('input', customizePrefixCls);
Expand Down Expand Up @@ -166,6 +182,9 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
mergedAllowClear = { clearIcon: <CloseCircleFilled /> };
}

const variant = useVariant(customVariant, bordered);
const enableVariantCls = inputVariants.includes(variant) && variant !== 'outlined';

return wrapCSSVar(
<RcInput
ref={composeRef(ref, inputRef)}
Expand Down Expand Up @@ -214,7 +233,7 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
[`${prefixCls}-sm`]: mergedSize === 'small',
[`${prefixCls}-lg`]: mergedSize === 'large',
[`${prefixCls}-rtl`]: direction === 'rtl',
[`${prefixCls}-borderless`]: !bordered,
[`${prefixCls}-${variant}`]: enableVariantCls,
},
!inputHasPrefixSuffix && getStatusClassNames(prefixCls, mergedStatus),
classes?.input,
Expand All @@ -228,7 +247,7 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
[`${prefixCls}-affix-wrapper-sm`]: mergedSize === 'small',
[`${prefixCls}-affix-wrapper-lg`]: mergedSize === 'large',
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
[`${prefixCls}-affix-wrapper-${variant}`]: enableVariantCls,
},
getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus, hasFeedback),
hashId,
Expand Down
23 changes: 19 additions & 4 deletions components/input/TextArea.tsx
Expand Up @@ -14,16 +14,21 @@ import DisabledContext from '../config-provider/DisabledContext';
import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext';
import { FormItemInputContext } from '../form/context';
import type { InputFocusOptions } from './Input';
import { triggerFocus } from './Input';
import type { InputFocusOptions, InputVariant } from './Input';
import { inputVariants, triggerFocus } from './Input';
import useStyle from './style';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import useVariant from '../_util/hooks/useVariants';
import { devUseWarning } from '../_util/warning';

export interface TextAreaProps extends Omit<RcTextAreaProps, 'suffix'> {
/** @deprecated Use `variant` instead */
bordered?: boolean;
size?: SizeType;
status?: InputStatus;
rootClassName?: string;
/** @default `outlined` */
variant?: InputVariant;
}

export interface TextAreaRef {
Expand All @@ -43,8 +48,15 @@ const TextArea = forwardRef<TextAreaRef, TextAreaProps>((props, ref) => {
classNames: classes,
rootClassName,
className,
variant: customVariant,
...rest
} = props;

if (process.env.NODE_ENV !== 'production') {
const { deprecated } = devUseWarning('TextArea');
deprecated(!('bordered' in props), 'bordered', 'variant');
}

const { getPrefixCls, direction } = React.useContext(ConfigContext);

// ===================== Size =====================
Expand Down Expand Up @@ -87,6 +99,9 @@ const TextArea = forwardRef<TextAreaRef, TextAreaProps>((props, ref) => {
const cssVarCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId] = useStyle(prefixCls, cssVarCls);

const variant = useVariant(customVariant, bordered);
const enableVariantCls = variant !== 'outlined' && inputVariants.includes(variant);

return wrapCSSVar(
<RcTextArea
{...rest}
Expand All @@ -98,7 +113,7 @@ const TextArea = forwardRef<TextAreaRef, TextAreaProps>((props, ref) => {
`${prefixCls}-textarea-affix-wrapper`,
{
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
[`${prefixCls}-affix-wrapper-${variant}`]: enableVariantCls,
[`${prefixCls}-affix-wrapper-sm`]: mergedSize === 'small',
[`${prefixCls}-affix-wrapper-lg`]: mergedSize === 'large',
[`${prefixCls}-textarea-show-count`]: props.showCount || props.count?.show,
Expand All @@ -111,7 +126,7 @@ const TextArea = forwardRef<TextAreaRef, TextAreaProps>((props, ref) => {
...classes,
textarea: classNames(
{
[`${prefixCls}-borderless`]: !bordered,
[`${prefixCls}-${variant}`]: enableVariantCls,
[`${prefixCls}-sm`]: mergedSize === 'small',
[`${prefixCls}-lg`]: mergedSize === 'large',
},
Expand Down
22 changes: 11 additions & 11 deletions components/input/__tests__/__snapshots__/demo-extend.test.ts.snap
Expand Up @@ -5296,17 +5296,6 @@ exports[`renders components/input/demo/basic.tsx extend context correctly 1`] =

exports[`renders components/input/demo/basic.tsx extend context correctly 2`] = `[]`;

exports[`renders components/input/demo/borderless.tsx extend context correctly 1`] = `
<input
class="ant-input ant-input-borderless"
placeholder="Borderless"
type="text"
value=""
/>
`;

exports[`renders components/input/demo/borderless.tsx extend context correctly 2`] = `[]`;

exports[`renders components/input/demo/borderless-debug.tsx extend context correctly 1`] = `
<div
style="background-color: rgba(0, 0, 128, 0.2);"
Expand Down Expand Up @@ -10813,3 +10802,14 @@ Array [
`;

exports[`renders components/input/demo/tooltip.tsx extend context correctly 2`] = `[]`;

exports[`renders components/input/demo/variant.tsx extend context correctly 1`] = `
<input
class="ant-input ant-input-borderless"
placeholder="Borderless"
type="text"
value=""
/>
`;

exports[`renders components/input/demo/variant.tsx extend context correctly 2`] = `[]`;
18 changes: 9 additions & 9 deletions components/input/__tests__/__snapshots__/demo.test.tsx.snap
Expand Up @@ -1374,15 +1374,6 @@ exports[`renders components/input/demo/basic.tsx correctly 1`] = `
/>
`;

exports[`renders components/input/demo/borderless.tsx correctly 1`] = `
<input
class="ant-input ant-input-borderless"
placeholder="Borderless"
type="text"
value=""
/>
`;

exports[`renders components/input/demo/borderless-debug.tsx correctly 1`] = `
<div
style="background-color:rgba(0, 0, 128, .2)"
Expand Down Expand Up @@ -4329,3 +4320,12 @@ exports[`renders components/input/demo/tooltip.tsx correctly 1`] = `
value=""
/>
`;

exports[`renders components/input/demo/variant.tsx correctly 1`] = `
<input
class="ant-input ant-input-borderless"
placeholder="Borderless"
type="text"
value=""
/>
`;
8 changes: 8 additions & 0 deletions components/input/__tests__/index.test.tsx
Expand Up @@ -527,6 +527,14 @@ describe('Input allowClear', () => {
fireEvent.focus(container.querySelector('input')!);
expect(container.querySelector('input')).not.toHaveStyle('background-color: transparent');
});

it('legacy bordered should work', () => {
const errSpy = jest.spyOn(console, 'error');
const { container } = render(<Input bordered={false} />);
expect(container.querySelector('input')).toHaveClass('ant-input-borderless');
expect(errSpy).toHaveBeenCalledWith(expect.stringContaining('`bordered` is deprecated'));
errSpy.mockRestore();
});
});

describe('typescript types', () => {
Expand Down
8 changes: 8 additions & 0 deletions components/input/__tests__/textarea.test.tsx
Expand Up @@ -516,4 +516,12 @@ describe('TextArea allowClear', () => {
);
expect(container).toMatchSnapshot();
});

it('legacy bordered should work', () => {
const errSpy = jest.spyOn(console, 'error');
const { container } = render(<TextArea bordered={false} />);
expect(container.querySelector('textarea')).toHaveClass('ant-input-borderless');
expect(errSpy).toHaveBeenCalledWith(expect.stringContaining('`bordered` is deprecated'));
errSpy.mockRestore();
});
});
14 changes: 7 additions & 7 deletions components/input/demo/borderless-debug.tsx
Expand Up @@ -5,13 +5,13 @@ const { TextArea } = Input;

const App: React.FC = () => (
<div style={{ backgroundColor: 'rgba(0, 0, 128, .2)' }}>
<Input placeholder="Unbordered" bordered={false} />
<Input placeholder="Unbordered" bordered={false} size="large" />
<TextArea placeholder="Unbordered" bordered={false} />
<TextArea placeholder="Unbordered" bordered={false} allowClear />
<Input placeholder="Unbordered" bordered={false} allowClear />
<Input prefix="¥" suffix="RMB" bordered={false} />
<Input prefix="¥" suffix="RMB" disabled bordered={false} />
<Input placeholder="Unbordered" variant="borderless" />
<Input placeholder="Unbordered" variant="borderless" size="large" />
<TextArea placeholder="Unbordered" variant="borderless" />
<TextArea placeholder="Unbordered" variant="borderless" allowClear />
<Input placeholder="Unbordered" variant="borderless" allowClear />
<Input prefix="¥" suffix="RMB" variant="borderless" />
<Input prefix="¥" suffix="RMB" disabled variant="borderless" />
<TextArea allowClear style={{ border: '2px solid #000' }} />
</div>
);
Expand Down
7 changes: 0 additions & 7 deletions components/input/demo/borderless.md

This file was deleted.

6 changes: 0 additions & 6 deletions components/input/demo/borderless.tsx

This file was deleted.

7 changes: 7 additions & 0 deletions components/input/demo/variant.md
@@ -0,0 +1,7 @@
## zh-CN

Input 形态变体。

## en-US

Variants of Input.
6 changes: 6 additions & 0 deletions components/input/demo/variant.tsx
@@ -0,0 +1,6 @@
import React from 'react';
import { Input } from 'antd';

const App: React.FC = () => <Input placeholder="Borderless" variant="borderless" />;

export default App;
4 changes: 2 additions & 2 deletions components/input/index.en-US.md
Expand Up @@ -20,6 +20,7 @@ A basic widget for getting the user input is a text field. Keyboard and mouse ca
<!-- prettier-ignore -->
<code src="./demo/basic.tsx">Basic usage</code>
<code src="./demo/size.tsx">Three sizes of Input</code>
<code src="./demo/variant.tsx">Variants</code>
<code src="./demo/addon.tsx">Pre / Post tab</code>
<code src="./demo/compact-style.tsx">Compact Style</code>
<code src="./demo/group.tsx" debug>Input Group</code>
Expand All @@ -34,7 +35,6 @@ A basic widget for getting the user input is a text field. Keyboard and mouse ca
<code src="./demo/show-count.tsx">With character counting</code>
<code src="./demo/advance-count.tsx" version=">= 5.10.0">Custom count logic</code>
<code src="./demo/status.tsx">Status</code>
<code src="./demo/borderless.tsx">Borderless</code>
<code src="./demo/focus.tsx">Focus</code>
<code src="./demo/borderless-debug.tsx" debug>Style Debug</code>
<code src="./demo/align.tsx" debug>Text Align</code>
Expand All @@ -53,7 +53,6 @@ Common props ref:[Common props](/docs/react/common-props)
| addonAfter | The label text displayed after (on the right side of) the input field | ReactNode | - | |
| addonBefore | The label text displayed before (on the left side of) the input field | ReactNode | - | |
| allowClear | If allow to remove input content with clear icon | boolean \| { clearIcon: ReactNode } | false | |
| bordered | Whether has border style | boolean | true | 4.5.0 |
| classNames | Semantic DOM class | Record<[SemanticDOM](#input-1), string> | - | 5.4.0 |
| count | Character count config | [CountConfig](#countconfig) | - | 5.10.0 |
| defaultValue | The initial input content | string | - | |
Expand All @@ -68,6 +67,7 @@ Common props ref:[Common props](/docs/react/common-props)
| suffix | The suffix icon for the Input | ReactNode | - | |
| type | The type of input, see: [MDN](https://developer.mozilla.org/docs/Web/HTML/Element/input#Form_%3Cinput%3E_types)( use `Input.TextArea` instead of `type="textarea"`) | string | `text` | |
| value | The input content value | string | - | |
| variant | Variants of Input | `outlined` \| `borderless` | `outlined` | 5.13.0 |
| onChange | Callback when user input | function(e) | - | |
| onPressEnter | The callback function that is triggered when Enter key is pressed | function(e) | - | |

Expand Down
4 changes: 2 additions & 2 deletions components/input/index.zh-CN.md
Expand Up @@ -21,6 +21,7 @@ demo:
<!-- prettier-ignore -->
<code src="./demo/basic.tsx">基本使用</code>
<code src="./demo/size.tsx">三种大小</code>
<code src="./demo/variant.tsx">变体</code>
<code src="./demo/addon.tsx">前置/后置标签</code>
<code src="./demo/compact-style.tsx">紧凑模式</code>
<code src="./demo/group.tsx" debug>输入框组合</code>
Expand All @@ -35,7 +36,6 @@ demo:
<code src="./demo/show-count.tsx">带字数提示</code>
<code src="./demo/advance-count.tsx" version=">= 5.10.0">定制计数能力</code>
<code src="./demo/status.tsx">自定义状态</code>
<code src="./demo/borderless.tsx">无边框</code>
<code src="./demo/focus.tsx">聚焦</code>
<code src="./demo/borderless-debug.tsx" debug>Style Debug</code>
<code src="./demo/align.tsx" debug>文本对齐</code>
Expand All @@ -54,7 +54,6 @@ demo:
| addonAfter | 带标签的 input,设置后置标签 | ReactNode | - | |
| addonBefore | 带标签的 input,设置前置标签 | ReactNode | - | |
| allowClear | 可以点击清除图标删除内容 | boolean \| { clearIcon: ReactNode } | - | |
| bordered | 是否有边框 | boolean | true | 4.5.0 |
| classNames | 语义化结构 class | Record<[SemanticDOM](#input-1), string> | - | 5.4.0 |
| count | 字符计数配置 | [CountConfig](#countconfig) | - | 5.10.0 |
| defaultValue | 输入框默认内容 | string | - | |
Expand All @@ -69,6 +68,7 @@ demo:
| suffix | 带有后缀图标的 input | ReactNode | - | |
| type | 声明 input 类型,同原生 input 标签的 type 属性,见:[MDN](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/input#属性)(请直接使用 `Input.TextArea` 代替 `type="textarea"`) | string | `text` | |
| value | 输入框内容 | string | - | |
| variant | 形态变体 | `outlined` \| `borderless` | `outlined` | 5.13.0 |
| onChange | 输入框内容变化时的回调 | function(e) | - | |
| onPressEnter | 按下回车的回调 | function(e) | - | |

Expand Down