storybookjs / storybook

Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation
https://storybook.js.org
MIT License
83.95k stars 9.22k forks source link

Allow enum types to choose their default control in addon-controls. #16018

Open andrewplummer opened 3 years ago

andrewplummer commented 3 years ago

In addon-controls, custom control types can be configured, however as far is I can tell they can only be explicitly configured with argTypes for specific properties. The default is to use radio when less than 5 options and select for larger. This is especially a problem when using prop-types as there is no way to know at runtime whether a prop is an enum, so this cannot be dynamically configured.

In the meantime this workaround will change all enums to select type. You can adjust as needed.

Note that this has only been tested on v6.3.8 and will probably break anywhere else.

First, you will need to add a webpackFinal entry in .storybook/main.js and add the following:

module.exports = {
  // ...
  webpackFinal: async (config) => {
    config.resolve.alias = {
      ...config.resolve.alias,

      // We will alias @storybook/client-api/inferControls.js module to a custom shim that we will create.
      // Note the import is relative so this will match any relative import in the build, however the name
      // is unique enough that it should be ok.
      './inferControls': path.resolve(__dirname, './shims/controls/inferControls.js'),

    };
    return config;
  },
};

Now create .storybook/shims/controls/inferControls.js and add the following:

import { inferControls } from '@storybook/client-api/dist/esm/inferControls';

// Hack to convert dynamically inferred radio controls to select,
// as there is no way to control this behavior. This needs to
// be aliased to `@storybook/client-api/inferControls`.
function inferControlsShim(...args) {
  const result = inferControls(...args);
  for (let entry of Object.values(result || {})) {
    if (entry.control?.type === 'radio') {
      entry.control.type = 'select';
    }
  }
  return result;
}

export * from '@storybook/client-api/dist/esm/inferControls';
export { inferControlsShim as inferControls };

This will hijack the inferControls method of @storybook/client-api and force any inferred radio types to be select. You can adjust as needed here, for example inline-radio.

shilman commented 2 years ago

@yannbf This seems similar in spirit to your matchers parameter. any thoughts?

andrewplummer commented 2 years ago

I was actually expecting a variant of that to work but it's not matching on the name so there didn't appear to be a way...

Also this isn't really a problem with controls unfortunately it appears to be buried deep inside client-api