radix-ui / themes

Radix Themes is an open-source component library optimized for fast development, easy maintenance, and accessibility. Maintained by @workos.
https://radix-ui.com/themes
MIT License
5.76k stars 208 forks source link

Cannot Insert Spaces in Dialogs input which are children of a DropdownItem Component. #342

Closed AbhinasRegmi closed 9 months ago

AbhinasRegmi commented 9 months ago

The Dropdown component will close when a item is clicked so i prevented the closing action on click and instead used the click as trigger to open the dropdown but the input fields inside the dialog doesn't show white spaces when typing. How do i fix this?. What am i doing wrong here. Is it a bad idea to do it this way?

vladmoroz commented 9 months ago

Are you sure the whitespace issue is related to everything else you mentioned? Could you do a minimal reproduction of your issue?

AbhinasRegmi commented 9 months ago

I used the same dialog component outside of the dropdown and i was able to insert white spaces.

AbhinasRegmi commented 9 months ago

I am using shadcnui here. Does that make any difference? This is the dialog component which is unable to show white spaces during typing placeholders seem to work.

export function Summary(props: {children: React.ReactNode, storyID: string, token: string}) {
    let [sum, setSum] = useState('');
    let [open, setOpen] = useState(false);

    let mutation = useMutation({
        mutationFn: updateStorySummary
    })

    function handler(){
        if(sum){
            mutation.mutate({token: props.token, storyID: props.storyID, summary: sum});
            setOpen(false);
        }
    }

  return (
    <Dialog open={open} onOpenChange={setOpen}>
      <DialogTrigger asChild>
        {props.children}
      </DialogTrigger>
      <DialogContent className="sm:max-w-[425px]">
        <DialogHeader>
          <DialogTitle>Add summary</DialogTitle>
          <DialogDescription>
            Make changes to summary for you story here. Click save when you're done.
          </DialogDescription>
        </DialogHeader>
        <div className="grid gap-4 py-4">
          <div className="flex-col">
            <Label htmlFor="summary" className="text-right">
              Summary
            </Label>
            <Textarea
              id="summary"
              placeholder="Enter your summary here..."
              className="col-span-3"
              value={sum}
              onChange={e=>{if(e.target.value) setSum(e.target.value);}}
            />
          </div>
        </div>
        <DialogFooter>
          <Button type="submit" onClick={handler}>Save changes</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

This is my dialog which is used inside the dropdown as

                <DropdownMenuTrigger asChild>
                    <Button variant="ghost" size="sm">
                        <MoreHorizontal />
                    </Button>
                </DropdownMenuTrigger>
                <DropdownMenuContent align="end" className="w-[200px]">
                    <DropdownMenuLabel>Actions</DropdownMenuLabel>
                    <DropdownMenuGroup>
                        <DropdownMenuItem onClick={() => { navigate(`/new-story?id=${props.data.key}`) }}>
                            <Pencil className="mr-2 w-4 h-4" />
                            Edit now
                        </DropdownMenuItem>

                        <DropdownMenuItem onClick={e=>{e.preventDefault();}}>
                            <Summary storyID={props.data.key} token={token}>
                                <div className='flex items-center w-full'>
                                    <ArrowUpZA className="mr-2 w-4 h-4" />
                                    Add Summary
                                </div>
                            </Summary>
                        </DropdownMenuItem>
                        <DropdownMenuItem>
                            <ImageUp className='mr-2 w-4 h-4' />
                            Add Cover
                        </DropdownMenuItem>
                        <DropdownMenuItem onClick={() => { umutation.mutate({ token: token, storyID: props.data.key, isPublished: true }); }}>
                            <Rocket className="mr-2 h-4 w-4" />
                            Publish now
                        </DropdownMenuItem>
                        <DropdownMenuSeparator />
                        <DropdownMenuItem className="text-red-600" onClick={e => e.preventDefault()}>
                            <Delete content='saved draft.' fn={() => { dmutation.mutate({ token: token, storyID: props.data.key }) }}>
                                <div className='flex items-center w-full'>
                                    <Trash className="mr-2 h-4 w-4" />
                                    Delete
                                </div>
                            </Delete>
                        </DropdownMenuItem>
                    </DropdownMenuGroup>
                </DropdownMenuContent>
            </DropdownMenu>
vladmoroz commented 9 months ago

Can you get it onto a site like CodeSandbox? We won't be able to help with this looking just at the code

AbhinasRegmi commented 9 months ago

This is the link for the sandbox. When you click the 3 dots and select Add Summary you get a dialog with input field but cannot insert white spaces.

sandbox

Noticed another behaviour in the sandbox cannot backspace the last single character in the input field.

vladmoroz commented 9 months ago

I see, you get this behaviour because you do <DropdownMenuItem onClick={e => { e.preventDefault(); }}> which catches space key presses from the dialog.

Instead of keeping the dialog open by preventing the default for all clicks within the menu item, you want to lift the state up and control the dialog from that component, e.g.:

<DropdownMenuItem onSelect={() => setSummaryOpen(true)}>
// Rest of the dropdown menu

// Render the controlled dialog outside of the dropdown menu
<Summary open={summaryOpen} onOpenChange={setSummaryOpen} />
AbhinasRegmi commented 9 months ago

Thank you for your response but i found a easier fix. I just had to stop event propagation on the input fields for keyDown like this

<Dialog open={open} onOpenChange={setOpen}>
      <DialogTrigger asChild>
        {props.children}
      </DialogTrigger>
      <DialogContent className="sm:max-w-[425px]">
        <DialogHeader>
          <DialogTitle>Add summary</DialogTitle>
          <DialogDescription>
            Make changes to summary for you story here. Click save when you're done.
          </DialogDescription>
        </DialogHeader>
        <div className="grid gap-4 py-4" onKeyDown={e => e.stopPropagation()}>
          <div className="flex-col">
            <Label htmlFor="summary" className="text-right">
              Summary
            </Label>
            <Textarea
              id="summary"
              placeholder="Enter your summary here..."
              className="col-span-3"
              value={sum}
              onChange={e => {
                setSum(e.target.value)
              }}
            />
          </div>
        </div>
        <DialogFooter>
          <Button type="submit" onClick={handler}>Save changes</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>

By doing this, I believe the Dropdown menu won't know when keyBoard is used inside the form and won't trigger any default behaviours.

vladmoroz commented 9 months ago

That's the wrong way to go about it to be honest, as you are still going to prevent default on all clicks inside the dialog

AbhinasRegmi commented 9 months ago

I tried to lift up the state of dialog in the dropdown but when i clicked the trigger for dialog the dialog opened promptly and closed which i believe is the default behaviour as dropdown closes on clicks on DropDownItems. So i prevented closing of the dropdown using this for single dropdownItem only

<DropdownMenuItem onSelect={e=>e.preventDefault()}>

Now the dropdown doesn't close when i click the trigger for dialog and again the keyDown events for spaces were not possible inside dropdown so i couldn't write spaces inside form which are children of DropDownItem so i stopped the keydown propagation for the input field only and i was able to type spaces.

Is there other method to solve this? I am lost here.

vladmoroz commented 9 months ago

I tried to lift up the state of dialog in the dropdown but when i clicked the trigger for dialog the dialog opened promptly and closed which i believe is the default behaviour as dropdown closes on clicks on DropDownItems. So i prevented closing of the dropdown using this for single dropdownItem only

This is because you were still rendering the dialog within the dropdown menu. When the dropdown menu closes, it unmounts. You just need to render the dialog outside of the menu, and have the menu item toggle the open state of the dialog.

I updated your sandbox example to show how:

https://codesandbox.io/p/github/AbhinasRegmi/sandbox/csb-vzqs7h/draft/mystifying-dew?file=%2Fsrc%2Fli.tsx%3A68%2C19

AbhinasRegmi commented 9 months ago

thanks a lot.