fix: SelectItem, ListboxItem, and AutocompleteItem not to accept value props (#4653)

* fix(select): `SelectItem` does not accept value props

* refactor: do not use the index as `key`

* Update .changeset/light-hairs-draw.md

* chore: remove unnecessary `value` props

* chore: update changeset

* refactor: remove unnecessary value prop

---------

Co-authored-by: WK Wong <wingkwong.code@gmail.com>
This commit is contained in:
Ryo Matsukawa 2025-02-11 22:07:20 +09:00 committed by GitHub
parent 8af2c5d8b1
commit 28b8606411
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 93 additions and 229 deletions

View File

@ -0,0 +1,7 @@
---
"@heroui/autocomplete": patch
"@heroui/listbox": patch
"@heroui/select": patch
---
Fix SelectItem, ListboxItem, and AutocompleteItem not to accept value props (#2283)

View File

@ -116,21 +116,11 @@ export default function App() {
name="country"
placeholder="Select country"
>
<SelectItem key="ar" value="ar">
Argentina
</SelectItem>
<SelectItem key="us" value="us">
United States
</SelectItem>
<SelectItem key="ca" value="ca">
Canada
</SelectItem>
<SelectItem key="uk" value="uk">
United Kingdom
</SelectItem>
<SelectItem key="au" value="au">
Australia
</SelectItem>
<SelectItem key="ar">Argentina</SelectItem>
<SelectItem key="us">United States</SelectItem>
<SelectItem key="ca">Canada</SelectItem>
<SelectItem key="uk">United Kingdom</SelectItem>
<SelectItem key="au">Australia</SelectItem>
</Select>
<Checkbox

View File

@ -45,10 +45,8 @@ export default function App() {
maxListboxHeight={400}
placeholder="Select..."
>
{items.map((item, index) => (
<SelectItem key={index} value={item.value}>
{item.label}
</SelectItem>
{items.map((item) => (
<SelectItem key={item.value}>{item.label}</SelectItem>
))}
</Select>
</div>

View File

@ -44,10 +44,8 @@ export default function App() {
maxListboxHeight={400}
placeholder="Select..."
>
{items.map((item, index) => (
<SelectItem key={index} value={item.value}>
{item.label}
</SelectItem>
{items.map((item) => (
<SelectItem key={item.value}>{item.label}</SelectItem>
))}
</Select>
</div>

View File

@ -44,9 +44,7 @@ export default function App() {
placeholder="Select..."
>
{items.map((item, index) => (
<SelectItem key={index} value={item.value}>
{item.label}
</SelectItem>
<SelectItem key={index}>{item.label}</SelectItem>
))}
</Select>
</div>

View File

@ -43,10 +43,8 @@ export default function App() {
label={"Select from 1000 items"}
placeholder="Select..."
>
{items.map((item, index) => (
<SelectItem key={index} value={item.value}>
{item.label}
</SelectItem>
{items.map((item) => (
<SelectItem key={item.value}>{item.label}</SelectItem>
))}
</Select>
</div>

View File

@ -295,6 +295,13 @@ the popover and listbox components.
<CodeDemo title="Custom Styles" files={selectContent.customStyles} />
### Using `value` attribute in option
The [`value`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option#value) attribute is not directly supported in `SelectItem`. Instead, the `key` property should be used to set the `value` to be submitted in forms.
If you need to submit a specific `value` instead of the `key` during form submission, consider implementing a lookup map in your application.
## Slots
- **base**: The main wrapper of the select. This wraps the rest of the slots.

View File

@ -67,15 +67,9 @@ const ControlledAutocomplete = <T extends object>(props: AutocompleteProps<T>) =
const AutocompleteExample = (props: Partial<AutocompleteProps> = {}) => (
<Autocomplete label="Favorite Animal" {...props}>
<AutocompleteItem key="penguin" value="penguin">
Penguin
</AutocompleteItem>
<AutocompleteItem key="zebra" value="zebra">
Zebra
</AutocompleteItem>
<AutocompleteItem key="shark" value="shark">
Shark
</AutocompleteItem>
<AutocompleteItem key="penguin">Penguin</AutocompleteItem>
<AutocompleteItem key="zebra">Zebra</AutocompleteItem>
<AutocompleteItem key="shark">Shark</AutocompleteItem>
</Autocomplete>
);
@ -102,15 +96,9 @@ describe("Autocomplete", () => {
render(
<Autocomplete ref={ref} aria-label="Favorite Animal" label="Favorite Animal">
<AutocompleteItem key="penguin" value="penguin">
Penguin
</AutocompleteItem>
<AutocompleteItem key="zebra" value="zebra">
Zebra
</AutocompleteItem>
<AutocompleteItem key="shark" value="shark">
Shark
</AutocompleteItem>
<AutocompleteItem key="penguin">Penguin</AutocompleteItem>
<AutocompleteItem key="zebra">Zebra</AutocompleteItem>
<AutocompleteItem key="shark">Shark</AutocompleteItem>
</Autocomplete>,
);
@ -131,17 +119,11 @@ describe("Autocomplete", () => {
const wrapper = render(
<Autocomplete aria-label="Favorite Animal" label="Favorite Animal">
<AutocompleteSection title="Birds">
<AutocompleteItem key="penguin" value="penguin">
Penguin
</AutocompleteItem>
<AutocompleteItem key="penguin">Penguin</AutocompleteItem>
</AutocompleteSection>
<AutocompleteSection title="Mammals">
<AutocompleteItem key="zebra" value="zebra">
Zebra
</AutocompleteItem>
<AutocompleteItem key="shark" value="shark">
Shark
</AutocompleteItem>
<AutocompleteItem key="zebra">Zebra</AutocompleteItem>
<AutocompleteItem key="shark">Shark</AutocompleteItem>
</AutocompleteSection>
</Autocomplete>,
);
@ -171,15 +153,9 @@ describe("Autocomplete", () => {
it("should focus when clicking autocomplete", async () => {
const wrapper = render(
<Autocomplete aria-label="Favorite Animal" data-testid="autocomplete" label="Favorite Animal">
<AutocompleteItem key="penguin" value="penguin">
Penguin
</AutocompleteItem>
<AutocompleteItem key="zebra" value="zebra">
Zebra
</AutocompleteItem>
<AutocompleteItem key="shark" value="shark">
Shark
</AutocompleteItem>
<AutocompleteItem key="penguin">Penguin</AutocompleteItem>
<AutocompleteItem key="zebra">Zebra</AutocompleteItem>
<AutocompleteItem key="shark">Shark</AutocompleteItem>
</Autocomplete>,
);
@ -198,15 +174,9 @@ describe("Autocomplete", () => {
it("should clear value after clicking clear button", async () => {
const wrapper = render(
<Autocomplete aria-label="Favorite Animal" data-testid="autocomplete" label="Favorite Animal">
<AutocompleteItem key="penguin" value="penguin">
Penguin
</AutocompleteItem>
<AutocompleteItem key="zebra" value="zebra">
Zebra
</AutocompleteItem>
<AutocompleteItem key="shark" value="shark">
Shark
</AutocompleteItem>
<AutocompleteItem key="penguin">Penguin</AutocompleteItem>
<AutocompleteItem key="zebra">Zebra</AutocompleteItem>
<AutocompleteItem key="shark">Shark</AutocompleteItem>
</Autocomplete>,
);
@ -244,15 +214,9 @@ describe("Autocomplete", () => {
it("should clear arbitrary value after clicking clear button", async () => {
const wrapper = render(
<Autocomplete aria-label="Favorite Animal" data-testid="autocomplete" label="Favorite Animal">
<AutocompleteItem key="penguin" value="penguin">
Penguin
</AutocompleteItem>
<AutocompleteItem key="zebra" value="zebra">
Zebra
</AutocompleteItem>
<AutocompleteItem key="shark" value="shark">
Shark
</AutocompleteItem>
<AutocompleteItem key="penguin">Penguin</AutocompleteItem>
<AutocompleteItem key="zebra">Zebra</AutocompleteItem>
<AutocompleteItem key="shark">Shark</AutocompleteItem>
</Autocomplete>,
);
@ -287,15 +251,9 @@ describe("Autocomplete", () => {
it("should keep the ListBox open after clicking clear button", async () => {
const wrapper = render(
<Autocomplete aria-label="Favorite Animal" data-testid="autocomplete" label="Favorite Animal">
<AutocompleteItem key="penguin" value="penguin">
Penguin
</AutocompleteItem>
<AutocompleteItem key="zebra" value="zebra">
Zebra
</AutocompleteItem>
<AutocompleteItem key="shark" value="shark">
Shark
</AutocompleteItem>
<AutocompleteItem key="penguin">Penguin</AutocompleteItem>
<AutocompleteItem key="zebra">Zebra</AutocompleteItem>
<AutocompleteItem key="shark">Shark</AutocompleteItem>
</Autocomplete>,
);
@ -403,15 +361,9 @@ describe("Autocomplete", () => {
it("should open and close listbox by clicking selector button", async () => {
const wrapper = render(
<Autocomplete aria-label="Favorite Animal" data-testid="autocomplete" label="Favorite Animal">
<AutocompleteItem key="penguin" value="penguin">
Penguin
</AutocompleteItem>
<AutocompleteItem key="zebra" value="zebra">
Zebra
</AutocompleteItem>
<AutocompleteItem key="shark" value="shark">
Shark
</AutocompleteItem>
<AutocompleteItem key="penguin">Penguin</AutocompleteItem>
<AutocompleteItem key="zebra">Zebra</AutocompleteItem>
<AutocompleteItem key="shark">Shark</AutocompleteItem>
</Autocomplete>,
);
@ -451,15 +403,9 @@ describe("Autocomplete", () => {
data-testid="close-when-clicking-outside-test"
label="Favorite Animal"
>
<AutocompleteItem key="penguin" value="penguin">
Penguin
</AutocompleteItem>
<AutocompleteItem key="zebra" value="zebra">
Zebra
</AutocompleteItem>
<AutocompleteItem key="shark" value="shark">
Shark
</AutocompleteItem>
<AutocompleteItem key="penguin">Penguin</AutocompleteItem>
<AutocompleteItem key="zebra">Zebra</AutocompleteItem>
<AutocompleteItem key="shark">Shark</AutocompleteItem>
</Autocomplete>,
);
@ -492,15 +438,9 @@ describe("Autocomplete", () => {
data-testid="close-when-clicking-outside-test"
label="Favorite Animal"
>
<AutocompleteItem key="penguin" value="penguin">
Penguin
</AutocompleteItem>
<AutocompleteItem key="zebra" value="zebra">
Zebra
</AutocompleteItem>
<AutocompleteItem key="shark" value="shark">
Shark
</AutocompleteItem>
<AutocompleteItem key="penguin">Penguin</AutocompleteItem>
<AutocompleteItem key="zebra">Zebra</AutocompleteItem>
<AutocompleteItem key="shark">Shark</AutocompleteItem>
</Autocomplete>
</ModalBody>
<ModalFooter>Modal footer</ModalFooter>
@ -527,15 +467,9 @@ describe("Autocomplete", () => {
it("should set the input after selection", async () => {
const wrapper = render(
<Autocomplete aria-label="Favorite Animal" data-testid="autocomplete" label="Favorite Animal">
<AutocompleteItem key="penguin" value="penguin">
Penguin
</AutocompleteItem>
<AutocompleteItem key="zebra" value="zebra">
Zebra
</AutocompleteItem>
<AutocompleteItem key="shark" value="shark">
Shark
</AutocompleteItem>
<AutocompleteItem key="penguin">Penguin</AutocompleteItem>
<AutocompleteItem key="zebra">Zebra</AutocompleteItem>
<AutocompleteItem key="shark">Shark</AutocompleteItem>
</Autocomplete>,
);
@ -569,30 +503,18 @@ describe("Autocomplete", () => {
data-testid="autocomplete"
label="Favorite Animal"
>
<AutocompleteItem key="penguin" value="penguin">
Penguin
</AutocompleteItem>
<AutocompleteItem key="zebra" value="zebra">
Zebra
</AutocompleteItem>
<AutocompleteItem key="shark" value="shark">
Shark
</AutocompleteItem>
<AutocompleteItem key="penguin">Penguin</AutocompleteItem>
<AutocompleteItem key="zebra">Zebra</AutocompleteItem>
<AutocompleteItem key="shark">Shark</AutocompleteItem>
</Autocomplete>
<Autocomplete
aria-label="Favorite Animal"
data-testid="autocomplete2"
label="Favorite Animal"
>
<AutocompleteItem key="penguin" value="penguin">
Penguin
</AutocompleteItem>
<AutocompleteItem key="zebra" value="zebra">
Zebra
</AutocompleteItem>
<AutocompleteItem key="shark" value="shark">
Shark
</AutocompleteItem>
<AutocompleteItem key="penguin">Penguin</AutocompleteItem>
<AutocompleteItem key="zebra">Zebra</AutocompleteItem>
<AutocompleteItem key="shark">Shark</AutocompleteItem>
</Autocomplete>
</>,
);

View File

@ -89,9 +89,7 @@ const defaultProps = {
};
const items = animalsData.map((item) => (
<AutocompleteItem key={item.value} value={item.value}>
{item.label}
</AutocompleteItem>
<AutocompleteItem key={item.value}>{item.label}</AutocompleteItem>
));
interface LargeDatasetSchema {
@ -138,9 +136,7 @@ const LargeDatasetTemplate = (args: AutocompleteProps & {numItems: number}) => {
return (
<Autocomplete label={`Search from ${args.numItems} items`} {...args}>
{largeDataset.map((item, index) => (
<AutocompleteItem key={index} value={item.value}>
{item.label}
</AutocompleteItem>
<AutocompleteItem key={index}>{item.label}</AutocompleteItem>
))}
</Autocomplete>
);

View File

@ -103,7 +103,7 @@ export type ListboxItemBaseProps<T extends object = {}> = Omit<Props<T>, "onClic
};
const ListboxItemBase = BaseItem as <T extends object>(
props: ListboxItemBaseProps<T>,
props: Omit<ListboxItemBaseProps<T>, "value">,
) => JSX.Element;
export default ListboxItemBase;

View File

@ -725,9 +725,7 @@ const LargeDatasetTemplate = (args: ListboxProps & {numItems: number}) => {
<div className="flex w-full max-w-full py-20 px-20">
<Listbox label={`Select from ${args.numItems} items`} {...args}>
{largeDataset.map((item, index) => (
<ListboxItem key={index} value={item.value}>
{item.label}
</ListboxItem>
<ListboxItem key={index}>{item.label}</ListboxItem>
))}
</Listbox>
</div>

View File

@ -526,15 +526,9 @@ describe("Select", () => {
data-testid="select"
label="Favorite Animal"
>
<SelectItem key="penguin" value="penguin">
Penguin
</SelectItem>
<SelectItem key="zebra" value="zebra">
Zebra
</SelectItem>
<SelectItem key="shark" value="shark">
Shark
</SelectItem>
<SelectItem key="penguin">Penguin</SelectItem>
<SelectItem key="zebra">Zebra</SelectItem>
<SelectItem key="shark">Shark</SelectItem>
</Select>
<Select
disableAnimation
@ -542,15 +536,9 @@ describe("Select", () => {
data-testid="select2"
label="Favorite Animal"
>
<SelectItem key="penguin" value="penguin">
Penguin
</SelectItem>
<SelectItem key="zebra" value="zebra">
Zebra
</SelectItem>
<SelectItem key="shark" value="shark">
Shark
</SelectItem>
<SelectItem key="penguin">Penguin</SelectItem>
<SelectItem key="zebra">Zebra</SelectItem>
<SelectItem key="shark">Shark</SelectItem>
</Select>
</>,
);
@ -688,15 +676,9 @@ describe("Select", () => {
data-testid="select"
label="Favorite Animal"
>
<SelectItem key="penguin" value="penguin">
Penguin
</SelectItem>
<SelectItem key="zebra" value="zebra">
Zebra
</SelectItem>
<SelectItem key="shark" value="shark">
Shark
</SelectItem>
<SelectItem key="penguin">Penguin</SelectItem>
<SelectItem key="zebra">Zebra</SelectItem>
<SelectItem key="shark">Shark</SelectItem>
</Select>,
);
@ -736,9 +718,7 @@ describe("Select", () => {
onChange={onChange}
>
{options.map((o) => (
<SelectItem key={o} value={o}>
{o}
</SelectItem>
<SelectItem key={o}>{o}</SelectItem>
))}
</Select>,
);
@ -775,9 +755,7 @@ describe("Select", () => {
onChange={onChange}
>
{options.map((o) => (
<SelectItem key={o} value={o}>
{o}
</SelectItem>
<SelectItem key={o}>{o}</SelectItem>
))}
</Select>,
);
@ -808,15 +786,9 @@ describe("Select", () => {
labelPlacement="outside"
placeholder="placeholder"
>
<SelectItem key="penguin" value="penguin">
Penguin
</SelectItem>
<SelectItem key="zebra" value="zebra">
Zebra
</SelectItem>
<SelectItem key="shark" value="shark">
Shark
</SelectItem>
<SelectItem key="penguin">Penguin</SelectItem>
<SelectItem key="zebra">Zebra</SelectItem>
<SelectItem key="shark">Shark</SelectItem>
</Select>,
);
@ -838,15 +810,9 @@ describe("Select", () => {
label={labelContent}
placeholder="placeholder"
>
<SelectItem key="penguin" value="penguin">
Penguin
</SelectItem>
<SelectItem key="zebra" value="zebra">
Zebra
</SelectItem>
<SelectItem key="shark" value="shark">
Shark
</SelectItem>
<SelectItem key="penguin">Penguin</SelectItem>
<SelectItem key="zebra">Zebra</SelectItem>
<SelectItem key="shark">Shark</SelectItem>
</Select>,
);
@ -972,9 +938,7 @@ describe("Select virtualization tests", () => {
onChange={onChange}
>
{options.map((o) => (
<SelectItem key={o} value={o}>
{o}
</SelectItem>
<SelectItem key={o}>{o}</SelectItem>
))}
</Select>
</div>,
@ -1007,9 +971,7 @@ describe("Select virtualization tests", () => {
onChange={onChange}
>
{options.map((o) => (
<SelectItem key={o} value={o}>
{o}
</SelectItem>
<SelectItem key={o}>{o}</SelectItem>
))}
</Select>
</div>,
@ -1060,9 +1022,7 @@ describe("Select virtualization tests", () => {
onChange={onChange}
>
{options.map((o) => (
<SelectItem key={o} value={o}>
{o}
</SelectItem>
<SelectItem key={o}>{o}</SelectItem>
))}
</Select>
</div>,
@ -1115,9 +1075,7 @@ describe("Select virtualization tests", () => {
onChange={onChange}
>
{options.map((o) => (
<SelectItem key={o} value={o}>
{o}
</SelectItem>
<SelectItem key={o}>{o}</SelectItem>
))}
</Select>
</div>,

View File

@ -69,11 +69,7 @@ const defaultProps = {
...select.defaultVariants,
};
const items = animalsData.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
));
const items = animalsData.map((item) => <SelectItem key={item.value}>{item.label}</SelectItem>);
const Template = ({color, variant, ...args}: SelectProps) => (
<Select className="max-w-xs" color={color} label="Favorite Animal" variant={variant} {...args}>
@ -885,10 +881,8 @@ const LargeDatasetTemplate = (args: SelectProps & {numItems: number}) => {
return (
<div className="flex w-full max-w-full py-20 xl:px-32 lg:px-20 px-20">
<Select label={`Select from ${args.numItems} items`} {...args}>
{largeDataset.map((item, index) => (
<SelectItem key={index} value={item.value}>
{item.label}
</SelectItem>
{largeDataset.map((item) => (
<SelectItem key={item.value}>{item.label}</SelectItem>
))}
</Select>
</div>