#Best way to set up an Angular enum control?

7 messages · Page 1 of 1 (latest)

ivory garden
#

Let's say that I have this enum:

export enum Variant {
  Primary = 'pr',
  Secondary = 'sec',
}

How would I make a control in storybook that allows me to switch between these two options? I deliberately made the values much less self-explanatory than the keys in this example, as that's one of the main benefits of enums - user-friendly labels for string values.

The problem I noticed with the common approaches is that they use the enum value, rather than key, which I don't think is desirable. But when you use something like this to try to fix this:

argTypes: {
  variant: {
    options: Object.keys(Variant),
    mapping: Variant
  }
}

The default value will need to be 'Primary' string instead of Variant.Primary. So it seems like full obfuscation of enum values plus never using strings instead of corresponding enums is not possible. I hope I'm wrong, as this seems like a common scenario and knobs had a very easy way to accomplish this:

variant: select<Variant>('variant', Variant, Variant.Primary)

It would also be cool if the controls docs included an enum example.

turbid sinew
#

Technically enums are just a string or number, so when you say "the default value will need to be 'Primary' string, instead of Variant.Primary" you are just referring to how it displays in Controls, right?

I see your knobs example uses a select, but Storybook uses a radio by default. You could add control: { type: 'select' } to use a select, instead, if that is what you are asking.

I think your mapping snippet is the best option, if you want to list the enums, instead of the real values.

It doesn't sound like that is what you want, though, so I did some playing around to see if I saw a way to list the options with the enum name prefixed on the property. Storybook could update how it creates the default mappings for inputs. I don't know if it would be preferred to change that or not, but you could see if it has been discussed anywhere and open an issue to propose that change, if not.

The following is the best I could come up with at the moment as a workaround. It isn't too great though.

function enumArgType(obj: any): any {
  const enumName = Object.keys(obj)[0];
  const enumObj = obj[enumName];
  const options = Object.keys(enumObj).map((x) => `${enumName}.${x}`);
  const mapping: any = {};
  Object.keys(enumObj).forEach((x) => {
    mapping[`${enumName}.${x}`] = enumObj[x];
    mapping[`${x}`] = enumObj[x];
    mapping[enumObj[x]] = enumObj[x];
  });
  // return { options, mapping, control: { type: 'select' } };
  return { options, mapping };
}

export default {
  argTypes: {
    // Passing the enum as the property of an object, so I can use the property name as the enum name.
    vehicle: enumArgType({ Variant }),
  },
}
ivory garden
#

@turbid sinew Thank you for your response! I should clarify what I meant by default value.

argTypes: {
  variant: {
    options: Object.keys(Variant),
    mapping: Variant
  }
}

Story.args = {
  variant: 'Primary', // what I have to do for storybook to select the 'Primary' option in the controls panel by default
  variant: Variant.Primary // what I would rather do
}

Do you know if there is a combination of configs that would allow me to do this?

turbid sinew
#

Oh, for me that actually works both ways, but the control doesn't show it selected. I am trying to figure out if I have something wrong or if it is a bug. I just noticed that I think your commment says that.

ivory garden
#

yeah, it seems to work, but the selected indicator isn't there in the Storybook UI. I'm not sure that's good enough for me, since it may confuse our storybook users. for example, when the "Default" column of the variant row in the UI says Variant.Primary, but in this particular story you want to showcase Variant.Secondary instead. so the user will think its showing the default value (Variant.Primary), since nothing is shown as being selected, but there is actually a different value selected behind the scenes (Variant.Secondary)

turbid sinew
#

Ok, yeah, I think I see the fix, but I am still trying think about why it didn't work the other way.

argTypes: {
  variant: {
    // I think `options` and `mapping` are optional for enums, since you are just dealing with primitives, but there may be a case I am not thinking about.
    options: Object.keys(Variant),
    mapping: Variant,
    control: {
      type: 'radio', // or 'select'
      labels: Object.keys(Variant).reduce((acc, curr) => {
        return { ...acc, [`${(Variant as any)[curr]}`]: curr }
      }, {}),
    },
  },
},
ivory garden
#

@turbid sinew that config does seem to solve the problem, as long as the options and mapping configs are removed. still, its strange that it doesn't work out of the box. thank you for your help!