i-like-robots / react-tag-autocomplete

⚛️ A simple, accessible, tagging component ready to drop into your React projects (new repo)
https://i-like-robots.github.io/react-tag-autocomplete/
ISC License
178 stars 12 forks source link

OnDelete Launched when object inside a <label> #60

Closed xavis closed 1 year ago

xavis commented 1 year ago

Expected behaviour

When component is inside a label it should work as usual.

Current behaviour

When component is inside a label it launched onDelete event on object addition and some weird behaviours.

Steps to Reproduce

Steps to reproduce the problem:

  1. Create a component wrapping the with a
  2. Make that component receive the selected, suggestions, onAdd, onDelete props from parent.
  3. Render the whole component and enjoy the bug.

Additional context

Maybe it's just me, I'm going to add my code below:

const TagPicker: FC<CustomTagPickerProps> = ({labelText, onUpdate, selectedTags, suggestions, ...rest}) => {
    const [component, setComponent] = useState<any>(null);

    return <>
        <label className={styles.Label}>{labelText}<br/>
           <ReactTags
              selected={selectedTags}
              suggestions={suggestions}
              onDelete={(i:number) => {
                  console.log("Delete");
                  const tags = selectedTags.slice(0);
                  tags.splice(i, 1);
                  onUpdate(tags);
              }}
              onAdd={(tag:Tag) => {
                  console.log("Add");
                  onUpdate([...selectedTags, tag]);
              }}
              labelText={labelText}
              allowNew={true}
              allowBackspace={false}
               />
        </label>
        </>
}

onUpdate method is a useState setter method from the parent while selectedTags is the useState value.

IMPORTANT INFO

I solve this issue by adding

onClick = { (e)=>{ e.preventDefault() } }

to the label tag. But I think there should be some issue with the component.

i-like-robots commented 1 year ago

Thank you for your report @xavis. Rendering a complex component such as this one within a label element is not valid HTML so this is not a use case I can support.

The HTML spec specififies that a label element may contain:

Phrasing content, but with no descendant labelable elements unless it is the element's labeled control, and no descendant label elements

This component is built with multiple flow content elements (e.g. div) and multiple labeleable elements (button, input) that should not be rendered inside a label.

I believe the behaviours you're experiencing are due to the native activation behaviour of label elements. What I expect you're seeing is that mouse events on the label element - hover, clicks, etc - are being fired as if the originating from the first interactive element rendered by this component. Here's an example given by the HTML spec:

on platforms where clicking a label activates the form control, clicking the label could trigger the user agent to fire a click event at the input element, as if the element itself had been triggered by the user.

I hope this helps.

xavis commented 1 year ago

Yes, that is exactly what happens. But I was surprised about the launch of onDelete method.

Just wanted to warn about that behaviour, and mainly share the quick fix:

if anyone find the same issue would be to add e.stopPropagation() on label onClick event.

i-like-robots commented 1 year ago

I was surprised about the launch of onDelete method

Hopefully this now makes sense after the explanation given above, the native activation behaviour of the label will target the first labeleable child - in your case a selected tag. I'll reiterate that this is not valid HTML so the behaviour you and your users experience may not be predictable, especially because the activation behaviour of labels is left to the implementor and may vary between browsers, platforms and devices.