payload icon indicating copy to clipboard operation
payload copied to clipboard

Radio buttons and checkboxes in drawer broken (and instead triggering other change) if a field with same name already exists in main document (or other drawer below)

Open CherryDT opened this issue 1 year ago • 1 comments

Link to reproduction

No response

Describe the Bug

Due to duplicate IDs being used in the HTML, the <label> tag of a radio button or a checkbox will do the wrong thing when you have a drawer in which you are editing a document that has a radio field with the same name as the document in the main editing screen (or another drawer below). It will appear to be broken and instead change the field in the main document which the user doesn't even notice!

To Reproduce

  1. Create a collection with a radio field A/B and a relation to its own collection (for simplicity - it would also happen with other collections as long as there is also a radio field with the same name).
  2. Create an item in the collection.
  3. Set radio button to A.
  4. In the relation field, click the "+" to create a second item to connect to the first.
  5. In the drawer, attempt to set the radio button B. Notice it doesn't change.
  6. Close the drawer again. Notice the radio field in the main document changed from A to B instead.

Payload Version

2.14.2

Adapters and Plugins

db-mongodb, bundler-webpack

CherryDT avatar Apr 30 '24 05:04 CherryDT

My current workaround:

// Hotfix for https://github.com/payloadcms/payload/issues/6131, ensure radio and checkbox buttons work correctly despite duplicate IDs
window.addEventListener('click', (event) => {
  // If a <label> gets clicked with an ID and the ID is not unique but the label *contains* an element with that ID (or has a certain type of sibling), click that element instead and make sure the default action is prevented
  const label = event.target.closest('label')
  if (label?.htmlFor) {
    const innerTarget = label.querySelector(`[id="${label.htmlFor}"]`) ?? label.closest('.checkbox-input')?.querySelector(`[id="${label.htmlFor}"]`)
    if (innerTarget && innerTarget !== document.getElementById(label.htmlFor)) {
      event.preventDefault()
      innerTarget.click()
    }
  }
})

I think the real solution would be to simply get rid of the for and id attributes for such labels entirely and make sure the <input> is always contained in the <label>. For radio buttons that's already the case, for checkboxes the markup would need to be rearranged slightly.

CherryDT avatar Apr 30 '24 05:04 CherryDT

This issue has been automatically locked. Please open a new issue if this issue persists with any additional detail.

github-actions[bot] avatar Sep 07 '24 04:09 github-actions[bot]