storybookjs / storybook

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

[Bug]: Only props defined in the component being displayed within Story #22187

Open Dawdre opened 1 year ago

Dawdre commented 1 year ago

Describe the bug

Context

I am at the start of implementing Storybook into our Vue 2 Typescript Design System. Given the repetitive nature of some of our components, i.e. form components, we use vue composables to make consuming common props and functionality easier.

Problem

The props defined in my util/composable are not being picked up and thus missing from the documentation/canvas.

Is there a way to get around this? Setting args manually for the "missing" props has drawbacks. I have done this in the examples below just for illustrative purposes

Goal

Have my abstract props be picked up by Storybook files

Text input component

// AtomTextInput.vue
const { coreFormProps, coreSingleInputProps } = useCoreFormAtomProps();

props: {
    ...coreFormProps,
    ...coreSingleInputProps,
    value: {
      type: String,
      required: true,
      default: undefined,
    },
    type: {
      type: String as PropType<AtomInputType>,
      default: "text",
    },
    autocomplete: {
      type: String as PropType<AtomAutoCompleteType>,
      default: "off",
    },
  },

Atom composable/util

export const useCoreFormAtomProps = () => {
  const coreFormProps = {
    id: {
      type: String,
      required: true,
      default: undefined,
    },
    label: {
      type: String,
      required: true,
    },
    /**
     * Set the field to optional
     * Also sets the aria-required attribute to true or false
     */
    optional: {
      type: Boolean,
      default: false,
    },
    errorMessage: {
      type: String,
      default: undefined,
    },
    hintText: {
      type: String,
      default: undefined,
    },
  };

  const coreSingleInputProps = {
    /**
     * Set the width of the input element
     * Both percentage and fixed widths are supported
     */
    width: {
      type: [String, Number] as PropType<AtomInputWidthType>,
      default: "full",
    },
    /**
     * Sets the label to be visually hidden
     * This ensures screen readers can still read out the label text
     */
    isLabelVisible: {
      type: Boolean,
      default: true,
    },
  };

  return {
    coreFormProps,
    coreSingleInputProps,
  };
};

Story file

const meta: Meta<typeof AtomTextInput> = {
  title: "eCase Design System/Atoms/AtomTextInput",
  component: AtomTextInput,
  args: {
    label: "Test label",
    id: "text-input-id",
    width: "one-half",
  },
};

export default meta;
type Story = StoryObj<typeof AtomTextInput>;

export const Default: Story = {
  render: (args, { argTypes }) => ({
    props: Object.keys(argTypes),
    components: { AtomTextInput },
    data() {
      return {
        something: "",
      };
    },
    template: "<atom-text-input v-bind='$props' v-model='something'/>",
  }),
};

To Reproduce

No response

System

System:
    OS: Windows 10 10.0.19044
    CPU: (8) x64 Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz
  Binaries:
    Node: 18.15.0 - C:\Program Files\nodejs\node.EXE
    npm: 9.5.0 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Chrome: 112.0.5615.138
    Edge: Spartan (44.19041.1266.0), Chromium (112.0.1722.39)
  npmPackages:
    @storybook/addon-a11y: ^7.0.5 => 7.0.5
    @storybook/addon-actions: ^7.1.0-alpha.0 => 7.1.0-alpha.0
    @storybook/addon-essentials: ^7.1.0-alpha.0 => 7.1.0-alpha.0
    @storybook/addon-interactions: ^7.1.0-alpha.0 => 7.1.0-alpha.0
    @storybook/addon-links: ^7.1.0-alpha.0 => 7.1.0-alpha.0
    @storybook/addon-mdx-gfm: ^7.1.0-alpha.0 => 7.1.0-alpha.0
    @storybook/preset-scss: ^1.0.3 => 1.0.3
    @storybook/testing-library: ^0.1.0 => 0.1.0
    @storybook/vue: ^7.1.0-alpha.0 => 7.1.0-alpha.0
    @storybook/vue-webpack5: ^7.1.0-alpha.0 => 7.1.0-alpha.0

Additional context

No response

chriskrj commented 1 year ago

Have the same Error. I'm passing parameters, decorates and test Information through a Configuration File

Configuration.ts

import { withTests } from '@storybook/addon-jest';
import results from '@kern/components/jest-test-results.json';
import { BADGE } from '@geometricpanda/storybook-addon-badges';
import { argTypes } from './arg-types';

export const HeadingConfiguration = {
  decorators: [
    withTests({
      results,
    }),
  ],
  parameters: {
    badges: [BADGE.STABLE],
    jest: ['heading.e2e.ts', 'heading.spec.ts', 'heading.spec.tsx'],
    status: { type: 'bitv' },
  },
  ...argTypes,
};

arg-types.ts

export const argTypes = {
  _level: {
  name: 'Überschrift (h1 bis h6)',
  description: 'Gibt an, welchen H-Level von 1 bis 6 die Überschrift hat. Oder ob es keine Überschrift ist, sondern nur fett gedruckt.',
  control: {
      type: 'select',
  },
  options: [1, 2, 3, 4, 5, 6],
  type: {
      required: true,
  },
 defaultValue: 1,
},
_label: {
  name: 'Titel',
  description: 'Gibt den Text der Überschrift an.',
  control: {
      type: 'text',
},
  defaultValue: 'Beliebiger Titel.',
  },
};

image

In AutoDocs of my Component, the argType information won't show up.

If I manually add argTypes Information in my react.stories.ts everything works normally

chriskrj commented 1 year ago

I changed my argTypes File and my configuration.ts and now everything works fine.

type: {
 required:true
 }

Is shown correctly in column name but satifies Meta<typeof Heading> doesn't know this parameter. Maybe this is a different Issue.

In my Configuration I changed to include from argTypes from ...argTypes to just argTypes

seaneez commented 9 months ago

Having trouble with the same issue! I think this solution might work.

  1. First make an util function

    function getMetaArguments(componentProps: { [key: string]: any }) {
    const metaArgs: { [key: string]: any } = {};
    Object.keys(componentProps).forEach((prop) => {
        const { default: defaultValue } = componentProps[prop];
        if (typeof defaultValue === 'function') {
            metaArgs[prop] = defaultValue();
            return;
        }
        if (defaultValue === undefined) {
            metaArgs[prop] = null;
        } else {
            metaArgs[prop] = defaultValue;
        }
    });
    return metaArgs;
    }
  2. add the result to meta.args

// ...stories.ts

const meta: Meta<typeof AtomTextInput> = {
   ...
}

meta.args = getMetaArguments(AtomTextInput.props);
export default meta;