I’ve noticed some inconsistent behavior with the linting rules for stories, but it will be easier to explain with a simple example. Let’s say we have a basic button component that looks like this:
type ButtonVariant = "primary" | "secondary";
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;
}
export const Button: React.FC<ButtonProps> = ({ ...props }) => {
return <button {...props}>click</button>;
};
and we wrote some simple stories for it:
type StoryProps = ComponentProps<typeof Button>;
const meta: Meta<StoryProps> = {
component: Button,
tags: ["autodocs"],
argTypes: {
variant: {
options: ["primary", "secondary"],
control: {
type: "select",
},
},
},
};
export default meta;
type Story = StoryObj<StoryProps>;
export const Primary: Story = {
args: {
variant: "variant-that-do-not-exist-or-whatever",
},
};
export const Secondary: Story = {
args: {
variant: "secondary",
},
};
when the file is opened in the editor, the linter highlights the “variant” property with an error message, but running the lint script doesn’t result in an error.
To Reproduce
Steps to reproduce the behavior:
- Provide any error value for one of args properties in Story object
- Save file
- Run lint
Expected behavior
IMO error should be thrown by eslint. Or shouldn’t be? If not, then why?
Screenshots
File:
Terminal output after running lint:
Additional context
Below you can find rest of the project config.
package.json
{
"name": "playground",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@chromatic-com/storybook": "^1.6.1",
"@eslint/js": "8.57.0",
"@storybook/addon-essentials": "8.2.4",
"@storybook/addon-interactions": "^8.2.9",
"@storybook/addon-links": "^8.2.9",
"@storybook/addon-onboarding": "^8.2.9",
"@storybook/blocks": "^8.2.9",
"@storybook/react": "8.2.4",
"@storybook/react-vite": "8.2.4",
"@storybook/test": "8.2.4",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "8.1.0",
"@typescript-eslint/parser": "8.1.0",
"@vitejs/plugin-react": "^4.3.1",
"eslint": "8.57.0",
"eslint-plugin-react": "7.35.0",
"eslint-plugin-react-hooks": "4.6.2",
"eslint-plugin-react-refresh": "0.4.7",
"eslint-plugin-storybook": "0.8.0",
"globals": "^15.9.0",
"storybook": "8.2.4",
"typescript": "^5.5.3",
"typescript-eslint": "^8.0.0",
"vite": "^5.4.0"
}
}
tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",
"allowJs": false,
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true
},
"include": ["src"]
}
.eslintrc.cjs
module.exports = {
root: true,
extends: [
"eslint:recommended",
"plugin:react-hooks/recommended",
"plugin:react/jsx-runtime",
"plugin:storybook/recommended",
"plugin:@typescript-eslint/recommended-type-checked",
],
plugins: [
"react",
"react-hooks",
"react-refresh",
"@typescript-eslint",
"storybook",
],
env: { browser: true, es2020: true },
ignorePatterns: [
"dist",
"storybook-static",
".eslintrc.cjs",
"vite.config.ts",
],
parser: "@typescript-eslint/parser",
parserOptions: {
project: true,
ecmaFeatures: { jsx: true },
},
settings: {
react: {
version: "detect",
},
},
};
I tried various configurations for ESLint, different plugin versions, and overriding lint rules for .stories files, but the result was always the same.