ui icon indicating copy to clipboard operation
ui copied to clipboard

Select: 'className' is missing in props validation

Open marco-digennaro opened this issue 1 year ago • 37 comments

In the Select component, the prop className doesn't exist in the interface so it gives the above error

Screenshot 2023-03-09 at 09 57 34

marco-digennaro avatar Mar 09 '23 08:03 marco-digennaro

Hmm that's odd. Trigger extends HTMLButtonElement so there should be a className in there.

shadcn avatar Mar 10 '23 11:03 shadcn

it's actually happening also on other components

marco-digennaro avatar Mar 21 '23 10:03 marco-digennaro

I'm having the exact same problem :( also in Label, Select...

doneumark avatar Apr 03 '23 20:04 doneumark

Hey @marco-digennaro @doneumark! It would be nice to have a minimal code example (maybe CodeSandbox?). I couldn't reproduce the error using TS 4.7.4

emicba avatar Apr 03 '23 20:04 emicba

Hey @emicba, It's actually the EXACT components above from the docs. But I think the problem is might be I have typescript 5? what happens if you update to the latest typescript 5 version? Thanks!

doneumark avatar Apr 03 '23 20:04 doneumark

Seems to be working fine on TS 5.0.3.

emicba avatar Apr 03 '23 21:04 emicba

Was having trouble with this today until I removed the following line from my tsconfig.json:

preserveSymlinks: true

litewarp avatar Apr 10 '23 03:04 litewarp

This is because of a change made in lucide-react I believe - using [email protected] instead of the latest fixed this for me.

Edit: after some further investigation, the latest version you can use without issue is [email protected]. The next version is where the break occurs.

nickcoad avatar Apr 16 '23 01:04 nickcoad

@nickcoad I tried downgrading to both [email protected] and [email protected], I'm still having the issue in the ContextMenu component. I even tried completely commenting out any Lucide icons and the Lucide import, I still have the issue.

b0o avatar Apr 16 '23 20:04 b0o

@b0o interesting - what's an example of a specific line that is showing this error, can you paste it here?

nickcoad avatar Apr 19 '23 01:04 nickcoad

2023-04-18_19-37-21_region

'use client'

import { DialogProps } from '@radix-ui/react-dialog'
import { Command as CommandPrimitive } from 'cmdk'
import { Search } from 'lucide-react'
import * as React from 'react'

import { cn } from '#/lib/utils'
import { Dialog, DialogContent } from '#/ui/shad/Dialog'

const Command = React.forwardRef<
  React.ElementRef<typeof CommandPrimitive>,
  React.ComponentPropsWithoutRef<typeof CommandPrimitive>
>(({ className, ...props }, ref) => (
  <CommandPrimitive
    ref={ref}
    className={cn('flex h-full w-full flex-col overflow-hidden rounded-lg bg-bl-gray-800', className)}
    {...props}
  />
))
Command.displayName = CommandPrimitive.displayName

That's just one example, but it occurs numerous times in that file.

b0o avatar Apr 19 '23 02:04 b0o

The issue got resolved for me as soon as I turned on Next.js TS server instead of VSCode one.

3atharvkurdukar avatar Apr 21 '23 17:04 3atharvkurdukar

I'm having this issue inside the Dropdown Menu component as well as the Context Menu component. It doesn't seem as though any of the solutions above have fixed it for me.

landendanyluk avatar May 04 '23 05:05 landendanyluk

For the moment I have silenced that error by creating an interface that extend the props type. To use @b0o example:

interface Props extends React.ComponentPropsWithoutRef<typeof CommandPrimitive> {}
const Command = React.forwardRef<
  React.ElementRef<typeof CommandPrimitive>,
  Props
>(({ className, ...props }, ref) => (
  <CommandPrimitive
    ref={ref}
    className={cn('flex h-full w-full flex-col overflow-hidden rounded-lg bg-bl-gray-800', className)}
    {...props}
  />
))
Command.displayName = CommandPrimitive.displayName

You'll also have to add/update the following rule in your eslint config, or you'll gett the @typescript-eslint/no-empty-interface error by default.

"@typescript-eslint/no-empty-interface": [
      "error",
      {
        allowSingleExtends: true,
      },
    ],

SebastienWae avatar Jun 01 '23 09:06 SebastienWae

Having the same problem while using the table component. Screenshot from 2023-06-05 01-35-50

DennohKim avatar Jun 04 '23 22:06 DennohKim

Having this same issue using the Tabs component

billyjacoby avatar Jun 19 '23 15:06 billyjacoby

@shadcn any update on this error? Seems to be plaguing everyone in our team as well.

mzavattaro avatar Jun 21 '23 08:06 mzavattaro

This fixes the error, but is not the best solution. Haven't had time to work look at the existing types.

type props = React.ElementRef<typeof AvatarPrimitive.Root> & { className?: String }
type props2 = React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> & { className?: String }

const Avatar = React.forwardRef<props, props2>(({ className, ...props }, ref) => (
  <AvatarPrimitive.Root ref={ref} className={cn('relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full', className)} {...props} />
))
Avatar.displayName = AvatarPrimitive.Root.displayName

rtorcato avatar Jun 21 '23 17:06 rtorcato

@billyjacoby @mzavattaro I still cannot reproduce this? Can you share some code I can take a look at?

shadcn avatar Jun 21 '23 18:06 shadcn

@shadcn Sure! It happens in both of the files in my browser app here:

https://github.com/billyjacoby/browsernaut/tree/main/src/components/ui

billyjacoby avatar Jun 21 '23 20:06 billyjacoby

@billyjacoby @mzavattaro I still cannot reproduce this? Can you share some code I can take a look at?

disabling eslint-plugin-react in my eslintrc file makes the error go away.

https://www.npmjs.com/package/eslint-plugin-react

in shadcn repo the eslint rules are:

"extends": [ "next/core-web-vitals", "turbo", "prettier", "plugin:tailwindcss/recommended" ],

but they really should have some linting for react and that's why devs are having problems when they have
"plugin:react/recommended" in their project.

in my previous post this is the fix. For Avatar as an example.

const Avatar = React.forwardRef<
  React.ElementRef<typeof AvatarPrimitive.Root> & { className: String },
  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> & {
    className: String
  }
>(({ className, ...props }, ref) => (
  <AvatarPrimitive.Root
    ref={ref}
    className={cn(
      "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
      className
    )}
    {...props}
  />
))

but there should be a more elegant way to define the props using generics.

rtorcato avatar Jun 21 '23 20:06 rtorcato

https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/prop-types.md#as-for-exceptions It says

It would seem that some common properties such as props.children or props.className (and alike) need to be treated as exceptions.

So instead of disabling eslint-plugin-react, i think it is better way to ignore "className" prop in rules of eslintrc.

"rules": {
    "react/prop-types": [2, { "ignore": ["className"] }],
    ...
}

Also, it will not trigger an error in other components if the 'className' is missing from props. However, if you're using TypeScript, the compiler will throw an error, so you should be fine.

Chuhj avatar Oct 01 '23 17:10 Chuhj

@Chuhj is best solution for now. Here is a link to the source of problem https://github.com/jsx-eslint/eslint-plugin-react/issues/3284

marlonmarcello avatar Nov 22 '23 01:11 marlonmarcello

To add to @Chuhj solution, I've used "overrides" to disable rule only for shadcn/ui components

  overrides: [
    {
      files: ['**/components/ui/*.tsx'], 
      rules: {
        'react/prop-types': [2, { ignore: ['className'] }],
        'react-refresh/only-export-components': 'off',
      },
    },
  ],
```

mimccio avatar Nov 27 '23 15:11 mimccio

@mimccio's solution is working great for me as a slight +1 to scope down @Chuhj's also great solution

roger-tbg avatar Dec 05 '23 14:12 roger-tbg

In my case, ESLint barks not only on className props, but on the other destructured props...

image

Despite the fact, that the type is inferred correctly...

image

For this given component I solved it by replacing React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root> type annotation with the direct type import from the radix-ui lib.

image

image

Such approach seems quite logical when looking in the Separator source code. But I haven't looked into another components yet, so don't know whether it can be applied to them.

Update: I've looked briefly at some other components. And it seems the approach can be applied to them also. For example, for the Select component in question we can:

import type {
  SelectProps,
  SelectTriggerProps,
  SelectValueProps,
  SelectIconProps,
  SelectPortalProps,
  SelectContentProps,
  SelectViewportProps,
  SelectGroupProps,
  SelectLabelProps,
  SelectItemProps,
  SelectItemTextProps,
  SelectItemIndicatorProps,
  SelectScrollUpButtonProps,
  SelectScrollDownButtonProps,
  SelectSeparatorProps,
  SelectArrowProps,
} from '@radix-ui/react-select';

And then replace React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content> with just SelectTriggerProps. And do the same for other subcomponets.

stsiarzhanau avatar Dec 16 '23 16:12 stsiarzhanau

Copilot chat answer:

If you're using ESLint with a TypeScript project, you might want to disable the react/prop-types rule, because it's not necessary when TypeScript is doing the type checking. You can do this in your ESLint configuration file (.eslintrc or .eslintrc.js) like this:

{
  "rules": {
    "react/prop-types": "off"
  }
}

leimantas avatar Dec 23 '23 11:12 leimantas

Alright. The culprit is eslintrc.cjs file.

I replaced mine with the Remix's eslint config and the error was gone. If you are using Next.js, then use the official eslintrc.cjs file from Next.js project.

My fix was this for a Remix project

inside .eslintrc.cjs

/** @type {import('eslint').Linter.Config} */
module.exports = {
  extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node"],
};

rajeshdavidbabu avatar Jan 11 '24 13:01 rajeshdavidbabu

Copilot chat answer:

If you're using ESLint with a TypeScript project, you might want to disable the react/prop-types rule, because it's not necessary when TypeScript is doing the type checking. You can do this in your ESLint configuration file (.eslintrc or .eslintrc.js) like this:

{
  "rules": {
    "react/prop-types": "off"
  }
}

I 2nd this. The prop-types rule doesn't offer anything in TS projects. TS itself will tell you when you're trying to use a prop that wasn't defined.

drake-nathan avatar Jan 18 '24 20:01 drake-nathan

You may have this problem with other props too, so in addition to @Chuhj 's answer, ESLint configuration (.eslintrc) files are hierarchical. So you can add one to your /ui directory and only your ui files will ignore this error. Solving like this made me feel better maybe someone else will want to solve like this

Sylpherena avatar Feb 28 '24 18:02 Sylpherena