eslint-plugin-storybook 0.4.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,54 @@
1
+ # v0.5.2 (Thu Dec 02 2021)
2
+
3
+ #### 🐛 Bug Fix
4
+
5
+ - fix: support jsx extension [#60](https://github.com/storybookjs/eslint-plugin-storybook/pull/60) ([@yannbf](https://github.com/yannbf))
6
+ - Update README.md spelling error, one letter. [#50](https://github.com/storybookjs/eslint-plugin-storybook/pull/50) ([@daylennguyen](https://github.com/daylennguyen))
7
+
8
+ #### Authors: 2
9
+
10
+ - Daylen Nguyen ([@daylennguyen](https://github.com/daylennguyen))
11
+ - Yann Braga ([@yannbf](https://github.com/yannbf))
12
+
13
+ ---
14
+
15
+ # v0.5.1 (Mon Nov 29 2021)
16
+
17
+ #### 🐛 Bug Fix
18
+
19
+ - Fix: define hasSuggestions to all fixable rules [#55](https://github.com/storybookjs/eslint-plugin-storybook/pull/55) ([@yannbf](https://github.com/yannbf))
20
+
21
+ #### Authors: 1
22
+
23
+ - Yann Braga ([@yannbf](https://github.com/yannbf))
24
+
25
+ ---
26
+
27
+ # v0.5.0 (Tue Nov 23 2021)
28
+
29
+ #### 🚀 Enhancement
30
+
31
+ - Rule: pass context when invoking play [#53](https://github.com/storybookjs/eslint-plugin-storybook/pull/53) ([@yannbf](https://github.com/yannbf))
32
+
33
+ #### Authors: 1
34
+
35
+ - Yann Braga ([@yannbf](https://github.com/yannbf))
36
+
37
+ ---
38
+
39
+ # v0.4.2 (Fri Nov 19 2021)
40
+
41
+ #### 🐛 Bug Fix
42
+
43
+ - Fix await interactions use cases [#51](https://github.com/storybookjs/eslint-plugin-storybook/pull/51) ([@yannbf](https://github.com/yannbf) [@shilman](https://github.com/shilman))
44
+
45
+ #### Authors: 2
46
+
47
+ - Michael Shilman ([@shilman](https://github.com/shilman))
48
+ - Yann Braga ([@yannbf](https://github.com/yannbf))
49
+
50
+ ---
51
+
1
52
  # v0.4.1 (Sat Nov 13 2021)
2
53
 
3
54
  #### 🐛 Bug Fix
package/README.md CHANGED
@@ -72,12 +72,12 @@ Optionally, you can override, add or disable rules settings. You likely don't wa
72
72
  "overrides": [
73
73
  {
74
74
  // or whatever matches stories specified in .storybook/main.js
75
- "files": ['*.stories.@(ts|tsx|js|mjs|cjs)'],
75
+ "files": ['*.stories.@(ts|tsx|js|jsx|mjs|cjs)'],
76
76
  "rules": {
77
77
  // example of overriding a rule
78
78
  'storybook/hierarchy-separator': 'error',
79
79
  // example of disabling a rule
80
- 'storybook/defaul-exports': 'off',
80
+ 'storybook/default-exports': 'off',
81
81
  }
82
82
  }
83
83
  ]
@@ -96,19 +96,20 @@ This plugin does not support MDX files.
96
96
 
97
97
  **Configurations**: csf, csf-strict, addon-interactions, recommended
98
98
 
99
- | Name | Description | 🔧 | Included in configurations |
100
- | ------------------------------------------------------------------------------------------ | --------------------------------------------------- | --- | -------------------------------------------------------- |
101
- | [`storybook/await-interactions`](./docs/rules/await-interactions.md) | Interactions should be awaited | 🔧 | <ul><li>addon-interactions</li><li>recommended</li></ul> |
102
- | [`storybook/csf-component`](./docs/rules/csf-component.md) | The component property should be set | | <ul><li>csf</li></ul> |
103
- | [`storybook/default-exports`](./docs/rules/default-exports.md) | Story files should have a default export | 🔧 | <ul><li>csf</li><li>recommended</li></ul> |
104
- | [`storybook/hierarchy-separator`](./docs/rules/hierarchy-separator.md) | Deprecated hierachy separator in title property | 🔧 | <ul><li>csf</li><li>recommended</li></ul> |
105
- | [`storybook/no-redundant-story-name`](./docs/rules/no-redundant-story-name.md) | A story should not have a redundant name property | 🔧 | <ul><li>csf</li><li>recommended</li></ul> |
106
- | [`storybook/no-stories-of`](./docs/rules/no-stories-of.md) | storiesOf is deprecated and should not be used | | <ul><li>csf-strict</li></ul> |
107
- | [`storybook/no-title-property-in-meta`](./docs/rules/no-title-property-in-meta.md) | Do not define a title in meta | 🔧 | <ul><li>csf-strict</li></ul> |
108
- | [`storybook/prefer-pascal-case`](./docs/rules/prefer-pascal-case.md) | Stories should use PascalCase | 🔧 | <ul><li>recommended</li></ul> |
109
- | [`storybook/story-exports`](./docs/rules/story-exports.md) | A story file must contain at least one story export | 🔧 | <ul><li>recommended</li><li>csf</li></ul> |
110
- | [`storybook/use-storybook-expect`](./docs/rules/use-storybook-expect.md) | Use expect from `@storybook/jest` | 🔧 | <ul><li>addon-interactions</li><li>recommended</li></ul> |
111
- | [`storybook/use-storybook-testing-library`](./docs/rules/use-storybook-testing-library.md) | Do not use testing-library directly on stories | 🔧 | <ul><li>addon-interactions</li><li>recommended</li></ul> |
99
+ | Name | Description | 🔧 | Included in configurations |
100
+ | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------- | --- | -------------------------------------------------------- |
101
+ | [`storybook/await-interactions`](./docs/rules/await-interactions.md) | Interactions should be awaited | 🔧 | <ul><li>addon-interactions</li><li>recommended</li></ul> |
102
+ | [`storybook/context-in-play-function`](./docs/rules/context-in-play-function.md) | Pass a context when invoking play function of another story | | <ul><li>recommended</li><li>addon-interactions</li></ul> |
103
+ | [`storybook/csf-component`](./docs/rules/csf-component.md) | The component property should be set | | <ul><li>csf</li></ul> |
104
+ | [`storybook/default-exports`](./docs/rules/default-exports.md) | Story files should have a default export | 🔧 | <ul><li>csf</li><li>recommended</li></ul> |
105
+ | [`storybook/hierarchy-separator`](./docs/rules/hierarchy-separator.md) | Deprecated hierachy separator in title property | 🔧 | <ul><li>csf</li><li>recommended</li></ul> |
106
+ | [`storybook/no-redundant-story-name`](./docs/rules/no-redundant-story-name.md) | A story should not have a redundant name property | 🔧 | <ul><li>csf</li><li>recommended</li></ul> |
107
+ | [`storybook/no-stories-of`](./docs/rules/no-stories-of.md) | storiesOf is deprecated and should not be used | | <ul><li>csf-strict</li></ul> |
108
+ | [`storybook/no-title-property-in-meta`](./docs/rules/no-title-property-in-meta.md) | Do not define a title in meta | 🔧 | <ul><li>csf-strict</li></ul> |
109
+ | [`storybook/prefer-pascal-case`](./docs/rules/prefer-pascal-case.md) | Stories should use PascalCase | 🔧 | <ul><li>recommended</li></ul> |
110
+ | [`storybook/story-exports`](./docs/rules/story-exports.md) | A story file must contain at least one story export | | <ul><li>recommended</li><li>csf</li></ul> |
111
+ | [`storybook/use-storybook-expect`](./docs/rules/use-storybook-expect.md) | Use expect from `@storybook/jest` | 🔧 | <ul><li>addon-interactions</li><li>recommended</li></ul> |
112
+ | [`storybook/use-storybook-testing-library`](./docs/rules/use-storybook-testing-library.md) | Do not use testing-library directly on stories | 🔧 | <ul><li>addon-interactions</li><li>recommended</li></ul> |
112
113
 
113
114
  <!-- RULES-LIST:END -->
114
115
 
@@ -3,10 +3,11 @@ module.exports = {
3
3
  plugins: ['storybook'],
4
4
  overrides: [
5
5
  {
6
- files: ['*.stories.@(ts|tsx|js|mjs|cjs)', '*.story.@(ts|tsx|js|mjs|cjs)'],
6
+ files: ['*.stories.@(ts|tsx|js|jsx|mjs|cjs)', '*.story.@(ts|tsx|js|jsx|mjs|cjs)'],
7
7
  rules: {
8
8
  'import/no-anonymous-default-export': 'off',
9
9
  'storybook/await-interactions': 'error',
10
+ 'storybook/context-in-play-function': 'error',
10
11
  'storybook/use-storybook-expect': 'error',
11
12
  'storybook/use-storybook-testing-library': 'error',
12
13
  },
@@ -3,7 +3,7 @@ module.exports = {
3
3
  plugins: ['storybook'],
4
4
  overrides: [
5
5
  {
6
- files: ['*.stories.@(ts|tsx|js|mjs|cjs)', '*.story.@(ts|tsx|js|mjs|cjs)'],
6
+ files: ['*.stories.@(ts|tsx|js|jsx|mjs|cjs)', '*.story.@(ts|tsx|js|jsx|mjs|cjs)'],
7
7
  rules: {
8
8
  'import/no-anonymous-default-export': 'off',
9
9
  'storybook/csf-component': 'warn',
@@ -3,10 +3,11 @@ module.exports = {
3
3
  plugins: ['storybook'],
4
4
  overrides: [
5
5
  {
6
- files: ['*.stories.@(ts|tsx|js|mjs|cjs)', '*.story.@(ts|tsx|js|mjs|cjs)'],
6
+ files: ['*.stories.@(ts|tsx|js|jsx|mjs|cjs)', '*.story.@(ts|tsx|js|jsx|mjs|cjs)'],
7
7
  rules: {
8
8
  'import/no-anonymous-default-export': 'off',
9
9
  'storybook/await-interactions': 'error',
10
+ 'storybook/context-in-play-function': 'error',
10
11
  'storybook/default-exports': 'error',
11
12
  'storybook/hierarchy-separator': 'warn',
12
13
  'storybook/no-redundant-story-name': 'warn',
@@ -21,6 +21,7 @@ module.exports = (0, create_storybook_rule_1.createStorybookRule)({
21
21
  },
22
22
  type: 'problem',
23
23
  fixable: 'code',
24
+ hasSuggestions: true,
24
25
  schema: [],
25
26
  },
26
27
  create(context) {
@@ -36,16 +37,27 @@ module.exports = (0, create_storybook_rule_1.createStorybookRule)({
36
37
  'waitForElement',
37
38
  'waitForDomChange',
38
39
  'userEvent',
40
+ 'play',
39
41
  ];
40
42
  const getMethodThatShouldBeAwaited = (expr) => {
41
43
  const shouldAwait = (name) => {
42
44
  return FUNCTIONS_TO_BE_AWAITED.includes(name) || name.startsWith('findBy');
43
45
  };
46
+ // When an expression is a return value it doesn't need to be awaited
47
+ if ((0, ast_1.isArrowFunctionExpression)(expr.parent) || (0, ast_1.isReturnStatement)(expr.parent)) {
48
+ return null;
49
+ }
44
50
  if ((0, ast_1.isMemberExpression)(expr.callee) &&
45
51
  (0, ast_1.isIdentifier)(expr.callee.object) &&
46
52
  shouldAwait(expr.callee.object.name)) {
47
53
  return expr.callee.object;
48
54
  }
55
+ if ((0, ast_1.isTSNonNullExpression)(expr.callee) &&
56
+ (0, ast_1.isMemberExpression)(expr.callee.expression) &&
57
+ (0, ast_1.isIdentifier)(expr.callee.expression.property) &&
58
+ shouldAwait(expr.callee.expression.property.name)) {
59
+ return expr.callee.expression.property;
60
+ }
49
61
  if ((0, ast_1.isMemberExpression)(expr.callee) &&
50
62
  (0, ast_1.isIdentifier)(expr.callee.property) &&
51
63
  shouldAwait(expr.callee.property.name)) {
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Pass a context object when invoking a play function
4
+ * @author Yann Braga
5
+ */
6
+ const create_storybook_rule_1 = require("../utils/create-storybook-rule");
7
+ const constants_1 = require("../utils/constants");
8
+ const ast_1 = require("../utils/ast");
9
+ module.exports = (0, create_storybook_rule_1.createStorybookRule)({
10
+ name: 'context-in-play-function',
11
+ defaultOptions: [],
12
+ meta: {
13
+ type: 'problem',
14
+ docs: {
15
+ description: 'Pass a context when invoking play function of another story',
16
+ categories: [constants_1.CategoryId.RECOMMENDED, constants_1.CategoryId.ADDON_INTERACTIONS],
17
+ recommended: 'error',
18
+ },
19
+ messages: {
20
+ passContextToPlayFunction: 'Pass a context when invoking play function of another story',
21
+ },
22
+ fixable: null,
23
+ schema: [],
24
+ },
25
+ create(context) {
26
+ // variables should be defined here
27
+ //----------------------------------------------------------------------
28
+ // Helpers
29
+ //----------------------------------------------------------------------
30
+ // any helper functions should go here or else delete this section
31
+ const isPlayFunctionFromAnotherStory = (expr) => {
32
+ if ((0, ast_1.isTSNonNullExpression)(expr.callee) &&
33
+ (0, ast_1.isMemberExpression)(expr.callee.expression) &&
34
+ (0, ast_1.isIdentifier)(expr.callee.expression.property) &&
35
+ expr.callee.expression.property.name === 'play') {
36
+ return true;
37
+ }
38
+ if ((0, ast_1.isMemberExpression)(expr.callee) &&
39
+ (0, ast_1.isIdentifier)(expr.callee.property) &&
40
+ expr.callee.property.name === 'play') {
41
+ return true;
42
+ }
43
+ return false;
44
+ };
45
+ // Expression passing an argument called context OR spreading a variable called context
46
+ const isNotPassingContextCorrectly = (expr) => {
47
+ const firstExpressionArgument = expr.arguments[0];
48
+ if (!firstExpressionArgument) {
49
+ return true;
50
+ }
51
+ if (expr.arguments.length === 1 &&
52
+ (0, ast_1.isIdentifier)(firstExpressionArgument) &&
53
+ firstExpressionArgument.name === 'context') {
54
+ return false;
55
+ }
56
+ if ((0, ast_1.isObjectExpression)(firstExpressionArgument) &&
57
+ firstExpressionArgument.properties.some((prop) => {
58
+ return ((0, ast_1.isSpreadElement)(prop) && (0, ast_1.isIdentifier)(prop.argument) && prop.argument.name === 'context');
59
+ })) {
60
+ return false;
61
+ }
62
+ return true;
63
+ };
64
+ //----------------------------------------------------------------------
65
+ // Public
66
+ //----------------------------------------------------------------------
67
+ let invocationsWithoutProperContext = [];
68
+ return {
69
+ CallExpression(node) {
70
+ if (isPlayFunctionFromAnotherStory(node) && isNotPassingContextCorrectly(node)) {
71
+ invocationsWithoutProperContext.push(node);
72
+ }
73
+ },
74
+ 'Program:exit': function () {
75
+ invocationsWithoutProperContext.forEach((node) => {
76
+ context.report({
77
+ node,
78
+ messageId: 'passContextToPlayFunction',
79
+ });
80
+ });
81
+ },
82
+ };
83
+ },
84
+ });
@@ -25,6 +25,7 @@ module.exports = (0, create_storybook_rule_1.createStorybookRule)({
25
25
  fixSuggestion: 'Add default export',
26
26
  },
27
27
  fixable: 'code',
28
+ hasSuggestions: true,
28
29
  schema: [],
29
30
  },
30
31
  create(context) {
@@ -13,6 +13,7 @@ module.exports = (0, create_storybook_rule_1.createStorybookRule)({
13
13
  meta: {
14
14
  type: 'problem',
15
15
  fixable: 'code',
16
+ hasSuggestions: true,
16
17
  docs: {
17
18
  description: 'Deprecated hierachy separator in title property',
18
19
  categories: [constants_1.CategoryId.CSF, constants_1.CategoryId.RECOMMENDED],
@@ -12,6 +12,7 @@ module.exports = (0, create_storybook_rule_1.createStorybookRule)({
12
12
  meta: {
13
13
  type: 'suggestion',
14
14
  fixable: 'code',
15
+ hasSuggestions: true,
15
16
  docs: {
16
17
  description: 'A story should not have a redundant name property',
17
18
  categories: [constants_1.CategoryId.CSF, constants_1.CategoryId.RECOMMENDED],
@@ -12,6 +12,7 @@ module.exports = (0, create_storybook_rule_1.createStorybookRule)({
12
12
  meta: {
13
13
  type: 'problem',
14
14
  fixable: 'code',
15
+ hasSuggestions: true,
15
16
  docs: {
16
17
  description: 'Do not define a title in meta',
17
18
  categories: [constants_1.CategoryId.CSF_STRICT],
@@ -15,6 +15,7 @@ module.exports = (0, create_storybook_rule_1.createStorybookRule)({
15
15
  meta: {
16
16
  type: 'suggestion',
17
17
  fixable: 'code',
18
+ hasSuggestions: true,
18
19
  docs: {
19
20
  description: 'Stories should use PascalCase',
20
21
  categories: [constants_1.CategoryId.RECOMMENDED],
@@ -22,7 +22,7 @@ module.exports = (0, create_storybook_rule_1.createStorybookRule)({
22
22
  shouldHaveStoryExport: 'The file should have at least one story export',
23
23
  addStoryExport: 'Add a story export',
24
24
  },
25
- fixable: 'code',
25
+ fixable: null,
26
26
  schema: [],
27
27
  },
28
28
  create(context) {
package/dist/utils/ast.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isTSAsExpression = exports.isTSInterfaceDeclaration = exports.isTSTypeAliasDeclaration = exports.isProgram = exports.isFunctionExpression = exports.isReturnStatement = exports.isProperty = exports.isObjectPattern = exports.isObjectExpression = exports.isNewExpression = exports.isMemberExpression = exports.isLiteral = exports.isJSXAttribute = exports.isImportSpecifier = exports.isImportNamespaceSpecifier = exports.isImportDefaultSpecifier = exports.isImportDeclaration = exports.isSequenceExpression = exports.isAssignmentExpression = exports.isVariableDeclaration = exports.isExpressionStatement = exports.isCallExpression = exports.isBlockStatement = exports.isArrowFunctionExpression = exports.isArrayExpression = exports.isVariableDeclarator = exports.isIdentifier = exports.isAwaitExpression = exports.ASTUtils = void 0;
3
+ exports.isTSNonNullExpression = exports.isTSAsExpression = exports.isTSInterfaceDeclaration = exports.isTSTypeAliasDeclaration = exports.isProgram = exports.isFunctionExpression = exports.isReturnStatement = exports.isSpreadElement = exports.isProperty = exports.isObjectPattern = exports.isObjectExpression = exports.isNewExpression = exports.isMemberExpression = exports.isLiteral = exports.isJSXAttribute = exports.isImportSpecifier = exports.isImportNamespaceSpecifier = exports.isImportDefaultSpecifier = exports.isImportDeclaration = exports.isSequenceExpression = exports.isAssignmentExpression = exports.isVariableDeclaration = exports.isExpressionStatement = exports.isCallExpression = exports.isBlockStatement = exports.isArrowFunctionExpression = exports.isArrayExpression = exports.isVariableDeclarator = exports.isIdentifier = exports.isAwaitExpression = exports.ASTUtils = void 0;
4
4
  const experimental_utils_1 = require("@typescript-eslint/experimental-utils");
5
5
  var experimental_utils_2 = require("@typescript-eslint/experimental-utils");
6
6
  Object.defineProperty(exports, "ASTUtils", { enumerable: true, get: function () { return experimental_utils_2.ASTUtils; } });
@@ -27,9 +27,11 @@ exports.isNewExpression = isNodeOfType(experimental_utils_1.AST_NODE_TYPES.NewEx
27
27
  exports.isObjectExpression = isNodeOfType(experimental_utils_1.AST_NODE_TYPES.ObjectExpression);
28
28
  exports.isObjectPattern = isNodeOfType(experimental_utils_1.AST_NODE_TYPES.ObjectPattern);
29
29
  exports.isProperty = isNodeOfType(experimental_utils_1.AST_NODE_TYPES.Property);
30
+ exports.isSpreadElement = isNodeOfType(experimental_utils_1.AST_NODE_TYPES.SpreadElement);
30
31
  exports.isReturnStatement = isNodeOfType(experimental_utils_1.AST_NODE_TYPES.ReturnStatement);
31
32
  exports.isFunctionExpression = isNodeOfType(experimental_utils_1.AST_NODE_TYPES.FunctionExpression);
32
33
  exports.isProgram = isNodeOfType(experimental_utils_1.AST_NODE_TYPES.Program);
33
34
  exports.isTSTypeAliasDeclaration = isNodeOfType(experimental_utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration);
34
35
  exports.isTSInterfaceDeclaration = isNodeOfType(experimental_utils_1.AST_NODE_TYPES.TSInterfaceDeclaration);
35
36
  exports.isTSAsExpression = isNodeOfType(experimental_utils_1.AST_NODE_TYPES.TSAsExpression);
37
+ exports.isTSNonNullExpression = isNodeOfType(experimental_utils_1.AST_NODE_TYPES.TSNonNullExpression);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-storybook",
3
- "version": "0.4.1",
3
+ "version": "0.5.2",
4
4
  "description": "Best practice rules for Storybook",
5
5
  "keywords": [
6
6
  "eslint",
@@ -71,7 +71,6 @@
71
71
  "ts-jest": "^27.0.7",
72
72
  "ts-migrate": "^0.1.26",
73
73
  "ts-node": "^10.4.0",
74
- "tsc": "^2.0.3",
75
74
  "typescript": "^4.4.4"
76
75
  },
77
76
  "engines": {