mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-11-30 02:59:04 +08:00
chore: merge feature into master #38313
This commit is contained in:
commit
5ede9b65af
@ -14,10 +14,15 @@ import Input from './Input';
|
||||
const defaultIconRender = (visible: boolean) =>
|
||||
visible ? <EyeOutlined /> : <EyeInvisibleOutlined />;
|
||||
|
||||
type VisibilityToggle = {
|
||||
visible?: boolean;
|
||||
onVisibleChange?: (visible: boolean) => void;
|
||||
};
|
||||
|
||||
export interface PasswordProps extends InputProps {
|
||||
readonly inputPrefixCls?: string;
|
||||
readonly action?: string;
|
||||
visibilityToggle?: boolean;
|
||||
visibilityToggle?: boolean | VisibilityToggle;
|
||||
iconRender?: (visible: boolean) => React.ReactNode;
|
||||
}
|
||||
|
||||
@ -27,9 +32,20 @@ const ActionMap: Record<string, string> = {
|
||||
};
|
||||
|
||||
const Password = React.forwardRef<InputRef, PasswordProps>((props, ref) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { visibilityToggle = true } = props;
|
||||
const visibilityControlled =
|
||||
typeof visibilityToggle === 'object' && visibilityToggle.visible !== undefined;
|
||||
const [visible, setVisible] = useState(() =>
|
||||
visibilityControlled ? visibilityToggle.visible! : false,
|
||||
);
|
||||
const inputRef = useRef<InputRef>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (visibilityControlled) {
|
||||
setVisible(visibilityToggle.visible!);
|
||||
}
|
||||
}, [visibilityControlled, visibilityToggle]);
|
||||
|
||||
// Remove Password value
|
||||
const removePasswordTimeout = useRemovePasswordTimeout(inputRef);
|
||||
|
||||
@ -41,7 +57,13 @@ const Password = React.forwardRef<InputRef, PasswordProps>((props, ref) => {
|
||||
if (visible) {
|
||||
removePasswordTimeout();
|
||||
}
|
||||
setVisible(prevState => !prevState);
|
||||
setVisible(prevState => {
|
||||
const newState = !prevState;
|
||||
if (typeof visibilityToggle === 'object') {
|
||||
visibilityToggle.onVisibleChange?.(newState);
|
||||
}
|
||||
return newState;
|
||||
});
|
||||
};
|
||||
|
||||
const getIcon = (prefixCls: string) => {
|
||||
@ -72,7 +94,6 @@ const Password = React.forwardRef<InputRef, PasswordProps>((props, ref) => {
|
||||
prefixCls: customizePrefixCls,
|
||||
inputPrefixCls: customizeInputPrefixCls,
|
||||
size,
|
||||
visibilityToggle = true,
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
@ -85,7 +106,7 @@ const Password = React.forwardRef<InputRef, PasswordProps>((props, ref) => {
|
||||
});
|
||||
|
||||
const omittedProps: InputProps = {
|
||||
...omit(restProps, ['suffix', 'iconRender']),
|
||||
...omit(restProps, ['suffix', 'iconRender', 'visibilityToggle']),
|
||||
type: visible ? 'text' : 'password',
|
||||
className: inputClassName,
|
||||
prefixCls: inputPrefixCls,
|
||||
|
@ -130,4 +130,26 @@ describe('Input.Password', () => {
|
||||
jest.clearAllTimers();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('should control password visible', () => {
|
||||
const { container, rerender } = render(<Input.Password visibilityToggle={{ visible: true }} />);
|
||||
expect(container.querySelectorAll('.anticon-eye').length).toBe(1);
|
||||
rerender(<Input.Password visibilityToggle={{ visible: false }} />);
|
||||
expect(container.querySelectorAll('.anticon-eye-invisible').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should call onPasswordVisibleChange when visible is changed', () => {
|
||||
const handlePasswordVisibleChange = jest.fn();
|
||||
const { container, rerender } = render(
|
||||
<Input.Password visibilityToggle={{ onVisibleChange: handlePasswordVisibleChange }} />,
|
||||
);
|
||||
fireEvent.click(container.querySelector('.ant-input-password-icon')!);
|
||||
expect(handlePasswordVisibleChange).toHaveBeenCalledTimes(1);
|
||||
rerender(
|
||||
<Input.Password visibilityToggle={{ onVisibleChange: handlePasswordVisibleChange }} />,
|
||||
);
|
||||
expect(handlePasswordVisibleChange).toHaveBeenCalledTimes(1);
|
||||
fireEvent.click(container.querySelector('.ant-input-password-icon')!);
|
||||
expect(handlePasswordVisibleChange).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
@ -8869,6 +8869,7 @@ exports[`renders ./components/input/demo/password-input.md extend context correc
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-password"
|
||||
@ -8908,6 +8909,69 @@ exports[`renders ./components/input/demo/password-input.md extend context correc
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-right:8px"
|
||||
>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-password"
|
||||
>
|
||||
<input
|
||||
class="ant-input"
|
||||
placeholder="input password"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<span
|
||||
aria-label="eye-invisible"
|
||||
class="anticon anticon-eye-invisible ant-input-password-icon"
|
||||
role="img"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye-invisible"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M942.2 486.2Q889.47 375.11 816.7 305l-50.88 50.88C807.31 395.53 843.45 447.4 874.7 512 791.5 684.2 673.4 766 512 766q-72.67 0-133.87-22.38L323 798.75Q408 838 512 838q288.3 0 430.2-300.3a60.29 60.29 0 000-51.5zm-63.57-320.64L836 122.88a8 8 0 00-11.32 0L715.31 232.2Q624.86 186 512 186q-288.3 0-430.2 300.3a60.3 60.3 0 000 51.5q56.69 119.4 136.5 191.41L112.48 835a8 8 0 000 11.31L155.17 889a8 8 0 0011.31 0l712.15-712.12a8 8 0 000-11.32zM149.3 512C232.6 339.8 350.7 258 512 258c54.54 0 104.13 9.36 149.12 28.39l-70.3 70.3a176 176 0 00-238.13 238.13l-83.42 83.42C223.1 637.49 183.3 582.28 149.3 512zm246.7 0a112.11 112.11 0 01146.2-106.69L401.31 546.2A112 112 0 01396 512z"
|
||||
/>
|
||||
<path
|
||||
d="M508 624c-3.46 0-6.87-.16-10.25-.47l-52.82 52.82a176.09 176.09 0 00227.42-227.42l-52.82 52.82c.31 3.38.47 6.79.47 10.25a111.94 111.94 0 01-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-default"
|
||||
style="width:80px"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Show
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -2667,6 +2667,7 @@ exports[`renders ./components/input/demo/password-input.md correctly 1`] = `
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-password"
|
||||
@ -2706,6 +2707,69 @@ exports[`renders ./components/input/demo/password-input.md correctly 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-right:8px"
|
||||
>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-password"
|
||||
>
|
||||
<input
|
||||
class="ant-input"
|
||||
placeholder="input password"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<span
|
||||
aria-label="eye-invisible"
|
||||
class="anticon anticon-eye-invisible ant-input-password-icon"
|
||||
role="img"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye-invisible"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M942.2 486.2Q889.47 375.11 816.7 305l-50.88 50.88C807.31 395.53 843.45 447.4 874.7 512 791.5 684.2 673.4 766 512 766q-72.67 0-133.87-22.38L323 798.75Q408 838 512 838q288.3 0 430.2-300.3a60.29 60.29 0 000-51.5zm-63.57-320.64L836 122.88a8 8 0 00-11.32 0L715.31 232.2Q624.86 186 512 186q-288.3 0-430.2 300.3a60.3 60.3 0 000 51.5q56.69 119.4 136.5 191.41L112.48 835a8 8 0 000 11.31L155.17 889a8 8 0 0011.31 0l712.15-712.12a8 8 0 000-11.32zM149.3 512C232.6 339.8 350.7 258 512 258c54.54 0 104.13 9.36 149.12 28.39l-70.3 70.3a176 176 0 00-238.13 238.13l-83.42 83.42C223.1 637.49 183.3 582.28 149.3 512zm246.7 0a112.11 112.11 0 01146.2-106.69L401.31 546.2A112 112 0 01396 512z"
|
||||
/>
|
||||
<path
|
||||
d="M508 624c-3.46 0-6.87-.16-10.25-.47l-52.82 52.82a176.09 176.09 0 00227.42-227.42l-52.82 52.82c.31 3.38.47 6.79.47 10.25a111.94 111.94 0 01-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-default"
|
||||
style="width:80px"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Show
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -15,18 +15,31 @@ Input type of password.
|
||||
|
||||
```tsx
|
||||
import { EyeInvisibleOutlined, EyeTwoTone } from '@ant-design/icons';
|
||||
import { Input, Space } from 'antd';
|
||||
import { Button, Input, Space } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Space direction="vertical">
|
||||
<Input.Password placeholder="input password" />
|
||||
<Input.Password
|
||||
placeholder="input password"
|
||||
iconRender={visible => (visible ? <EyeTwoTone /> : <EyeInvisibleOutlined />)}
|
||||
/>
|
||||
</Space>
|
||||
);
|
||||
const App: React.FC = () => {
|
||||
const [passwordVisible, setPasswordVisible] = React.useState(false);
|
||||
|
||||
return (
|
||||
<Space direction="vertical">
|
||||
<Input.Password placeholder="input password" />
|
||||
<Input.Password
|
||||
placeholder="input password"
|
||||
iconRender={visible => (visible ? <EyeTwoTone /> : <EyeInvisibleOutlined />)}
|
||||
/>
|
||||
<Space direction="horizontal">
|
||||
<Input.Password
|
||||
placeholder="input password"
|
||||
visibilityToggle={{ visible: passwordVisible, onVisibleChange: setPasswordVisible }}
|
||||
/>
|
||||
<Button style={{ width: 80 }} onClick={() => setPasswordVisible(prevState => !prevState)}>
|
||||
{passwordVisible ? 'Hide' : 'Show'}
|
||||
</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
```
|
||||
|
@ -85,7 +85,14 @@ Supports all props of `Input`.
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| iconRender | Custom toggle button | (visible) => ReactNode | (visible) => (visible ? <EyeOutlined /> : <EyeInvisibleOutlined />) | 4.3.0 |
|
||||
| visibilityToggle | Whether show toggle button | boolean | true | |
|
||||
| visibilityToggle | Whether show toggle button or control password visible | boolean \| [VisibilityToggle](#VisibilityToggle) | true | |
|
||||
|
||||
#### VisibilityToggle
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| visible | Whether the password is show or hide | boolean | false | 4.24.0 |
|
||||
| onVisibleChange | Callback executed when visibility of the password is changed | boolean | - | 4.24.0 |
|
||||
|
||||
#### Input Methods
|
||||
|
||||
|
@ -86,7 +86,14 @@ Input 的其他属性和 React 自带的 [input](https://reactjs.org/docs/dom-el
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| iconRender | 自定义切换按钮 | (visible) => ReactNode | (visible) => (visible ? <EyeOutlined /> : <EyeInvisibleOutlined />) | 4.3.0 |
|
||||
| visibilityToggle | 是否显示切换按钮 | boolean | true | |
|
||||
| visibilityToggle | 是否显示切换按钮或者控制密码显隐 | boolean \| [VisibilityToggle](#VisibilityToggle) | true | |
|
||||
|
||||
#### VisibilityToggle
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --------------- | -------------------- | ------- | ------- | ------- |
|
||||
| visible | 用于手动控制密码显隐 | boolean | false | 4.24 |
|
||||
| onVisibleChange | 显隐密码的回调 | boolean | - | 4.24 |
|
||||
|
||||
#### Input Methods
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user