primitives icon indicating copy to clipboard operation
primitives copied to clipboard

User is no longer able to reset optional `<Select/>` value

Open jwueller opened this issue 1 year ago • 28 comments

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:

  1. Select an entry
  2. Unable to clear the selection even though it is not 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/[email protected]

jwueller avatar Feb 15 '24 17:02 jwueller

I'm happy to provide a PR if there is a consensus on how to address this.

jwueller avatar Feb 15 '24 17:02 jwueller

Having the same issue.

juancdominici avatar Feb 28 '24 21:02 juancdominici

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. 🫠 image

maerzhase avatar May 13 '24 09:05 maerzhase

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.

psirenny avatar May 13 '24 09:05 psirenny

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".

Blaekbit avatar May 15 '24 16:05 Blaekbit

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

Nekroido avatar May 31 '24 10:05 Nekroido

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 ""...

anatolii-yemets avatar Jun 12 '24 12:06 anatolii-yemets

Facing the same Issue. Just like how native select allows to deselect the value being selected, Radix Select should also allow

AayushKarki714 avatar Jun 12 '24 16:06 AayushKarki714

This would be useful. It would be great if clicking an already-selected value would deselect it and set the value to "".

BrendanC23 avatar Jun 13 '24 15:06 BrendanC23

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.

jwueller avatar Jun 13 '24 20:06 jwueller

maybe a <Select.Clear> component could help with this?

ysulyma avatar Jun 17 '24 14:06 ysulyma

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.

jwueller avatar Jun 19 '24 14:06 jwueller

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...

linden-dg avatar Jul 12 '24 02:07 linden-dg

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) image value="encryption" (Check working as normal) image

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>

derekbking avatar Jul 24 '24 20:07 derekbking

I agree that there should also be a way to deselect and return to a placeholder state.

berkerdemirer avatar Jul 30 '24 12:07 berkerdemirer

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

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.

Develliot avatar Aug 09 '24 16:08 Develliot