reactjs - Storybook: Cum să marchezi tipurile generice pentru args/props


Context și Cod

Am extins butonul nostru astfel:

export type ButtonProps<P = Record<string, unknown>> = P & {
  children: ReactNode | string
  onClick?: () => void
  className?: string
  disabled?: boolean
  secondary?: boolean
}

const Button = <P extends Record<string, unknown>>({
  children,
  onClick,
  className,
  disabled = false,
  secondary = false,
}: ButtonProps<P>) => {
  ...
}

Această supraîncărcare înseamnă că putem implementa butoane noi cu proprietăți suplimentare, fără a deranja butonul original, ceva de genul:

const FooterTwoButtons = ({
  secondaryOnClick,
  secondaryIconType,
  icon,
  className,
  ...props
}: ButtonProps<TwoButtonsProps>) => {
  return (
    <div className={clsx('grid grid-cols-5', className)}>
      <Button onClick={secondaryOnClick} secondary>
        {(secondaryIconType !== undefined && (
          <Icon type={secondaryIconType} />
        )) ||
          (icon !== undefined && icon)}
      </Button>
      <FooterButton {...props}>{props.children}</FooterButton>
    </div>
  )
}

Este super fain deoarece permite ca mai multe componente efemere de buton să fie extinse din tipul nostru de bază. Totul se compilează și funcționează conform așteptărilor, cu excepția storybook...

Problemă

Se pare că nu pot găsi nicio întrebare/problema legată de un component modern storybook care să permită proprietăți generice.

Povestea ExtendedButtonProps:

export const ButtonExtendedProps: ComponentStory<typeof Button> = ({
  secondaryIconType,
  children,
  ...props
}: ButtonProps<ExtendedProps>) => {
  return (
    <div>
      <Button secondaryIconType={secondaryIconType} {...props}>
        {children}
      </Button>
    </div>
  )
}
ButtonExtendedProps.args = {
  ...Default.args,
  secondaryIconType: 'CircledX',
}

Iată eroarea compilatorului tsc, care apare la ButtonExtendedProps în codul de mai sus:

module ButtonExtendedProps
const ButtonExtendedProps: ComponentStory<(<P extends Record<string, unknown>>({ children, onClick, className, disabled, secondary, }: ButtonProps<P>) => JSX.Element)>
Type '{ ({ secondaryIconType, children, ...props }: ButtonProps<ExtendedProps>): JSX.Element; args: Partial<Record<string, unknown> & { children: ReactNode; onClick?: (() => void) | undefined; className?: string | undefined; disabled?: boolean | undefined; secondary?: boolean | undefined; }> | undefined; }' is not assignable to type 'ComponentStory<(<P extends Record<string, unknown>>({ children, onClick, className, disabled, secondary, }: ButtonProps<P>) => Element)>'.
  Type '{ ({ secondaryIconType, children, ...props }: ButtonProps<ExtendedProps>): JSX.Element; args: Partial<Record<string, unknown> & { children: ReactNode; onClick?: (() => void) | undefined; className?: string | undefined; disabled?: boolean | undefined; secondary?: boolean | undefined; }> | undefined; }' is not assignable to type 'ArgsStoryFn<ReactFramework, Record<string, unknown> & { children: ReactNode; onClick?: (() => void) | undefined; className?: string | undefined; disabled?: boolean | undefined; secondary?: boolean | undefined; }>'.
    Types of parameters '__0' and 'args' are incompatible.
      Type 'Record<string, unknown> & { children: ReactNode; onClick?: (() => void) | undefined; className?: string | undefined; disabled?: boolean | undefined; secondary?: boolean | undefined; }' is not assignable to type 'ButtonProps<ExtendedProps>'.
        Property 'secondaryIconType' is missing in type 'Record<string, unknown> & { children: ReactNode; onClick?: (() => void) | undefined; className?: string | undefined; disabled?: boolean | undefined; secondary?: boolean | undefined; }' but required in type 'ExtendedProps'.ts(2322)

Poate cineva să vadă ce am ratat aici? Există o pagină în documentația Storybook care oferă exemple pentru asta? Am căutat destul de mult astăzi, dar nu sunt sigur ce cuvinte cheie să folosesc. Orice sfat este foarte apreciat. Mulțumesc.

Răspuns #1

Așa cum s-ar întâmpla, ComponentStory<typeof Button> === Story<ButtonProps>

Deci semnătura poveștii rezultate arăta astfel:

export const ButtonExtendedProps: Story<ButtonProps<ExtendedProps>> = ({
  altIconType,
  altClassName,
  altAriaLabel,
  children,
  clean,
  ...props
}: ButtonProps<ExtendedProps>) => {