primitives icon indicating copy to clipboard operation
primitives copied to clipboard

[Select] Not inheriting `disabled` from parent fieldset

Open piotrkulpinski opened this issue 2 years ago • 6 comments
trafficstars

Bug report

Hi, I just noticed a small, but important issue with Select Primitive. If you wrap it with a <fieldset disabled></fieldset> it'll not inherit the disabled attribute. This works like this with every other "form" primitive (radio group, switch, checkbox etc) so I'd also think it should be the same for Select.

Current Behavior

This works:

<Select.Root disabled>
  (...)
</Select.Root>

This doesn't:

<fieldset disabled>
  <Select.Root>
    (...)
  </Select.Root>
</fieldset>

Expected behavior

A select root to inherit disabled attribute from parent fieldset.

piotrkulpinski avatar Sep 27 '23 12:09 piotrkulpinski

I should also add, that the underlying element gets marked as disabled properly, but you still can open the dialog and select values.

piotrkulpinski avatar Sep 27 '23 12:09 piotrkulpinski

You can use css to remove the pointer events when the object is disabled, im using tailwindcss but you can do it with vanilla as well

<RadixSelect.Trigger className='disabled:pointer-events-none'>

javiaggomezz avatar Sep 29 '23 10:09 javiaggomezz

You can use css to remove the pointer events when the object is disabled, im using tailwindcss but you can do it with vanilla as well

<RadixSelect.Trigger className='disabled:pointer-events-none'>

  • The field is still keyboard accessible
  • cursor: not-allowed doesn't work

glocore avatar Nov 06 '23 15:11 glocore

I'm also running into this issue. I'm using Shadcn, which uses Radix's select implementation. One workaround is to keep track of whether the select is within a disabled fieldset or not. The disabled attribute can then be added to the button element, which will then behave as being properly disabled.

const ref = useRef<HTMLButtonElement>(null);
const isInDisabledFieldset = Boolean(ref.current?.closest("fieldset:disabled"));

return (
    <Select.Root>
        <Select.Trigger ref={ref} disabled={isInDisabledFieldset}>
        </Select.Trigger>
    </Select.Root>
);

BrendanC23 avatar Mar 07 '24 16:03 BrendanC23

I used @BrendanC23's great idea, but had to put the disabled prop on the Select.Root, still with the ref on the Select.Trigger, in order to make it work.

const ref = useRef<HTMLButtonElement>(null);
const isInDisabledFieldset = Boolean(ref.current?.closest("fieldset:disabled"));

return (
    <Select.Root disabled={isInDisabledFieldset}>
        <Select.Trigger ref={ref}>
        </Select.Trigger>
    </Select.Root>
);

kluplau avatar Aug 23 '24 08:08 kluplau

#3174

I opened the PR for solving this issue! Check it out!

ojj1123 avatar Oct 10 '24 05:10 ojj1123

This doesn't seem to be limited to just Selects. A disabled fieldset should disable all descendant form controls, which includes <button>s in general:

<fieldset disabled>
    <button onClick={...}>Btn</button> {/* ✅ Native behavior: Can't be interacted with */}
</fieldset>

But e.g. DropdownMenuTriggers aren't properly disabled (just like SelectTrigger):

<fieldset disabled>
    <DropdownMenu.Root>
        <DropdownMenu.Trigger asChild>
             <button>Trigger</button> {/* ❌ Can be clicked to open the dropdown */}
        </DropdownMenu.Trigger>
        <DropdownMenu.Portal>
            <DropdownMenu.Content>...</DropdownMenu.Content>
        </DropdownMenu.Portal>
    </DropdownMenu.Root>
</fieldset>

But CollapsibleTriggers are disabled for some reason:

<fieldset disabled>
    <Collapsible.Root>
        <Collapsible.Trigger asChild>
            <button>Trigger</button> {/* ✅ Can't be interacted with */}
        </Collapsible.Trigger>
        <Collapsible.Content>...</Collapsible.Content>
    </Collapsible.Root>
</fieldset>

I haven't checked other trigger components, I just noticed the disparity between these two.

janruo avatar Feb 21 '25 13:02 janruo