Best practice for enum argument to phoenix function component

When creating function component with an “enum” like argument, I was wondering what you used.

For example:

<.foo flavor="info" />
<.foo flavor={:info} />
<.foo info />
<.foo_info />

What do you think is the best considering readability and maintenance/coding?

c if it is the only status (a boolean flag and not an enum), otherwise a or b. b is more obviously Elixir code, i.e. not an html attribute.

I like c because it is clean to read, but yeah, it is an enum, so you can technically write <.foo info warning /> and it will conflict.

b is the most technically correct I would say, but it is a bit ugly.

It’s certainly a style choice, but judicious use of mutually exclusive attributes can be ok. There is precedent in, for example, the <.link> component—you can give only one of patch, navigate, or href. Of course these aren’t booleans.

I do use booleans like this in one place with buttons. The button “variant” can be one of primary, secondary, or ghost (which is what we call “muted” or “tertiary”). I wasn’t super happy with calling the attribute variant, kind, or style, and type was out because that is already an HTML attribute for <button> that has a completely different meaning. So I went with mutually exclusive booleans.

I use the following code to raise if someone passes more than one:

def validate_boolean_group!(assigns, group) do
  {attrs, booleans} =
    assigns
    |> Enum.filter(fn
      {attr, _value} -> attr in group
      _ -> false
    end)
    |> Enum.unzip()

  num_true_values = Enum.count(booleans, & &1 == true)

  if num_true_values <= 1 do
    true
  else
    message = "You may only set one of: [:" <> Enum.join(attrs, ", :") <> "]"

    raise DesignSystem.AttributeError, message: message
  end
end

group is a list of possible values.

It’s definitely a smell that you have to add developer guard-rails. You also can’t use attr’s :default if you do this otherwise that check won’t work, so you have to set it in the component itself. This isn’t something I would use much at all, but in this one case I preferred the API. This is for my employer’s design system so depending on feedback, I may bail out of this decision anyway :slight_smile:

Yeah, the button example is very close to my use case. I guess adding a “developer guard” is a good precaution.

Might be easier to use an enum and set the values option?

In your case, the name of the attribute should be the_vibe_of_it.

lol, I like it.

Ya, sorry I thought it was implied that that is alternative—I have plenty of those. Just this one case I did indeed just like the vibe of the booleans.

Based on what you’ve described my vote is for C. Yes it is a bit aesthetically ugly but I think it makes it very clear that you’re passing a mutually exclusive enum that will be used directly within the elixir code (e.g. not something that gets directly passed down to html). For me clarity wins over aesthetics.