mirror of
https://github.com/ant-design/ant-design.git
synced 2025-01-18 22:36:31 +08:00
feat: support options (#38876)
* feat: support options * feat: update package * feat: update package * feat: update doc * feat: update doc * feat: update doc * feat: add test case
This commit is contained in:
parent
6f4b96567b
commit
066be35de3
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import Mentions from '..';
|
||||
import Mentions,{ Option } from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
@ -84,4 +84,39 @@ describe('Mentions', () => {
|
||||
expect(wrapper.container.querySelectorAll('li.ant-mentions-dropdown-menu-item').length).toBe(1);
|
||||
expect(wrapper.container.querySelectorAll('.bamboo-light').length).toBeTruthy();
|
||||
});
|
||||
it('do not lose label when use children Option', () => {
|
||||
const wrapper = render( <Mentions
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<Mentions.Option value="afc163">Afc163</Mentions.Option>
|
||||
<Mentions.Option value="zombieJ">ZombieJ</Mentions.Option>
|
||||
<Mentions.Option value="yesmeck">Yesmeck</Mentions.Option>
|
||||
</Mentions>);
|
||||
simulateInput(wrapper, '@');
|
||||
const {container} = wrapper
|
||||
fireEvent.mouseEnter(
|
||||
container.querySelector('li.ant-mentions-dropdown-menu-item:last-child')!,
|
||||
);
|
||||
fireEvent.focus(container.querySelector('textarea')!);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(
|
||||
wrapper.container.querySelector('.ant-mentions-dropdown-menu-item-active')?.textContent,
|
||||
).toBe('Yesmeck');
|
||||
});
|
||||
it('warning if use Mentions.Option', () => {
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(
|
||||
<Mentions style={{ width: '100%' }} defaultValue="@afc163">
|
||||
<Option value="afc163">afc163</Option>
|
||||
<Option value="zombieJ">zombieJ</Option>
|
||||
<Option value="yesmeck">yesmeck</Option>
|
||||
</Mentions>,
|
||||
);
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Mentions] `Mentions.Option` is deprecated. Please use `options` instead.',
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -14,11 +14,10 @@ title:
|
||||
async
|
||||
|
||||
```tsx
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import { Mentions } from 'antd';
|
||||
import debounce from 'lodash/debounce';
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
|
||||
const { Option } = Mentions;
|
||||
const App: React.FC = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [users, setUsers] = useState<{ login: string; avatar_url: string }[]>([]);
|
||||
@ -52,14 +51,22 @@ const App: React.FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Mentions style={{ width: '100%' }} loading={loading} onSearch={onSearch}>
|
||||
{users.map(({ login, avatar_url: avatar }) => (
|
||||
<Option key={login} value={login} className="antd-demo-dynamic-option">
|
||||
<img src={avatar} alt={login} />
|
||||
<span>{login}</span>
|
||||
</Option>
|
||||
))}
|
||||
</Mentions>
|
||||
<Mentions
|
||||
style={{ width: '100%' }}
|
||||
loading={loading}
|
||||
onSearch={onSearch}
|
||||
options={users.map(({ login, avatar_url: avatar }) => ({
|
||||
key: login,
|
||||
value: login,
|
||||
className: 'antd-demo-dynamic-option',
|
||||
label: (
|
||||
<>
|
||||
<img src={avatar} alt={login} />
|
||||
<span>{login}</span>
|
||||
</>
|
||||
),
|
||||
}))}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -14,17 +14,28 @@ title:
|
||||
Height autoSize.
|
||||
|
||||
```tsx
|
||||
import { Mentions } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const { Option } = Mentions;
|
||||
import { Mentions } from 'antd';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Mentions autoSize style={{ width: '100%' }}>
|
||||
<Option value="afc163">afc163</Option>
|
||||
<Option value="zombieJ">zombieJ</Option>
|
||||
<Option value="yesmeck">yesmeck</Option>
|
||||
</Mentions>
|
||||
<Mentions
|
||||
autoSize
|
||||
style={{ width: '100%' }}
|
||||
options={[
|
||||
{
|
||||
value: 'afc163',
|
||||
label: 'afc163',
|
||||
},
|
||||
{
|
||||
value: 'zombieJ',
|
||||
label: 'zombieJ',
|
||||
},
|
||||
{
|
||||
value: 'yesmeck',
|
||||
label: 'yesmeck',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
@ -14,17 +14,15 @@ title:
|
||||
Basic usage.
|
||||
|
||||
```tsx
|
||||
import { Mentions } from 'antd';
|
||||
import type { OptionProps } from 'antd/es/mentions';
|
||||
import React from 'react';
|
||||
|
||||
const { Option } = Mentions;
|
||||
import { Mentions } from 'antd';
|
||||
import type { MentionsOptionProps } from 'antd/es/mentions';
|
||||
|
||||
const onChange = (value: string) => {
|
||||
console.log('Change:', value);
|
||||
};
|
||||
|
||||
const onSelect = (option: OptionProps) => {
|
||||
const onSelect = (option: MentionsOptionProps) => {
|
||||
console.log('select', option);
|
||||
};
|
||||
|
||||
@ -34,11 +32,21 @@ const App: React.FC = () => (
|
||||
onChange={onChange}
|
||||
onSelect={onSelect}
|
||||
defaultValue="@afc163"
|
||||
>
|
||||
<Option value="afc163">afc163</Option>
|
||||
<Option value="zombieJ">zombieJ</Option>
|
||||
<Option value="yesmeck">yesmeck</Option>
|
||||
</Mentions>
|
||||
options={[
|
||||
{
|
||||
value: 'afc163',
|
||||
label: 'afc163',
|
||||
},
|
||||
{
|
||||
value: 'zombieJ',
|
||||
label: 'zombieJ',
|
||||
},
|
||||
{
|
||||
value: 'yesmeck',
|
||||
label: 'yesmeck',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
@ -14,10 +14,10 @@ title:
|
||||
Controlled mode, for example, to work with `Form`.
|
||||
|
||||
```tsx
|
||||
import { Button, Form, Mentions } from 'antd';
|
||||
import React from 'react';
|
||||
import { Button, Form, Mentions } from 'antd';
|
||||
|
||||
const { Option, getMentions } = Mentions;
|
||||
const { getMentions } = Mentions;
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [form] = Form.useForm();
|
||||
@ -52,11 +52,23 @@ const App: React.FC = () => {
|
||||
wrapperCol={{ span: 16 }}
|
||||
rules={[{ validator: checkMention }]}
|
||||
>
|
||||
<Mentions rows={1}>
|
||||
<Option value="afc163">afc163</Option>
|
||||
<Option value="zombieJ">zombieJ</Option>
|
||||
<Option value="yesmeck">yesmeck</Option>
|
||||
</Mentions>
|
||||
<Mentions
|
||||
rows={1}
|
||||
options={[
|
||||
{
|
||||
value: 'afc163',
|
||||
label: 'afc163',
|
||||
},
|
||||
{
|
||||
value: 'zombieJ',
|
||||
label: 'zombieJ',
|
||||
},
|
||||
{
|
||||
value: 'yesmeck',
|
||||
label: 'yesmeck',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="bio"
|
||||
@ -65,11 +77,24 @@ const App: React.FC = () => {
|
||||
wrapperCol={{ span: 16 }}
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Mentions rows={3} placeholder="You can use @ to ref user here">
|
||||
<Option value="afc163">afc163</Option>
|
||||
<Option value="zombieJ">zombieJ</Option>
|
||||
<Option value="yesmeck">yesmeck</Option>
|
||||
</Mentions>
|
||||
<Mentions
|
||||
rows={3}
|
||||
placeholder="You can use @ to ref user here"
|
||||
options={[
|
||||
{
|
||||
value: 'afc163',
|
||||
label: 'afc163',
|
||||
},
|
||||
{
|
||||
value: 'zombieJ',
|
||||
label: 'zombieJ',
|
||||
},
|
||||
{
|
||||
value: 'yesmeck',
|
||||
label: 'yesmeck',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item wrapperCol={{ span: 14, offset: 6 }}>
|
||||
<Button htmlType="submit" type="primary">
|
||||
|
@ -14,17 +14,28 @@ title:
|
||||
Change the suggestions placement.
|
||||
|
||||
```tsx
|
||||
import { Mentions } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const { Option } = Mentions;
|
||||
import { Mentions } from 'antd';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Mentions style={{ width: '100%' }} placement="top">
|
||||
<Option value="afc163">afc163</Option>
|
||||
<Option value="zombieJ">zombieJ</Option>
|
||||
<Option value="yesmeck">yesmeck</Option>
|
||||
</Mentions>
|
||||
<Mentions
|
||||
style={{ width: '100%' }}
|
||||
placement="top"
|
||||
options={[
|
||||
{
|
||||
value: 'afc163',
|
||||
label: 'afc163',
|
||||
},
|
||||
{
|
||||
value: 'zombieJ',
|
||||
label: 'zombieJ',
|
||||
},
|
||||
{
|
||||
value: 'yesmeck',
|
||||
label: 'yesmeck',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
@ -14,10 +14,8 @@ title:
|
||||
Customize Trigger Token by `prefix` props. Default to `@`, `Array<string>` also supported.
|
||||
|
||||
```tsx
|
||||
import { Mentions } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const { Option } = Mentions;
|
||||
import { Mentions } from 'antd';
|
||||
|
||||
const MOCK_DATA = {
|
||||
'@': ['afc163', 'zombiej', 'yesmeck'],
|
||||
@ -39,13 +37,12 @@ const App: React.FC = () => {
|
||||
placeholder="input @ to mention people, # to mention tag"
|
||||
prefix={['@', '#']}
|
||||
onSearch={onSearch}
|
||||
>
|
||||
{(MOCK_DATA[prefix] || []).map(value => (
|
||||
<Option key={value} value={value}>
|
||||
{value}
|
||||
</Option>
|
||||
))}
|
||||
</Mentions>
|
||||
options={(MOCK_DATA[prefix] || []).map(value => ({
|
||||
key: value,
|
||||
value,
|
||||
label: value,
|
||||
}))}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -14,28 +14,31 @@ title:
|
||||
Configurate `disabled` and `readOnly`.
|
||||
|
||||
```tsx
|
||||
import { Mentions } from 'antd';
|
||||
import React from 'react';
|
||||
import { Mentions } from 'antd';
|
||||
|
||||
const { Option } = Mentions;
|
||||
|
||||
const getOptions = () =>
|
||||
['afc163', 'zombiej', 'yesmeck'].map(value => (
|
||||
<Option key={value} value={value}>
|
||||
{value}
|
||||
</Option>
|
||||
));
|
||||
const options = ['afc163', 'zombiej', 'yesmeck'].map(value => ({
|
||||
value,
|
||||
key: value,
|
||||
label: value,
|
||||
}));
|
||||
|
||||
const App: React.FC = () => (
|
||||
<>
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<Mentions style={{ width: '100%' }} placeholder="this is disabled Mentions" disabled>
|
||||
{getOptions()}
|
||||
</Mentions>
|
||||
<Mentions
|
||||
style={{ width: '100%' }}
|
||||
placeholder="this is disabled Mentions"
|
||||
disabled
|
||||
options={options}
|
||||
/>
|
||||
</div>
|
||||
<Mentions style={{ width: '100%' }} placeholder="this is readOnly Mentions" readOnly>
|
||||
{getOptions()}
|
||||
</Mentions>
|
||||
<Mentions
|
||||
style={{ width: '100%' }}
|
||||
placeholder="this is readOnly Mentions"
|
||||
readOnly
|
||||
options={options}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
|
@ -11,14 +11,26 @@ Mention component.
|
||||
|
||||
When you need to mention someone or something.
|
||||
|
||||
## API
|
||||
### Usage upgrade after 4.24.4
|
||||
|
||||
```__react
|
||||
import Alert from '../alert';
|
||||
ReactDOM.render(<Alert message="After version 4.24.4, we provide a simpler usage <Mentions options={[...]} /> with better performance and potential of writing simpler code style in your applications. Meanwhile, we deprecated the old usage in browser console, we will remove it in antd 5.0." />, mountNode);
|
||||
```
|
||||
|
||||
```jsx
|
||||
// works when >=4.24.4, recommended ✅
|
||||
const options = [{ value: 'sample', label: 'sample' }];
|
||||
return <Mentions options={options} />;
|
||||
|
||||
// works when <4.24.4, deprecated when >=4.24.4 🙅🏻♀️
|
||||
<Mentions onChange={onChange}>
|
||||
<Mentions.Option value="sample">Sample</Mentions.Option>
|
||||
</Mentions>
|
||||
</Mentions>;
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Mention
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
@ -41,6 +53,7 @@ When you need to mention someone or something.
|
||||
| onResize | The callback function that is triggered when textarea resize | function({ width, height }) | - | |
|
||||
| onSearch | Trigger when prefix hit | (text: string, prefix: string) => void | - | |
|
||||
| onSelect | Trigger when user select the option | (option: OptionProps, prefix: string) => void | - | |
|
||||
| options | Option Configuration | [Options](#Option) | \[] | 4.24.4 |
|
||||
|
||||
### Mention methods
|
||||
|
||||
@ -53,5 +66,9 @@ When you need to mention someone or something.
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| children | Suggestion content | ReactNode | - |
|
||||
| value | The value of suggestion, the value will insert into input filed while selected | string | - |
|
||||
| label | Title of the option | React.ReactNode | - |
|
||||
| key | The key value of the option | string | - |
|
||||
| disabled | Optional | boolean | - |
|
||||
| className | className | string | - |
|
||||
| style | The style of the option | React.CSSProperties | - |
|
||||
|
@ -3,6 +3,7 @@ import RcMentions from 'rc-mentions';
|
||||
import type {
|
||||
MentionsProps as RcMentionsProps,
|
||||
MentionsRef as RcMentionsRef,
|
||||
DataDrivenOptionProps as MentionsOptionProps,
|
||||
} from 'rc-mentions/lib/Mentions';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
import * as React from 'react';
|
||||
@ -12,6 +13,7 @@ import { FormItemInputContext } from '../form/context';
|
||||
import Spin from '../spin';
|
||||
import type { InputStatus } from '../_util/statusUtils';
|
||||
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
|
||||
import warning from '../_util/warning';
|
||||
|
||||
export const { Option } = RcMentions;
|
||||
|
||||
@ -21,6 +23,10 @@ function loadingFilterOption() {
|
||||
|
||||
export type MentionPlacement = 'top' | 'bottom';
|
||||
|
||||
export type {
|
||||
DataDrivenOptionProps as MentionsOptionProps,
|
||||
} from 'rc-mentions/lib/Mentions';
|
||||
|
||||
export interface OptionProps {
|
||||
value: string;
|
||||
children: React.ReactNode;
|
||||
@ -30,6 +36,7 @@ export interface OptionProps {
|
||||
export interface MentionProps extends RcMentionsProps {
|
||||
loading?: boolean;
|
||||
status?: InputStatus;
|
||||
options?: MentionsOptionProps[];
|
||||
}
|
||||
|
||||
export interface MentionsRef extends RcMentionsRef {}
|
||||
@ -63,6 +70,7 @@ const InternalMentions: React.ForwardRefRenderFunction<MentionsRef, MentionProps
|
||||
filterOption,
|
||||
children,
|
||||
notFoundContent,
|
||||
options,
|
||||
status: customStatus,
|
||||
...restProps
|
||||
},
|
||||
@ -71,6 +79,16 @@ const InternalMentions: React.ForwardRefRenderFunction<MentionsRef, MentionProps
|
||||
const [focused, setFocused] = React.useState(false);
|
||||
const innerRef = React.useRef<MentionsRef>();
|
||||
const mergedRef = composeRef(ref, innerRef);
|
||||
|
||||
// =================== Warning =====================
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warning(
|
||||
!children,
|
||||
'Mentions',
|
||||
'`Mentions.Option` is deprecated. Please use `options` instead.',
|
||||
);
|
||||
}
|
||||
|
||||
const { getPrefixCls, renderEmpty, direction } = React.useContext(ConfigContext);
|
||||
const {
|
||||
status: contextStatus,
|
||||
@ -114,6 +132,16 @@ const InternalMentions: React.ForwardRefRenderFunction<MentionsRef, MentionProps
|
||||
return children;
|
||||
};
|
||||
|
||||
const mergedOptions = loading
|
||||
? [
|
||||
{
|
||||
value: 'ANTD_SEARCHING',
|
||||
disabled: true,
|
||||
label: <Spin size="small" />,
|
||||
},
|
||||
]
|
||||
: options;
|
||||
|
||||
const getFilterOption = (): any => {
|
||||
if (loading) {
|
||||
return loadingFilterOption;
|
||||
@ -145,6 +173,7 @@ const InternalMentions: React.ForwardRefRenderFunction<MentionsRef, MentionProps
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
ref={mergedRef as any}
|
||||
options={mergedOptions}
|
||||
>
|
||||
{getOptions()}
|
||||
</RcMentions>
|
||||
@ -185,7 +214,7 @@ Mentions.getMentions = (value: string = '', config: MentionsConfig = {}): Mentio
|
||||
.map((str = ''): MentionsEntity | null => {
|
||||
let hitPrefix: string | null = null;
|
||||
|
||||
prefixList.some(prefixStr => {
|
||||
prefixList.some((prefixStr) => {
|
||||
const startStr = str.slice(0, prefixStr.length);
|
||||
if (startStr === prefixStr) {
|
||||
hitPrefix = prefixStr;
|
||||
|
@ -12,14 +12,26 @@ cover: https://gw.alipayobjects.com/zos/alicdn/jPE-itMFM/Mentions.svg
|
||||
|
||||
用于在输入中提及某人或某事,常用于发布、聊天或评论功能。
|
||||
|
||||
## API
|
||||
### 4.24.4 用法升级
|
||||
|
||||
```__react
|
||||
import Alert from '../alert';
|
||||
ReactDOM.render(<Alert message="在 4.24.4 版本后,我们提供了 <Mentions options={[...]} /> 的简写方式,有更好的性能和更方便的数据组织方式,开发者不再需要自行拼接 JSX。同时我们废弃了原先的写法,你还是可以在 4.x 继续使用,但会在控制台看到警告,并会在 5.0 后移除。" />, mountNode);
|
||||
```
|
||||
|
||||
```jsx
|
||||
// >=4.24.4 可用,推荐的写法 ✅
|
||||
const options = [{ value: 'sample', label: 'sample' }];
|
||||
return <Mentions options={options} />;
|
||||
|
||||
// <4.24.4 可用,>=4.24.4 时不推荐 🙅🏻♀️
|
||||
<Mentions onChange={onChange}>
|
||||
<Mentions.Option value="sample">Sample</Mentions.Option>
|
||||
</Mentions>
|
||||
</Mentions>;
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Mentions
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
@ -42,6 +54,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/jPE-itMFM/Mentions.svg
|
||||
| onResize | resize 回调 | function({ width, height }) | - | |
|
||||
| onSearch | 搜索时触发 | (text: string, prefix: string) => void | - | |
|
||||
| onSelect | 选择选项时触发 | (option: OptionProps, prefix: string) => void | - | |
|
||||
| options | 选项配置 | [Options](#Option) | [] | 4.24.4 |
|
||||
|
||||
### Mentions 方法
|
||||
|
||||
@ -52,7 +65,11 @@ cover: https://gw.alipayobjects.com/zos/alicdn/jPE-itMFM/Mentions.svg
|
||||
|
||||
### Option
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| -------- | -------------- | --------- | ------ |
|
||||
| children | 选项内容 | ReactNode | - |
|
||||
| value | 选择时填充的值 | string | - |
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --------- | -------------- | ------------------- | ------ |
|
||||
| value | 选择时填充的值 | string | - |
|
||||
| label | 选项的标题 | React.ReactNode | - |
|
||||
| key | 选项的 key 值 | string | - |
|
||||
| disabled | 是否可选 | boolean | - |
|
||||
| className | css 类名 | string | - |
|
||||
| style | 选项样式 | React.CSSProperties | - |
|
||||
|
@ -134,8 +134,8 @@
|
||||
"rc-image": "~5.12.0",
|
||||
"rc-input": "~0.1.4",
|
||||
"rc-input-number": "~7.3.9",
|
||||
"rc-mentions": "~1.11.0",
|
||||
"rc-menu": "~9.7.2",
|
||||
"rc-mentions": "~1.13.1",
|
||||
"rc-menu": "~9.8.0",
|
||||
"rc-motion": "^2.6.1",
|
||||
"rc-notification": "~4.6.0",
|
||||
"rc-pagination": "~3.2.0",
|
||||
@ -149,7 +149,7 @@
|
||||
"rc-steps": "~5.0.0-alpha.2",
|
||||
"rc-switch": "~3.2.0",
|
||||
"rc-table": "~7.26.0",
|
||||
"rc-tabs": "~12.3.0",
|
||||
"rc-tabs": "~12.4.1",
|
||||
"rc-textarea": "~0.4.5",
|
||||
"rc-tooltip": "~5.2.0",
|
||||
"rc-tree": "~5.7.0",
|
||||
|
Loading…
Reference in New Issue
Block a user