-
-
Notifications
You must be signed in to change notification settings - Fork 334
feat:add maxTagPlaceholder api #938
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
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -23,17 +23,37 @@ const sharedLocale = { | |||||||||||||||||||||||||||||||||||||
style: { width: 300 }, | ||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
const maxTagPlaceholder = (value: any[]) => { | ||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||
<ul> | ||||||||||||||||||||||||||||||||||||||
{value?.map((item) => { | ||||||||||||||||||||||||||||||||||||||
return <li>{item?.format('YYYY-MM-DD')}</li>; | ||||||||||||||||||||||||||||||||||||||
})} | ||||||||||||||||||||||||||||||||||||||
</ul> | ||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||
|
const maxTagPlaceholder = (value: any[]) => { | |
return ( | |
<ul> | |
{value?.map((item) => { | |
return <li>{item?.format('YYYY-MM-DD')}</li>; | |
})} | |
</ul> | |
); | |
}; | |
const maxTagPlaceholder = (value: any[]) => { | |
return ( | |
<ul> | |
{value?.map((item, idx) => { | |
return <li key={item?.valueOf?.() ?? idx}>{item?.format('YYYY-MM-DD')}</li>; | |
})} | |
</ul> | |
); | |
}; |
🧰 Tools
🪛 Biome (2.1.2)
[error] 30-30: Missing key property for this element in iterable.
The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.
(lint/correctness/useJsxKeyInIterable)
🤖 Prompt for AI Agents
In docs/examples/multiple.tsx around lines 26 to 34, the <li> elements inside
the map are missing a stable key causing React/BIOME warnings; update the map to
pass a unique key to each <li> (for example use item.format('YYYY-MM-DD') or
another unique identifier on the item, and fall back to the index only if no
stable id exists) so each list item has a stable key prop.
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -4,7 +4,7 @@ import * as React from 'react'; | |||||||||||||||||||||||||||||||||||||
import type { PickerProps } from '../../SinglePicker'; | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
export interface MultipleDatesProps<DateType extends object = any> | ||||||||||||||||||||||||||||||||||||||
extends Pick<PickerProps, 'maxTagCount'> { | ||||||||||||||||||||||||||||||||||||||
extends Pick<PickerProps, 'maxTagCount' | 'maxTagPlaceholder'> { | ||||||||||||||||||||||||||||||||||||||
prefixCls: string; | ||||||||||||||||||||||||||||||||||||||
value: DateType[]; | ||||||||||||||||||||||||||||||||||||||
onRemove: (value: DateType) => void; | ||||||||||||||||||||||||||||||||||||||
|
@@ -25,6 +25,7 @@ export default function MultipleDates<DateType extends object = any>( | |||||||||||||||||||||||||||||||||||||
formatDate, | ||||||||||||||||||||||||||||||||||||||
disabled, | ||||||||||||||||||||||||||||||||||||||
maxTagCount, | ||||||||||||||||||||||||||||||||||||||
maxTagPlaceholder, | ||||||||||||||||||||||||||||||||||||||
placeholder, | ||||||||||||||||||||||||||||||||||||||
} = props; | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
|
@@ -68,8 +69,13 @@ export default function MultipleDates<DateType extends object = any>( | |||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
// ========================= Rest ========================= | ||||||||||||||||||||||||||||||||||||||
function renderRest(omittedValues: DateType[]) { | ||||||||||||||||||||||||||||||||||||||
const content = `+ ${omittedValues.length} ...`; | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
if (!value.length) { | ||||||||||||||||||||||||||||||||||||||
return null; | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
const content = | ||||||||||||||||||||||||||||||||||||||
typeof maxTagPlaceholder === 'function' | ||||||||||||||||||||||||||||||||||||||
? maxTagPlaceholder(omittedValues) | ||||||||||||||||||||||||||||||||||||||
: maxTagPlaceholder; | ||||||||||||||||||||||||||||||||||||||
return renderSelector(content); | ||||||||||||||||||||||||||||||||||||||
|
if (!value.length) { | |
return null; | |
} | |
const content = | |
typeof maxTagPlaceholder === 'function' | |
? maxTagPlaceholder(omittedValues) | |
: maxTagPlaceholder; | |
return renderSelector(content); | |
function renderRest(omittedValues: DateType[]) { | |
if (!omittedValues?.length) { | |
return null; | |
} | |
const content = | |
typeof maxTagPlaceholder === 'function' | |
? maxTagPlaceholder(omittedValues) | |
: (maxTagPlaceholder ?? `+ ${omittedValues.length} ...`); | |
return renderSelector(content); | |
} |
🤖 Prompt for AI Agents
In src/PickerInput/Selector/SingleSelector/MultipleDates.tsx around lines 72 to
79, the early-return currently checks value.length which can wrongly suppress or
render the maxTagPlaceholder when there are no omitted items; change the
condition to check omittedValues.length instead so the component returns null
when there are no omitted items to display, and only computes/render the
maxTagPlaceholder when omittedValues.length > 0.
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -12,7 +12,7 @@ import MultipleDates from './MultipleDates'; | |||||
|
||||||
export interface SingleSelectorProps<DateType extends object = any> | ||||||
extends SelectorProps<DateType>, | ||||||
Pick<PickerProps, 'multiple' | 'maxTagCount'> { | ||||||
Pick<PickerProps, 'multiple' | 'maxTagCount' | 'maxTagPlaceholder'> { | ||||||
id?: string; | ||||||
|
||||||
value?: DateType[]; | ||||||
|
@@ -75,6 +75,7 @@ function SingleSelector<DateType extends object = any>( | |||||
onInputChange, | ||||||
multiple, | ||||||
maxTagCount, | ||||||
maxTagPlaceholder = (omittedValues: string[]) => `+ ${omittedValues.length} ...`, | ||||||
|
maxTagPlaceholder = (omittedValues: string[]) => `+ ${omittedValues.length} ...`, | |
maxTagPlaceholder = (omittedValues: DateType[]) => `+ ${omittedValues.length} ...`, |
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -155,6 +155,115 @@ describe('Picker.Multiple', () => { | |
).toBeFalsy(); | ||
}); | ||
}); | ||
describe('maxTagPlaceholder', () => { | ||
it('should not show maxTagPlaceholder when items count is within maxTagCount', () => { | ||
const maxTagPlaceholder = (omittedValues: any[]) => ( | ||
<span className="custom-max-tag-placeholder">+{omittedValues.length} more</span> | ||
); | ||
|
||
const { container } = render( | ||
<DayPicker | ||
multiple | ||
maxTagCount={3} | ||
maxTagPlaceholder={maxTagPlaceholder} | ||
defaultValue={[getDay('2000-01-01'), getDay('2000-01-02')]} | ||
/>, | ||
); | ||
|
||
// Should show all items, no placeholder | ||
expect(container.querySelectorAll('.rc-picker-selection-item')).toHaveLength(2); | ||
expect(container.querySelector('.custom-max-tag-placeholder')).toBeFalsy(); | ||
}); | ||
|
||
it('should show maxTagPlaceholder when items count exceeds maxTagCount', () => { | ||
const maxTagPlaceholder = (omittedValues: any[]) => ( | ||
<span className="custom-max-tag-placeholder">+{omittedValues.length} items</span> | ||
); | ||
|
||
const { container } = render( | ||
<DayPicker | ||
multiple | ||
maxTagCount={2} | ||
maxTagPlaceholder={maxTagPlaceholder} | ||
defaultValue={[ | ||
getDay('2000-01-01'), | ||
getDay('2000-01-02'), | ||
getDay('2000-01-03'), | ||
getDay('2000-01-04'), | ||
]} | ||
/>, | ||
); | ||
|
||
// Should show maxTagCount items + placeholder | ||
expect(container.querySelectorAll('.rc-picker-selection-item')).toHaveLength(3); | ||
expect(container.querySelector('.custom-max-tag-placeholder').textContent).toBe('+2 items'); | ||
}); | ||
|
||
it('should work with custom maxTagPlaceholder component', () => { | ||
const CustomPlaceholder = ({ omittedValues }: { omittedValues: any[] }) => ( | ||
<div className="custom-placeholder-wrapper"> | ||
<span className="omitted-count">{omittedValues.length}</span> | ||
<span className="omitted-text">hidden dates</span> | ||
</div> | ||
); | ||
|
||
const { container } = render( | ||
<DayPicker | ||
multiple | ||
maxTagCount={1} | ||
maxTagPlaceholder={(omittedValues) => <CustomPlaceholder omittedValues={omittedValues} />} | ||
defaultValue={[getDay('2000-01-01'), getDay('2000-01-02'), getDay('2000-01-03')]} | ||
/>, | ||
); | ||
|
||
expect(container.querySelector('.custom-placeholder-wrapper')).toBeTruthy(); | ||
expect(container.querySelector('.omitted-count').textContent).toBe('2'); | ||
expect(container.querySelector('.omitted-text').textContent).toBe('hidden dates'); | ||
}); | ||
|
||
it('should handle maxTagCount edge cases1', () => { | ||
const maxTagPlaceholder = (omittedValues: any[]) => ( | ||
<span className="edge-case-placeholder">+{omittedValues.length}</span> | ||
); | ||
|
||
// Test maxTagCount = 0 | ||
const { container, rerender } = render( | ||
|
||
<DayPicker | ||
multiple | ||
maxTagCount={0} | ||
maxTagPlaceholder={maxTagPlaceholder} | ||
defaultValue={[getDay('2000-01-01')]} | ||
/>, | ||
); | ||
expect(container.querySelectorAll('.rc-picker-selection-item')).toHaveLength(1); | ||
expect(container.querySelector('.edge-case-placeholder')).toBeTruthy(); | ||
expect(container.querySelector('.edge-case-placeholder').textContent).toBe('+1'); | ||
}); | ||
|
||
it('should pass correct omittedValues to maxTagPlaceholder', () => { | ||
const maxTagPlaceholder = jest.fn((omittedValues) => ( | ||
<span className="test-placeholder">+{omittedValues.length}</span> | ||
)); | ||
|
||
const values = [ | ||
getDay('2000-01-01'), | ||
getDay('2000-01-02'), | ||
getDay('2000-01-03'), | ||
getDay('2000-01-04'), | ||
]; | ||
|
||
render( | ||
<DayPicker | ||
multiple | ||
maxTagCount={2} | ||
maxTagPlaceholder={maxTagPlaceholder} | ||
defaultValue={values} | ||
/>, | ||
); | ||
|
||
expect(maxTagPlaceholder).toHaveBeenCalledWith([values[2], values[3]]); | ||
}); | ||
}); | ||
|
||
it('click year panel should not select', () => { | ||
const onChange = jest.fn(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maxTagPlaceholder
函数存在两个问题:value
参数的类型是any[]
,这太宽泛了。考虑到这个文件的上下文,它应该被更具体地类型化为dayjs.Dayjs[]
。map
函数中生成的<li>
元素缺少key
属性。在 React 中,列表中的每个元素都必须有一个唯一的key
,以避免重新渲染时的性能问题和潜在的 bug。