Skip to content

Radio Group render props problem when use dangerouslySetInnerHTML #3652

@mjr

Description

@mjr

Describe the Bug

When using Radio Group with dangerouslySetInnerHTML have a different behavior from render props and data attributes, is necessary 2 click to change the option when using render props, the problem just happens if click inside the component that have the dangerouslySetInnerHTML.

Link to the code that reproduces this issue:

https://github.com/mjr/headlessui-repro-1/blob/main/app/components.js

To Reproduce

Go to https://headlessui-repro-1.vercel.app
Click on text options from left side.
Click on text options from right side:

Using data attributes

function ExampleDataAttributes() {
  const [selected, setSelected] = useState(plans[0])

  return (
    <fieldset aria-label="Server size">
      <RadioGroup value={selected} onChange={setSelected} className="space-y-4">
        {plans.map((plan) => (
          <Radio
            key={plan.id}
            value={plan}
            className="group relative block cursor-pointer rounded-lg border border-gray-300 bg-white px-6 py-4 shadow-sm focus:outline-none data-[focus]:border-indigo-600 data-[focus]:ring-2 data-[focus]:ring-indigo-600 sm:flex sm:justify-between"
          >
            <span className="flex size-5 items-center justify-center rounded-full border bg-white group-data-[checked]:bg-blue-400">
              <span className="invisible size-2 rounded-full bg-white group-data-[checked]:visible" />
            </span>

            <div>
              <p>{plan.label}</p>
            </div>

            <div dangerouslySetInnerHTML={{ __html: plan.content }} />
            {/* <div>{plan.content}</div> */}

            <div>
              <button type="button">Ver</button>
            </div>

            <span
              aria-hidden="true"
              className="pointer-events-none absolute -inset-px rounded-lg border-2 border-transparent group-data-[focus]:border group-data-[checked]:border-indigo-600"
            />
          </Radio>
        ))}
      </RadioGroup>
    </fieldset>
  )
}

Using render props

function ExampleRenderProps() {
  const [selected, setSelected] = useState(plans[0])

  return (
    <fieldset aria-label="Server size">
      <RadioGroup value={selected} onChange={setSelected} className="space-y-4">
        {plans.map((plan) => (
          <Radio key={plan.id} as={Fragment} value={plan}>
            {({ checked, disabled }) => (
              <span className="group relative block cursor-pointer rounded-lg border border-gray-300 bg-white px-6 py-4 shadow-sm focus:outline-none data-[focus]:border-indigo-600 data-[focus]:ring-2 data-[focus]:ring-indigo-600 sm:flex sm:justify-between">
                <span className="flex size-5 items-center justify-center rounded-full border bg-white group-data-[checked]:bg-blue-400">
                  <span className="invisible size-2 rounded-full bg-white group-data-[checked]:visible" />
                </span>

                <div>
                  <p>{plan.label}</p>
                </div>

                <div dangerouslySetInnerHTML={{ __html: plan.content }} />
                {/* <div>{plan.content}</div> */}

                <div>
                  <button type="button">Ver</button>
                </div>

                <span
                  aria-hidden="true"
                  className="pointer-events-none absolute -inset-px rounded-lg border-2 border-transparent group-data-[focus]:border group-data-[checked]:border-indigo-600"
                />
              </span>
            )}
          </Radio>
        ))}
      </RadioGroup>
    </fieldset>
  )
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions