Skip to content

Commit 4853ba4

Browse files
authored
fix: title in option not work (#833)
* test: fix title missing * test: add test case * chore: clean lint rule
1 parent 590e54d commit 4853ba4

File tree

7 files changed

+60
-9
lines changed

7 files changed

+60
-9
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ module.exports = {
99
'react/sort-comp': 0,
1010
'jsx-a11y/interactive-supports-focus': 0,
1111
'jsx-a11y/no-autofocus': 0,
12+
'react/no-unknown-property': 0,
1213
},
1314
};

src/BaseSelect.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export interface DisplayValueType {
6161
key?: React.Key;
6262
value?: RawValueType;
6363
label?: React.ReactNode;
64+
title?: string | number;
6465
disabled?: boolean;
6566
}
6667

src/Select.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ const Select = React.forwardRef(
247247
let rawLabel: React.ReactNode;
248248
let rawKey: React.Key;
249249
let rawDisabled: boolean | undefined;
250+
let rawTitle: string;
250251

251252
// Fill label & value
252253
if (isRawValue(val)) {
@@ -264,6 +265,7 @@ const Select = React.forwardRef(
264265
rawLabel = option?.[optionLabelProp || mergedFieldNames.label];
265266
if (rawKey === undefined) rawKey = option?.key ?? rawValue;
266267
rawDisabled = option?.disabled;
268+
rawTitle = option?.title;
267269

268270
// Warning if label not same as provided
269271
if (process.env.NODE_ENV !== 'production' && !optionLabelProp) {
@@ -279,6 +281,7 @@ const Select = React.forwardRef(
279281
value: rawValue,
280282
key: rawKey,
281283
disabled: rawDisabled,
284+
title: rawTitle,
282285
};
283286
});
284287
},

src/Selector/MultipleSelector.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { InnerSelectorProps } from '.';
88
import Input from './Input';
99
import useLayoutEffect from '../hooks/useLayoutEffect';
1010
import type { DisplayValueType, RenderNode, CustomTagProps, RawValueType } from '../BaseSelect';
11+
import { getTitle } from '../utils/commonUtil';
1112

1213
function itemKey(value: DisplayValueType) {
1314
return value.key ?? value.value;
@@ -89,7 +90,7 @@ const SelectSelector: React.FC<SelectorProps> = (props) => {
8990
// ===================== Render ======================
9091
// >>> Render Selector Node. Includes Item & Rest
9192
function defaultRenderSelector(
92-
title: React.ReactNode,
93+
item: DisplayValueType,
9394
content: React.ReactNode,
9495
itemDisabled: boolean,
9596
closable?: boolean,
@@ -100,9 +101,7 @@ const SelectSelector: React.FC<SelectorProps> = (props) => {
100101
className={classNames(`${selectionPrefixCls}-item`, {
101102
[`${selectionPrefixCls}-item-disabled`]: itemDisabled,
102103
})}
103-
title={
104-
typeof title === 'string' || typeof title === 'number' ? title.toString() : undefined
105-
}
104+
title={getTitle(item)}
106105
>
107106
<span className={`${selectionPrefixCls}-item-content`}>{content}</span>
108107
{closable && (
@@ -167,7 +166,7 @@ const SelectSelector: React.FC<SelectorProps> = (props) => {
167166

168167
return typeof tagRender === 'function'
169168
? customizeRenderSelector(value, displayLabel, itemDisabled, closable, onClose)
170-
: defaultRenderSelector(label, displayLabel, itemDisabled, closable, onClose);
169+
: defaultRenderSelector(valueItem, displayLabel, itemDisabled, closable, onClose);
171170
}
172171

173172
function renderRest(omittedValues: DisplayValueType[]) {

src/Selector/SingleSelector.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as React from 'react';
22
import pickAttrs from 'rc-util/lib/pickAttrs';
33
import Input from './Input';
44
import type { InnerSelectorProps } from '.';
5+
import { getTitle } from '../utils/commonUtil';
56

67
interface SelectorProps extends InnerSelectorProps {
78
inputElement: React.ReactElement;
@@ -57,10 +58,8 @@ const SingleSelector: React.FC<SelectorProps> = (props) => {
5758
// Not show text when closed expect combobox mode
5859
const hasTextInput = mode !== 'combobox' && !open && !showSearch ? false : !!inputValue;
5960

60-
const title =
61-
item && (typeof item.label === 'string' || typeof item.label === 'number')
62-
? item.label.toString()
63-
: undefined;
61+
// Get title
62+
const title = getTitle(item);
6463

6564
const renderPlaceholder = () => {
6665
if (item) {

src/utils/commonUtil.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { DisplayValueType } from '../BaseSelect';
2+
13
export function toArray<T>(value: T | T[]): T[] {
24
if (Array.isArray(value)) {
35
return value;
@@ -14,3 +16,20 @@ export const isBrowserClient = process.env.NODE_ENV !== 'test' && isClient;
1416
export function hasValue(value) {
1517
return value !== undefined && value !== null;
1618
}
19+
20+
function isTitleType(title: any) {
21+
return ['string', 'number'].includes(typeof title);
22+
}
23+
24+
export function getTitle(item: DisplayValueType): string {
25+
let title: string = undefined;
26+
if (item) {
27+
if (isTitleType(item.title)) {
28+
title = item.title.toString();
29+
} else if (isTitleType(item.label)) {
30+
title = item.label.toString();
31+
}
32+
}
33+
34+
return title;
35+
}

tests/Select.test.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1914,4 +1914,33 @@ describe('Select.Basic', () => {
19141914
expect(wrapper.find('div.rc-select-item').prop('data-test')).toEqual('good');
19151915
expect(wrapper.find('div.rc-select-item').prop('aria-label')).toEqual('well');
19161916
});
1917+
1918+
// https://github.com/ant-design/ant-design/issues/37591
1919+
describe('title attr', () => {
1920+
it('single', () => {
1921+
const wrapper = mount(
1922+
<Select value="b" options={[{ label: 'bamboo', title: 'TitleBamboo', value: 'b' }]} />,
1923+
);
1924+
1925+
expect(wrapper.find('.rc-select-selection-item').prop('title')).toEqual('TitleBamboo');
1926+
});
1927+
1928+
it('multiple', () => {
1929+
const wrapper = mount(
1930+
<Select
1931+
mode="multiple"
1932+
value={['b', 'l']}
1933+
options={[
1934+
{ label: 'bamboo', title: 'TitleBamboo', value: 'b' },
1935+
{ label: 'little', value: 'l' },
1936+
]}
1937+
/>,
1938+
);
1939+
1940+
expect(wrapper.find('.rc-select-selection-item').first().prop('title')).toEqual(
1941+
'TitleBamboo',
1942+
);
1943+
expect(wrapper.find('.rc-select-selection-item').last().prop('title')).toEqual('little');
1944+
});
1945+
});
19171946
});

0 commit comments

Comments
 (0)