Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow html for options dropdown #51

Open
silviurosu opened this issue Dec 22, 2023 · 5 comments
Open

Allow html for options dropdown #51

silviurosu opened this issue Dec 22, 2023 · 5 comments

Comments

@silviurosu
Copy link

I have a usecase for options to contain more than a text value. I want to be able to display an html I creating with label and sublabel.
Screenshot 2023-12-22 at 10 53 53

I was able to display my own html sending the options like this:

  s = """
         <span>
          #{city}</br>
          <small>#{state}</small>
        </span>
        """
        {raw(s), city}

but there is a problem when selecting the option from dropdown. It tries to serialize the label as json and I get a crash:

[error] GenServer #PID<0.1414.0> terminating
** (Protocol.UndefinedError) protocol Jason.Encoder not implemented for {:safe, "<span>\n  Iaşi</br>\n  <small>Iaşi</small>\n</span>\n"} of type Tuple, Jason.Encoder protocol must always be explicitly implemented. This protocol is implemented for the following type(s): Any, Atom, BitString, Date, DateTime, Decimal, Ecto.Association.NotLoaded, Ecto.Schema.Metadata, Float, Integer, Jason.Fragment, Jason.OrderedObject, List, Map, NaiveDateTime, Recenza.Models.Service, Recenza.Models.Speciality, Time
    (jason 1.4.1) lib/jason.ex:213: Jason.encode_to_iodata!/2
@silviurosu
Copy link
Author

I did some investigation myself and the issue comes from client_select method where it tries to push "select" event to server with this payload:

%{
  id: "search_component_city",
  input_event: true,
  mode: :single,
  parent_event: nil,
  selection: [
    %{
      label: {:safe, "<span>\n  Iaşi</br>\n  <small>Iaşi</small>\n</span>\n"},
      value: "Iaşi"
    }
  ]
}

I guess the problem lies in the fact that it tries to send back to the server the label and the value.
One idea would be to send the html as string without raw() but on UI to append raw automatically, or via a parameter.

@silviurosu
Copy link
Author

Adding raw here:

<%= Phoenix.HTML.raw(option.label) %>

kind of works with the selection but after I select the value it shows the html as selected value which is not what I want.

Screenshot 2023-12-22 at 12 08 33

I would nice to support an option to define templates like typeahead has:

templates: {
    empty: [
      '<div class="empty-message">',
        'unable to find any Best Picture winners that match the current query',
      '</div>'
    ].join('\n'),
    suggestion: Handlebars.compile('<div><strong>{{value}}</strong> – {{year}}</div>')
  }

In the same note it would be nice to highlight also the part of the text that matched.
Screenshot 2023-12-22 at 12 06 52

@silviurosu
Copy link
Author

I finally found how to do it reading though the code and test cases.
When I send the values to the component I can append extra keys to the map:

%{key: city, value: city, state: state}

then I can use the option tag to customize how it renders:

 <:option :let={option}>
      <span>
         <%= option.label %><br />
         <small><%= option.state %></small>
      </span>
</:option>

I guess this needs more documentation. Can I send a PR for that?
Thew only open question I have is how to highlight the part of the text that matches.

@maxmarcon
Copy link
Owner

Hi, glad you found the right way to render HTML in the options :)

Sure, go ahead and send a PR for the documentation if you feel it can be improved!

Regarding highlighting the matching part of the text: this can be done on the same lines of what you're doing to show your "option.state" label. One could add the matching prefix as an additional field to the option and then render accordingly.

Something like this:

<:option :let={%{label: label, matching_prefix: matching_prefix}}>
    <div><strong><%= matching_prefix %></strong><%= String.replace_prefix(label, matching_prefix, "") %></div>
</:option>

I could add a cheatsheet that shows how to do it. What do you think?

@silviurosu
Copy link
Author

It would be good to add it to cheatsheet indeed. I was not able to find this from the documentation alone so it would help others

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants