Open jwueller opened 9 months ago
I'm happy to provide a PR if there is a consensus on how to address this.
Having the same issue.
chiming in. This constrain does not really make sense to me. When i add a select item with an empty value its exactly the point to reset it back to the placeholder. 🫠
chiming in. This constrain does not really make sense to me. When i add a select item with an empty value its exactly the point to reset it back to the placeholder.
Totally agree with this. We end up coming up with a contrived string ID and using a custom change handler to deselect when it encounters that ID. For optional selects (those not requiring a value), a user ought to be able to unset their selection.
We are facing the same problem and are happy to hear a functioning solution that does not include the usage of a proxy value like "none".
This change unnecessarily bloats otherwise straightforward implementations. This breaks standards and will hurt adoption of RadixUI.
Consider the following example. Message
model has optional property templateId
. Empty string is nullish, so no extra transformations are required:
const templateId = Boolean(value) ? BigInt(value) : undefined;
This fits naturally with validation libraries like Yup and Zod. Even the W3C's <select>
tag example uses empty string for "no selection", and this is what I hate the most about this change.
I will sound rude, and I mean it. We got a whole bag of problems to deal with because one bright mind couldn't figure out how to set value to undefined
. Cool stuff
Have the same issue. In our case the value is directly used as a filter. So now we need to somehow workaround the fact that "none" is ""...
Facing the same Issue. Just like how native select allows to deselect the value being selected, Radix Select should also allow
This would be useful. It would be great if clicking an already-selected value would deselect it and set the value to ""
.
This would be useful. It would be great if clicking an already-selected value would deselect it and set the value to
""
.
The standard way to clear a <select/>
is to provide an <option/>
without a value (i.e. an empty string). This issue is about restoring that particular functionality.
What you're describing seems less like a direct <select/>
replacement and more like a drop-down version of a toggle group.
maybe a <Select.Clear>
component could help with this?
maybe a
<Select.Clear>
component could help with this?
I would like to avoid adding extra steps for standard functionality that should "just work"™. I still haven't seen a compelling reason for why this would be forbidden in the first place. It seem like a very arbitrary decision.
While it throws a type error, it appears that setting the value to null
has the same effect as previous setting the value to ""
, e.g:
<Select.Item value={null}>Clear</Select.Item>
I'm not sure if this is intended behavior or not though...
I am having this same struggle. The Mozilla docs for the select element shows a simple example of an empty value for a select option. The user can select an option and return to the placeholder state if they would like. I don't see why Radix does not support that simple scenario.
In my case, I need the user to be able to return to the disabled state. I am working on migrating to Radix from native HTML elements, and many parts of my codebase use the empty value. I could assign an actual value to the disabled state, but then I need to refactor my logic to repopulate the form and the logic to submit to my API. It looks like @linden-dg's solution will work, but it feels bad to cast to any to satisfy the string type value={null as any}
. It also comes with the small side effect of a missing checked icon.
value={null as any}
(Check missing)
value="encryption"
(Check working as normal)
Ideally my code would be this
<SelectInput name={inputs.PROCESSING_TYPE} title="Processing Type">
<SelectItem value="">Disabled</SelectItem>
<SelectItem value={processingTypes.ENCRYPTION}>Encryption</SelectItem>
<SelectItem value={processingTypes.DECRYPTION}>Decryption</SelectItem>
<SelectItem value={processingTypes.COMPRESSION}>Compression</SelectItem>
<SelectItem value={processingTypes.DECOMPRESSION}>Decompression</SelectItem>
</SelectInput>
Currently I am doing this
<SelectInput name={inputs.PROCESSING_TYPE} title="Processing Type" placeholder="Disabled">
<SelectItem value={null as any}>Disabled</SelectItem>
<SelectItem value={processingTypes.ENCRYPTION}>Encryption</SelectItem>
<SelectItem value={processingTypes.DECRYPTION}>Decryption</SelectItem>
<SelectItem value={processingTypes.COMPRESSION}>Compression</SelectItem>
<SelectItem value={processingTypes.DECOMPRESSION}>Decompression</SelectItem>
</SelectInput>
I agree that there should also be a way to deselect and return to a placeholder state.
This change unnecessarily bloats otherwise straightforward implementations. This breaks standards and will hurt adoption of RadixUI. Consider the following example.
Message
model has optional propertytemplateId
. Empty string is nullish, so no extra transformations are required:const templateId = Boolean(value) ? BigInt(value) : undefined;
This fits naturally with validation libraries like Yup and Zod. Even the W3C's
<select>
tag example uses empty string for "no selection", and this is what I hate the most about this change. I will sound rude, and I mean it. We got a whole bag of problems to deal with because one bright mind couldn't figure out how to set value toundefined
. Cool stuff
The browser API for getting FormData from a Form element is really annoying because undefined might work for POST if you then send that FormData over the wire, but it doesn't work for PATCH. Undefined means omitted from form data.
So if you actually want to take something was previously a truthy value and unset it, you have a problem. Everything is a string so there is no concept of null it would be "null" as a string. One solution is sending a value of empty string over the wire, it is then included in FormData, and your server using something like Zod can transform that literal empty string and only that literal empty string to null or whatever you need it to be. Allowing empty string in this scenario would be really useful.
Hi, guys! How are u?
I have the same problem and so much as you, I don't want to add unnecessary complexity to the code, but I need the “reset” feature anyway, so I do this:
// design-system/components/Select.tsx
<Select.Root
// Another non-related props...
/** @see https://github.com/radix-ui/primitives/issues/2706 */
onValueChange={_value => onValueChange?.(_value ?? '')}
>
{/* Another non-related components... */}
<Select.Item label="Select..." value={null as any}>
{/* Another non-related components... */}
</Select.Item>
</Select.Root>
The unnecessary complexity is isolated in the inside of the component and works perfectly to controlled fields. My use case to uncontrolled fields is uncommon, so for these cases, unfortunately we need to handle this in another way.
This is a struggle. I'm using react hook form and Zod . When I place a null value (because it won't accept ""), then submit, I get a zod error. Even though the field is optional and nullable, it sends '' (an empty string) when I set a value of null.
Even though the field is optional and nullable, it sends '' (an empty string) when I set a value of null.
This is because the value property of an <option/>
must be a string. Everything gets converted to a string:
let option = document.createElement('option');
// option.value => ""
option.value = null;
// option.value => "null"
So you can't even have a null
value in the first place.
I was able to use handleSelectChange
to allow setting the value to an empty string.
import { Select } from "@radix-ui/themes";
import { FC } from "react";
interface SelectOptionType {
name: string;
value: string;
disabled: boolean;
}
interface SelectFieldProps {
value: string;
onChange: (value: string) => void;
options: SelectOptionType[];
}
export const SelectField: FC<SelectFieldProps> = ({
value,
onChange,
options,
}) => {
const selectOptions: SelectOptionType[] = [
{
name: "None",
value: "none",
disabled: false,
},
...options,
];
const handleSelectChange = (v: string): void => {
if (v === "none") {
onChange("");
}
onChange(v);
};
return (
<Select.Root value={value} onValueChange={handleSelectChange}>
{selectOptions.map((option) => (
<Select.Item
key={option.value}
value={option.value}
disabled={option.disabled}
>
{option.name}
</Select.Item>
))}
</Select.Root>
);
};
@axelmukwena Thank you for sharing your approach. I think this is probably the best approach in the meantime.
However I say "meantime" because IMO this is absolutely needed within the base component, including support for null
and number
values.
Of course, normal <input>
elements don't handle that, but thaaaaaat's why we build React components around them: to extend and enhance.
well this works for me
<SelectItem value={null as unknown as string}>
All Items
</SelectItem>
I made a feature request to support non-string values:
Bug report
Current Behavior
As far as I can tell, there is no way for a user to reset a
<Select/>
to its original (empty) state, even if it is optional.Expected behavior
Ability to clear the currently selected value on optional
<Select/>
s.Reproducible example
Hero example on https://www.radix-ui.com/primitives/docs/components/select:
required
Suggested solution
Add an empty/placeholder record for optional
<Select/>
s, or at least allow adding one manually, like it was before #2174.Additional context
This seems to have been broken by #2174, which now actively forbids adding an empty item. It is now only possible to clear by adding additional control for unsetting a
<Select/>
.I believe it is an anti-pattern to not allow the user to undo an erroneous change they might have made. This control currently only allows a one-way change from the placeholder, as opposed to how it worked before.
Your environment
@radix-ui/react-select@2.0.0