浏览代码

Upgrade ESLint configuration, with needed refactorings

- Change ESLint configuration syntax for ease of editing
- Autofix basic issues picked up by ESLint
- Make sure JS linting runs on TypeScript files
Thibaud Colas 3 年之前
父节点
当前提交
81ec3244d3
共有 51 个文件被更改,包括 149 次插入95 次删除
  1. 78 18
      .eslintrc.js
  2. 3 3
      client/src/api/admin.ts
  3. 1 1
      client/src/components/Button/Button.tsx
  4. 3 1
      client/src/components/CommentApp/components/Comment/index.tsx
  5. 1 1
      client/src/components/CommentApp/components/CommentHeader/index.tsx
  6. 1 1
      client/src/components/CommentApp/components/CommentReply/index.tsx
  7. 5 5
      client/src/components/CommentApp/main.tsx
  8. 1 1
      client/src/components/Draftail/sources/ModalWorkflowSource.js
  9. 1 1
      client/src/components/Explorer/Explorer.tsx
  10. 2 2
      client/src/components/Explorer/ExplorerHeader.tsx
  11. 1 1
      client/src/components/Explorer/ExplorerItem.tsx
  12. 1 1
      client/src/components/Explorer/ExplorerPanel.tsx
  13. 1 1
      client/src/components/Explorer/ExplorerToggle.tsx
  14. 1 1
      client/src/components/Explorer/PageCount.tsx
  15. 2 2
      client/src/components/Explorer/reducers/nodes.ts
  16. 1 1
      client/src/components/Icon/Icon.tsx
  17. 1 1
      client/src/components/PageExplorer/PageCount.tsx
  18. 1 1
      client/src/components/PageExplorer/PageExplorer.tsx
  19. 2 2
      client/src/components/PageExplorer/PageExplorerHeader.tsx
  20. 1 1
      client/src/components/PageExplorer/PageExplorerItem.tsx
  21. 1 1
      client/src/components/PageExplorer/PageExplorerPanel.tsx
  22. 2 2
      client/src/components/PageExplorer/reducers/nodes.ts
  23. 1 1
      client/src/components/Sidebar/SidebarPanel.tsx
  24. 1 1
      client/src/components/Sidebar/menu/LinkMenuItem.tsx
  25. 1 1
      client/src/components/Sidebar/menu/PageExplorerMenuItem.tsx
  26. 1 1
      client/src/components/Sidebar/menu/SubMenuItem.tsx
  27. 1 1
      client/src/components/Sidebar/modules/Search.tsx
  28. 1 1
      client/src/components/Sidebar/modules/WagtailBranding.tsx
  29. 2 2
      client/src/components/StreamField/blocks/StreamBlock.js
  30. 4 4
      client/src/components/StreamField/blocks/StructBlock.js
  31. 1 1
      client/src/custom.d.ts
  32. 2 2
      client/src/entrypoints/admin/comments.js
  33. 1 1
      client/src/entrypoints/admin/core.js
  34. 1 1
      client/src/entrypoints/admin/hallo-plugins/hallo-wagtaillink.js
  35. 0 4
      client/src/entrypoints/admin/modal-workflow.test.js
  36. 1 1
      client/src/entrypoints/admin/page-chooser.js
  37. 3 3
      client/src/entrypoints/admin/page-editor.js
  38. 1 1
      client/src/entrypoints/admin/privacy-switch.js
  39. 1 1
      client/src/entrypoints/admin/task-chooser.js
  40. 1 1
      client/src/entrypoints/admin/telepath/widgets.js
  41. 2 2
      client/src/entrypoints/admin/workflow-action.js
  42. 1 1
      client/src/entrypoints/admin/workflow-status.js
  43. 1 1
      client/src/entrypoints/contrib/table_block/table.js
  44. 2 2
      client/src/entrypoints/contrib/table_block/table.test.js
  45. 1 1
      client/src/entrypoints/documents/document-chooser.js
  46. 1 1
      client/src/entrypoints/images/image-chooser.js
  47. 1 1
      client/src/entrypoints/snippets/snippet-chooser.js
  48. 1 1
      client/src/utils/cleanForSlug.js
  49. 0 5
      client/tests/integration/.eslintrc.js
  50. 3 2
      package.json
  51. 1 1
      wagtail/contrib/modeladmin/static_src/wagtailmodeladmin/js/prepopulate.js

+ 78 - 18
.eslintrc → .eslintrc.js

@@ -1,23 +1,66 @@
-{
+// Rules which have been enforced in configuration upgrades and flag issues in existing code.
+// We need to consider whether to disable those rules permanently, or fix the issues.
+const legacyCode = {
+  "class-methods-use-this": "off",
+  "constructor-super": "off",
+  "default-param-last": "off",
+  "import/extensions": "off",
+  "import/first": "off",
+  "import/newline-after-import": "off",
+  "import/no-extraneous-dependencies": "off",
+  "import/no-unresolved": "off",
+  "import/no-useless-path-segments": "off",
+  "import/order": "off",
+  "import/prefer-default-export": "off",
+  "jsx-a11y/alt-text": "off",
+  "jsx-a11y/anchor-is-valid": "off",
+  "jsx-a11y/click-events-have-key-events": "off",
+  "jsx-a11y/interactive-supports-focus": "off",
+  "jsx-a11y/no-noninteractive-element-interactions": "off",
+  "jsx-a11y/role-supports-aria-props": "off",
+  "lines-between-class-members": "off",
+  "max-classes-per-file": "off",
+  "no-await-in-loop": "off",
+  "no-continue": "off",
+  "no-else-return": "off",
+  "no-extra-boolean-cast": "off",
+  "no-import-assign": "off",
+  "no-lonely-if": "off",
+  "no-plusplus": "off",
+  "no-prototype-builtins": "off",
+  "no-restricted-syntax": "off",
+  "no-this-before-super": "off",
+  "operator-assignment": "off",
+  "prefer-destructuring": "off",
+  "prefer-object-spread": "off",
+  "prefer-promise-reject-errors": "off",
+  "react-hooks/exhaustive-deps": "off",
+  "react-hooks/rules-of-hooks": "off",
+  "react/button-has-type": "off",
+  "react/destructuring-assignment": "off",
+  "react/forbid-prop-types": "off",
+  "react/function-component-definition": "off",
+  "react/jsx-curly-brace-presence": "off",
+  "react/jsx-filename-extension": "off",
+  "react/jsx-no-useless-fragment": "off",
+  "react/jsx-props-no-spreading": "off",
+  "react/no-danger": "off",
+  "react/no-deprecated": "off",
+  "react/require-default-props": "off",
+}
+
+module.exports = {
+  "extends": [
+    "@wagtail/eslint-config-wagtail",
+    "plugin:@typescript-eslint/recommended"
+  ],
   "parser": "@typescript-eslint/parser",
   "parser": "@typescript-eslint/parser",
   "plugins": [
   "plugins": [
     "@typescript-eslint"
     "@typescript-eslint"
   ],
   ],
-  "extends": [
-    "wagtail",
-    "plugin:@typescript-eslint/recommended"
-  ],
-
   "env": {
   "env": {
-    "jest": true
+    "jest": true,
-  },
+    "browser": true,
-
-  "settings": {
-    "import/resolver": {
-      "webpack": {
-        "config": "client/webpack.config.js"
-      }
-    }
   },
   },
   "rules": {
   "rules": {
     "no-underscore-dangle": ["error", { "allow": ["__REDUX_DEVTOOLS_EXTENSION__"] }],
     "no-underscore-dangle": ["error", { "allow": ["__REDUX_DEVTOOLS_EXTENSION__"] }],
@@ -28,9 +71,26 @@
     "@typescript-eslint/explicit-module-boundary-types": "off",
     "@typescript-eslint/explicit-module-boundary-types": "off",
     "@typescript-eslint/explicit-member-accessibility": "off",
     "@typescript-eslint/explicit-member-accessibility": "off",
     "@typescript-eslint/explicit-function-return-type": "off",
     "@typescript-eslint/explicit-function-return-type": "off",
-    "@typescript-eslint/no-explicit-any": "off"
+    "@typescript-eslint/no-explicit-any": "off",
+    'react/jsx-filename-extension': [
+      "error",
+      { extensions: ['.js', '.tsx'] },
+    ],
+    'import/extensions': [
+      "error",
+      'always',
+      {
+        ignorePackages: true,
+        pattern: {
+          js: 'never',
+          jsx: 'never',
+          ts: 'never',
+          tsx: 'never',
+        },
+      },
+    ],
+    ...legacyCode,
   },
   },
-
   "overrides": [
   "overrides": [
     {
     {
       // Rules we don’t want to enforce for test and tooling code.
       // Rules we don’t want to enforce for test and tooling code.
@@ -45,6 +105,7 @@
       "globals": { "$": "readonly" }
       "globals": { "$": "readonly" }
     },
     },
     {
     {
+      "files": ["wagtail/**/**"],
       "globals": {
       "globals": {
         "$": "readonly",
         "$": "readonly",
         "addMessage": "readonly",
         "addMessage": "readonly",
@@ -61,7 +122,6 @@
         "QUERY_CHOOSER_MODAL_ONLOAD_HANDLERS": "writable",
         "QUERY_CHOOSER_MODAL_ONLOAD_HANDLERS": "writable",
         "SNIPPET_CHOOSER_MODAL_ONLOAD_HANDLERS": "writable"
         "SNIPPET_CHOOSER_MODAL_ONLOAD_HANDLERS": "writable"
       },
       },
-      "files": ["wagtail/**/**"],
       "rules": {
       "rules": {
         "@typescript-eslint/no-unused-vars": "off",
         "@typescript-eslint/no-unused-vars": "off",
         "@typescript-eslint/no-use-before-define": "off",
         "@typescript-eslint/no-use-before-define": "off",

+ 3 - 3
client/src/api/admin.ts

@@ -8,7 +8,7 @@ export interface WagtailPageAPI {
     status: {
     status: {
       status: string;
       status: string;
       live: boolean;
       live: boolean;
-      /* eslint-disable-next-line camelcase */
+       
       has_unpublished_changes: boolean;
       has_unpublished_changes: boolean;
     }
     }
     children: any;
     children: any;
@@ -18,13 +18,13 @@ export interface WagtailPageAPI {
     locale?: string;
     locale?: string;
     translations?: any;
     translations?: any;
   };
   };
-  /* eslint-disable-next-line camelcase */
+   
   admin_display_title?: string;
   admin_display_title?: string;
 }
 }
 
 
 interface WagtailPageListAPI {
 interface WagtailPageListAPI {
   meta: {
   meta: {
-    /* eslint-disable-next-line camelcase */
+     
     total_count: number;
     total_count: number;
   };
   };
   items: WagtailPageAPI[];
   items: WagtailPageAPI[];

+ 1 - 1
client/src/components/Button/Button.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import * as React from 'react';
 import * as React from 'react';
 
 

+ 3 - 1
client/src/components/CommentApp/components/Comment/index.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+
 
 
 import React from 'react';
 import React from 'react';
 import ReactDOM from 'react-dom';
 import ReactDOM from 'react-dom';
@@ -639,6 +639,7 @@ export default class CommentComponent extends React.Component<CommentProps> {
   }
   }
 
 
   componentDidMount() {
   componentDidMount() {
+    // eslint-disable-next-line react/no-find-dom-node
     const element = ReactDOM.findDOMNode(this);
     const element = ReactDOM.findDOMNode(this);
 
 
     if (element instanceof HTMLElement) {
     if (element instanceof HTMLElement) {
@@ -658,6 +659,7 @@ export default class CommentComponent extends React.Component<CommentProps> {
   }
   }
 
 
   componentDidUpdate() {
   componentDidUpdate() {
+    // eslint-disable-next-line react/no-find-dom-node
     const element = ReactDOM.findDOMNode(this);
     const element = ReactDOM.findDOMNode(this);
 
 
     // Keep height up to date so that other comments will be moved out of the way
     // Keep height up to date so that other comments will be moved out of the way

+ 1 - 1
client/src/components/CommentApp/components/CommentHeader/index.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import dateFormat from 'dateformat';
 import dateFormat from 'dateformat';
 import React, { FunctionComponent, useState, useEffect, useRef } from 'react';
 import React, { FunctionComponent, useState, useEffect, useRef } from 'react';

+ 1 - 1
client/src/components/CommentApp/components/CommentReply/index.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import React from 'react';
 import React from 'react';
 
 

+ 5 - 5
client/src/components/CommentApp/main.tsx

@@ -72,7 +72,7 @@ export const defaultStrings = {
   SAVE_PAGE_TO_SAVE_REPLY: 'Save the page to save this reply',
   SAVE_PAGE_TO_SAVE_REPLY: 'Save the page to save this reply',
 };
 };
 
 
-/* eslint-disable camelcase */
+ 
 // This is done as this is serialized pretty directly from the Django model
 // This is done as this is serialized pretty directly from the Django model
 export interface InitialCommentReply {
 export interface InitialCommentReply {
   pk: number;
   pk: number;
@@ -97,7 +97,7 @@ export interface InitialComment {
 }
 }
 /* eslint-enable */
 /* eslint-enable */
 
 
-// eslint-disable-next-line camelcase
+ 
 const getAuthor = (authors: Map<string, {name: string, avatar_url: string}>, id: any): Author => {
 const getAuthor = (authors: Map<string, {name: string, avatar_url: string}>, id: any): Author => {
   const authorData = getOrDefault(authors, String(id), { name: '', avatar_url: '' });
   const authorData = getOrDefault(authors, String(id), { name: '', avatar_url: '' });
 
 
@@ -165,7 +165,7 @@ export class CommentApp {
     });
     });
     this.layout = new LayoutController();
     this.layout = new LayoutController();
   }
   }
-  // eslint-disable-next-line camelcase
+   
   setUser(userId: any, authors: Map<string, {name: string, avatar_url: string}>) {
   setUser(userId: any, authors: Map<string, {name: string, avatar_url: string}>) {
     this.store.dispatch(
     this.store.dispatch(
       updateGlobalSettings({
       updateGlobalSettings({
@@ -237,7 +237,7 @@ export class CommentApp {
     outputElement: HTMLElement,
     outputElement: HTMLElement,
     userId: any,
     userId: any,
     initialComments: InitialComment[],
     initialComments: InitialComment[],
-    // eslint-disable-next-line camelcase
+     
     authors: Map<string, {name: string, avatar_url: string}>,
     authors: Map<string, {name: string, avatar_url: string}>,
     translationStrings: TranslatableStrings | null
     translationStrings: TranslatableStrings | null
   ) {
   ) {
@@ -338,7 +338,7 @@ export class CommentApp {
       }
       }
 
 
       // If this is the initial focused comment. Focus and pin it
       // If this is the initial focused comment. Focus and pin it
-      // eslint-disable-next-line no-warning-comments
+       
       // TODO: Scroll to this comment
       // TODO: Scroll to this comment
       if (initialFocusedCommentId && comment.pk === initialFocusedCommentId) {
       if (initialFocusedCommentId && comment.pk === initialFocusedCommentId) {
         this.store.dispatch(setFocusedComment(commentId, { updatePinnedComment: true, forceFocus: true }));
         this.store.dispatch(setFocusedComment(commentId, { updatePinnedComment: true, forceFocus: true }));

+ 1 - 1
client/src/components/Draftail/sources/ModalWorkflowSource.js

@@ -46,7 +46,7 @@ class ModalWorkflowSource extends Component {
 
 
     $(document.body).on('hidden.bs.modal', this.onClose);
     $(document.body).on('hidden.bs.modal', this.onClose);
 
 
-    // eslint-disable-next-line new-cap
+     
     this.workflow = global.ModalWorkflow({
     this.workflow = global.ModalWorkflow({
       url,
       url,
       urlParams,
       urlParams,

+ 1 - 1
client/src/components/Explorer/Explorer.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import React from 'react';
 import React from 'react';
 import { connect } from 'react-redux';
 import { connect } from 'react-redux';

+ 2 - 2
client/src/components/Explorer/ExplorerHeader.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import React from 'react';
 import React from 'react';
 import { ADMIN_URLS, STRINGS } from '../../config/wagtailConfig';
 import { ADMIN_URLS, STRINGS } from '../../config/wagtailConfig';
@@ -30,7 +30,7 @@ const SelectLocale: React.FunctionComponent<SelectLocaleProps> = ({ locale, tran
   return (
   return (
     <div className="c-explorer__header__select">
     <div className="c-explorer__header__select">
       <select value={locale} onChange={onChange} disabled={options.length < 2}>{options}</select>
       <select value={locale} onChange={onChange} disabled={options.length < 2}>{options}</select>
-      <span></span>
+      <span />
     </div>
     </div>
   );
   );
 };
 };

+ 1 - 1
client/src/components/Explorer/ExplorerItem.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import React from 'react';
 import React from 'react';
 
 

+ 1 - 1
client/src/components/Explorer/ExplorerPanel.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import React from 'react';
 import React from 'react';
 import FocusTrap from 'focus-trap-react';
 import FocusTrap from 'focus-trap-react';

+ 1 - 1
client/src/components/Explorer/ExplorerToggle.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import React from 'react';
 import React from 'react';
 import { connect } from 'react-redux';
 import { connect } from 'react-redux';

+ 1 - 1
client/src/components/Explorer/PageCount.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import React from 'react';
 import React from 'react';
 
 

+ 2 - 2
client/src/components/Explorer/reducers/nodes.ts

@@ -66,7 +66,7 @@ interface GetChildrenSuccess {
   payload: {
   payload: {
     id: number;
     id: number;
     meta: {
     meta: {
-      /* eslint-disable-next-line camelcase */
+       
       total_count: number;
       total_count: number;
     };
     };
     items: WagtailPageAPI[];
     items: WagtailPageAPI[];
@@ -87,7 +87,7 @@ interface GetTranslationsSuccess {
   payload: {
   payload: {
     id: number;
     id: number;
     meta: {
     meta: {
-      /* eslint-disable-next-line camelcase */
+       
       total_count: number;
       total_count: number;
     };
     };
     items: WagtailPageAPI[];
     items: WagtailPageAPI[];

+ 1 - 1
client/src/components/Icon/Icon.tsx

@@ -12,7 +12,7 @@ export interface IconProps {
 const Icon: React.FunctionComponent<IconProps> = ({ name, className, title }) => (
 const Icon: React.FunctionComponent<IconProps> = ({ name, className, title }) => (
   <>
   <>
     <svg className={`icon icon-${name} ${className || ''}`} aria-hidden="true">
     <svg className={`icon icon-${name} ${className || ''}`} aria-hidden="true">
-      <use href={`#icon-${name}`}></use>
+      <use href={`#icon-${name}`} />
     </svg>
     </svg>
     {title &&
     {title &&
       <span className="visuallyhidden">
       <span className="visuallyhidden">

+ 1 - 1
client/src/components/PageExplorer/PageCount.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import React from 'react';
 import React from 'react';
 
 

+ 1 - 1
client/src/components/PageExplorer/PageExplorer.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import React from 'react';
 import React from 'react';
 import { connect } from 'react-redux';
 import { connect } from 'react-redux';

+ 2 - 2
client/src/components/PageExplorer/PageExplorerHeader.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import React from 'react';
 import React from 'react';
 import { ADMIN_URLS, STRINGS } from '../../config/wagtailConfig';
 import { ADMIN_URLS, STRINGS } from '../../config/wagtailConfig';
@@ -30,7 +30,7 @@ const SelectLocale: React.FunctionComponent<SelectLocaleProps> = ({ locale, tran
   return (
   return (
     <div className="c-page-explorer__header__select">
     <div className="c-page-explorer__header__select">
       <select value={locale} onChange={onChange} disabled={options.length < 2}>{options}</select>
       <select value={locale} onChange={onChange} disabled={options.length < 2}>{options}</select>
-      <span></span>
+      <span />
     </div>
     </div>
   );
   );
 };
 };

+ 1 - 1
client/src/components/PageExplorer/PageExplorerItem.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import React from 'react';
 import React from 'react';
 
 

+ 1 - 1
client/src/components/PageExplorer/PageExplorerPanel.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+
 
 
 import React from 'react';
 import React from 'react';
 
 

+ 2 - 2
client/src/components/PageExplorer/reducers/nodes.ts

@@ -66,7 +66,7 @@ interface GetChildrenSuccess {
   payload: {
   payload: {
     id: number;
     id: number;
     meta: {
     meta: {
-      /* eslint-disable-next-line camelcase */
+       
       total_count: number;
       total_count: number;
     };
     };
     items: WagtailPageAPI[];
     items: WagtailPageAPI[];
@@ -87,7 +87,7 @@ interface GetTranslationsSuccess {
   payload: {
   payload: {
     id: number;
     id: number;
     meta: {
     meta: {
-      /* eslint-disable-next-line camelcase */
+       
       total_count: number;
       total_count: number;
     };
     };
     items: WagtailPageAPI[];
     items: WagtailPageAPI[];

+ 1 - 1
client/src/components/Sidebar/SidebarPanel.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import * as React from 'react';
 import * as React from 'react';
 
 

+ 1 - 1
client/src/components/Sidebar/menu/LinkMenuItem.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import * as React from 'react';
 import * as React from 'react';
 
 

+ 1 - 1
client/src/components/Sidebar/menu/PageExplorerMenuItem.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import * as React from 'react';
 import * as React from 'react';
 
 

+ 1 - 1
client/src/components/Sidebar/menu/SubMenuItem.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import * as React from 'react';
 import * as React from 'react';
 
 

+ 1 - 1
client/src/components/Sidebar/modules/Search.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import * as React from 'react';
 import * as React from 'react';
 
 

+ 1 - 1
client/src/components/Sidebar/modules/WagtailBranding.tsx

@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types */
+ 
 
 
 import * as React from 'react';
 import * as React from 'react';
 import { ModuleDefinition, Strings } from '../Sidebar';
 import { ModuleDefinition, Strings } from '../Sidebar';

+ 2 - 2
client/src/components/StreamField/blocks/StreamBlock.js

@@ -237,7 +237,7 @@ export class StreamBlock extends BaseSequenceBlock {
     // If we can add blocks, check if there are any block types that have count limits
     // If we can add blocks, check if there are any block types that have count limits
     this.disabledBlockTypes = new Set();
     this.disabledBlockTypes = new Set();
     if (this.canAddBlock) {
     if (this.canAddBlock) {
-      // eslint-disable-next-line no-restricted-syntax
+       
       for (const blockType in this.blockDef.meta.blockCounts) {
       for (const blockType in this.blockDef.meta.blockCounts) {
         if (this.blockDef.meta.blockCounts.hasOwnProperty(blockType)) {
         if (this.blockDef.meta.blockCounts.hasOwnProperty(blockType)) {
           const counts = this.blockDef.meta.blockCounts[blockType];
           const counts = this.blockDef.meta.blockCounts[blockType];
@@ -332,7 +332,7 @@ export class StreamBlock extends BaseSequenceBlock {
     }
     }
 
 
     // Block errors
     // Block errors
-    // eslint-disable-next-line no-restricted-syntax
+     
     for (const blockIndex in error.blockErrors) {
     for (const blockIndex in error.blockErrors) {
       if (error.blockErrors.hasOwnProperty(blockIndex)) {
       if (error.blockErrors.hasOwnProperty(blockIndex)) {
         this.children[blockIndex].setError(error.blockErrors[blockIndex]);
         this.children[blockIndex].setError(error.blockErrors[blockIndex]);

+ 4 - 4
client/src/components/StreamField/blocks/StructBlock.js

@@ -75,7 +75,7 @@ export class StructBlock {
   }
   }
 
 
   setState(state) {
   setState(state) {
-    // eslint-disable-next-line guard-for-in, no-restricted-syntax
+    // eslint-disable-next-line guard-for-in
     for (const name in state) {
     for (const name in state) {
       this.childBlocks[name].setState(state[name]);
       this.childBlocks[name].setState(state[name]);
     }
     }
@@ -87,7 +87,7 @@ export class StructBlock {
     }
     }
     const error = errorList[0];
     const error = errorList[0];
 
 
-    // eslint-disable-next-line no-restricted-syntax
+     
     for (const blockName in error.blockErrors) {
     for (const blockName in error.blockErrors) {
       if (error.blockErrors.hasOwnProperty(blockName)) {
       if (error.blockErrors.hasOwnProperty(blockName)) {
         this.childBlocks[blockName].setError(error.blockErrors[blockName]);
         this.childBlocks[blockName].setError(error.blockErrors[blockName]);
@@ -97,7 +97,7 @@ export class StructBlock {
 
 
   getState() {
   getState() {
     const state = {};
     const state = {};
-    // eslint-disable-next-line guard-for-in, no-restricted-syntax
+    // eslint-disable-next-line guard-for-in
     for (const name in this.childBlocks) {
     for (const name in this.childBlocks) {
       state[name] = this.childBlocks[name].getState();
       state[name] = this.childBlocks[name].getState();
     }
     }
@@ -106,7 +106,7 @@ export class StructBlock {
 
 
   getValue() {
   getValue() {
     const value = {};
     const value = {};
-    // eslint-disable-next-line guard-for-in, no-restricted-syntax
+    // eslint-disable-next-line guard-for-in
     for (const name in this.childBlocks) {
     for (const name in this.childBlocks) {
       value[name] = this.childBlocks[name].getValue();
       value[name] = this.childBlocks[name].getValue();
     }
     }

+ 1 - 1
client/src/custom.d.ts

@@ -23,7 +23,7 @@ declare global {
         I18N_ENABLED: boolean;
         I18N_ENABLED: boolean;
         LOCALES: {
         LOCALES: {
             code: string;
             code: string;
-            /* eslint-disable-next-line camelcase */
+             
             display_name: string;
             display_name: string;
         }[];
         }[];
         STRINGS: any;
         STRINGS: any;

+ 2 - 2
client/src/entrypoints/admin/comments.js

@@ -112,7 +112,7 @@ window.comments = (() => {
     onUnfocus() {
     onUnfocus() {
       this.node.classList.add('button-secondary');
       this.node.classList.add('button-secondary');
       this.node.ariaLabel = STRINGS.FOCUS_COMMENT;
       this.node.ariaLabel = STRINGS.FOCUS_COMMENT;
-      // eslint-disable-next-line no-warning-comments
+       
       // TODO: ensure comment is focused accessibly when this is clicked,
       // TODO: ensure comment is focused accessibly when this is clicked,
       // and that screenreader users can return to the annotation point when desired
       // and that screenreader users can return to the annotation point when desired
     }
     }
@@ -214,7 +214,7 @@ window.comments = (() => {
           }
           }
         }
         }
       });
       });
-      // eslint-disable-next-line no-warning-comments
+       
       return unsubscribeWidget; // TODO: listen for widget deletion and use this
       return unsubscribeWidget; // TODO: listen for widget deletion and use this
     }
     }
     updateVisibility(newShown) {
     updateVisibility(newShown) {

+ 1 - 1
client/src/entrypoints/admin/core.js

@@ -571,7 +571,7 @@ const DropDownController = {
 function DropDown(el, registry) {
 function DropDown(el, registry) {
   if (!el || !registry) {
   if (!el || !registry) {
     if ('error' in console) {
     if ('error' in console) {
-      // eslint-disable-next-line max-len, no-console
+      // eslint-disable-next-line no-console
       console.error('A dropdown was created without an element or the DropDownController.\nMake sure to pass both to your component.');
       console.error('A dropdown was created without an element or the DropDownController.\nMake sure to pass both to your component.');
       return;
       return;
     }
     }

+ 1 - 1
client/src/entrypoints/admin/hallo-plugins/hallo-wagtaillink.js

@@ -73,7 +73,7 @@ $.widget('IKS.hallowagtaillink', {
         urlParams.link_text = lastSelection.toString();
         urlParams.link_text = lastSelection.toString();
       }
       }
 
 
-      // eslint-disable-next-line no-undef, new-cap
+      // eslint-disable-next-line no-undef
       return ModalWorkflow({
       return ModalWorkflow({
         url: url,
         url: url,
         urlParams: urlParams,
         urlParams: urlParams,

+ 0 - 4
client/src/entrypoints/admin/modal-workflow.test.js

@@ -24,7 +24,6 @@ describe('modal-workflow', () => {
     let modalWorkflow;
     let modalWorkflow;
 
 
     const openModal = () => {
     const openModal = () => {
-      // eslint-disable-next-line new-cap
       modalWorkflow = window.ModalWorkflow({ url: 'path/to/endpoint' });
       modalWorkflow = window.ModalWorkflow({ url: 'path/to/endpoint' });
     };
     };
 
 
@@ -53,7 +52,6 @@ describe('modal-workflow', () => {
     let modalWorkflow;
     let modalWorkflow;
 
 
     const openModal = () => {
     const openModal = () => {
-      // eslint-disable-next-line new-cap
       modalWorkflow = window.ModalWorkflow({ url: 'path/to/endpoint' });
       modalWorkflow = window.ModalWorkflow({ url: 'path/to/endpoint' });
     };
     };
 
 
@@ -81,7 +79,6 @@ describe('modal-workflow', () => {
     let modalWorkflow;
     let modalWorkflow;
 
 
     const openModal = () => {
     const openModal = () => {
-      // eslint-disable-next-line new-cap
       modalWorkflow = window.ModalWorkflow({ url: 'path/to/endpoint' });
       modalWorkflow = window.ModalWorkflow({ url: 'path/to/endpoint' });
     };
     };
 
 
@@ -109,7 +106,6 @@ describe('modal-workflow', () => {
     let modalWorkflow;
     let modalWorkflow;
 
 
     const openModal = () => {
     const openModal = () => {
-      // eslint-disable-next-line new-cap
       modalWorkflow = window.ModalWorkflow({
       modalWorkflow = window.ModalWorkflow({
         url: 'path/to/endpoint',
         url: 'path/to/endpoint',
         urlParams,
         urlParams,

+ 1 - 1
client/src/entrypoints/admin/page-chooser.js

@@ -72,7 +72,7 @@ function createPageChooser(id, openAtParentId, options) {
       if (options.user_perms) {
       if (options.user_perms) {
         urlParams.user_perms = options.user_perms;
         urlParams.user_perms = options.user_perms;
       }
       }
-      // eslint-disable-next-line no-undef, new-cap
+      // eslint-disable-next-line no-undef
       ModalWorkflow({
       ModalWorkflow({
         url: url,
         url: url,
         urlParams: urlParams,
         urlParams: urlParams,

+ 3 - 3
client/src/entrypoints/admin/page-editor.js

@@ -233,7 +233,7 @@ function initErrorDetection() {
   });
   });
 
 
   // now identify them on each tab
   // now identify them on each tab
-  // eslint-disable-next-line no-restricted-syntax, guard-for-in
+  // eslint-disable-next-line guard-for-in
   for (const index in errorSections) {
   for (const index in errorSections) {
     $('[data-tab-nav] a[href="#' + index + '"]').addClass('errors').attr('data-count', errorSections[index]);
     $('[data-tab-nav] a[href="#' + index + '"]').addClass('errors').attr('data-count', errorSections[index]);
   }
   }
@@ -296,7 +296,7 @@ $(() => {
       // and deleted (DOMSubtreeModified event), and we need to delay
       // and deleted (DOMSubtreeModified event), and we need to delay
       // setPreviewData when typing to avoid useless extra AJAX requests
       // setPreviewData when typing to avoid useless extra AJAX requests
       // (so we postpone setPreviewData when keyup occurs).
       // (so we postpone setPreviewData when keyup occurs).
-      // eslint-disable-next-line no-warning-comments
+       
       // TODO: Replace DOMSubtreeModified with a MutationObserver.
       // TODO: Replace DOMSubtreeModified with a MutationObserver.
       $form.on('change keyup DOMSubtreeModified', () => {
       $form.on('change keyup DOMSubtreeModified', () => {
         clearTimeout(autoUpdatePreviewDataTimeout);
         clearTimeout(autoUpdatePreviewDataTimeout);
@@ -321,7 +321,7 @@ $(() => {
       } else {
       } else {
         window.focus();
         window.focus();
         previewWindow.close();
         previewWindow.close();
-        // eslint-disable-next-line no-warning-comments
+         
         // TODO: Stop sending the form, as it removes file data.
         // TODO: Stop sending the form, as it removes file data.
         $form.trigger('submit');
         $form.trigger('submit');
       }
       }

+ 1 - 1
client/src/entrypoints/admin/privacy-switch.js

@@ -4,7 +4,7 @@ $(() => {
   /* Interface to set permissions from the explorer / editor */
   /* Interface to set permissions from the explorer / editor */
   // eslint-disable-next-line func-names
   // eslint-disable-next-line func-names
   $('button.action-set-privacy').on('click', function () {
   $('button.action-set-privacy').on('click', function () {
-    // eslint-disable-next-line no-undef, new-cap
+    // eslint-disable-next-line no-undef
     ModalWorkflow({
     ModalWorkflow({
       url: this.getAttribute('data-url'),
       url: this.getAttribute('data-url'),
       onload: {
       onload: {

+ 1 - 1
client/src/entrypoints/admin/task-chooser.js

@@ -7,7 +7,7 @@ function createTaskChooser(id) {
   const editAction = chooserElement.find('.action-edit');
   const editAction = chooserElement.find('.action-edit');
 
 
   $('.action-choose', chooserElement).on('click', () => {
   $('.action-choose', chooserElement).on('click', () => {
-    // eslint-disable-next-line no-undef, new-cap
+    // eslint-disable-next-line no-undef
     ModalWorkflow({
     ModalWorkflow({
       url: chooserElement.data('chooserUrl'),
       url: chooserElement.data('chooserUrl'),
       // eslint-disable-next-line no-undef
       // eslint-disable-next-line no-undef

+ 1 - 1
client/src/entrypoints/admin/telepath/widgets.js

@@ -1,4 +1,4 @@
-/* eslint-disable indent */
+ 
 
 
 /* global $ */
 /* global $ */
 
 

+ 2 - 2
client/src/entrypoints/admin/workflow-action.js

@@ -20,7 +20,7 @@ function ActivateWorkflowActionsForDashboard(csrfToken) {
       e.stopPropagation();
       e.stopPropagation();
 
 
       if ('launchModal' in buttonElement.dataset) {
       if ('launchModal' in buttonElement.dataset) {
-        // eslint-disable-next-line no-undef, new-cap
+        // eslint-disable-next-line no-undef
         ModalWorkflow({
         ModalWorkflow({
           url: buttonElement.dataset.workflowActionUrl,
           url: buttonElement.dataset.workflowActionUrl,
           onload: {
           onload: {
@@ -68,7 +68,7 @@ function ActivateWorkflowActionsForEditView(formSelector) {
         e.stopPropagation();
         e.stopPropagation();
 
 
         // open the modal at the given URL
         // open the modal at the given URL
-        // eslint-disable-next-line no-undef, new-cap
+        // eslint-disable-next-line no-undef
         ModalWorkflow({
         ModalWorkflow({
           url: buttonElement.dataset.workflowActionModalUrl,
           url: buttonElement.dataset.workflowActionModalUrl,
           onload: {
           onload: {

+ 1 - 1
client/src/entrypoints/admin/workflow-status.js

@@ -4,7 +4,7 @@ $(() => {
   /* Interface to view the workflow status from the explorer / editor */
   /* Interface to view the workflow status from the explorer / editor */
   // eslint-disable-next-line func-names
   // eslint-disable-next-line func-names
   $('button.action-workflow-status').on('click', function () {
   $('button.action-workflow-status').on('click', function () {
-    // eslint-disable-next-line no-undef, new-cap
+    // eslint-disable-next-line no-undef
     ModalWorkflow({
     ModalWorkflow({
       url: this.getAttribute('data-url'),
       url: this.getAttribute('data-url'),
     });
     });

+ 1 - 1
client/src/entrypoints/contrib/table_block/table.js

@@ -209,7 +209,7 @@ class TableInput {
             <input type="text" id="${id}-handsontable-col-caption" name="handsontable-col-caption" />
             <input type="text" id="${id}-handsontable-col-caption" name="handsontable-col-caption" />
           </div>
           </div>
           <p class="help">
           <p class="help">
-            ${this.strings['A heading that identifies the overall topic of the table, and is useful for screen reader users'] /* eslint-disable-line max-len */}
+            ${this.strings['A heading that identifies the overall topic of the table, and is useful for screen reader users']  }
           </p>
           </p>
         </div>
         </div>
       </div>
       </div>

+ 2 - 2
client/src/entrypoints/contrib/table_block/table.test.js

@@ -37,7 +37,7 @@ const TEST_STRINGS = {
   'Column header': 'Column header',
   'Column header': 'Column header',
   'Display the first column as a header.': 'Display the first column as a header.',
   'Display the first column as a header.': 'Display the first column as a header.',
   'Table caption': 'Table caption',
   'Table caption': 'Table caption',
-  // eslint-disable-next-line max-len
+   
   'A heading that identifies the overall topic of the table, and is useful for screen reader users': 'A heading that identifies the overall topic of the table, and is useful for screen reader users',
   'A heading that identifies the overall topic of the table, and is useful for screen reader users': 'A heading that identifies the overall topic of the table, and is useful for screen reader users',
   'Table': 'Table'
   'Table': 'Table'
 };
 };
@@ -127,7 +127,7 @@ describe('telepath: wagtail.widgets.TableInput', () => {
       'Column header': 'En-tête de colonne',
       'Column header': 'En-tête de colonne',
       'Display the first column as a header.': 'Affichez la première colonne sous forme d\'en-tête.',
       'Display the first column as a header.': 'Affichez la première colonne sous forme d\'en-tête.',
       'Table caption': 'Légende du tableau',
       'Table caption': 'Légende du tableau',
-      // eslint-disable-next-line max-len
+       
       'A heading that identifies the overall topic of the table, and is useful for screen reader users': 'Un en-tête qui identifie le sujet général du tableau et qui est utile pour les utilisateurs de lecteurs d\'écran',
       'A heading that identifies the overall topic of the table, and is useful for screen reader users': 'Un en-tête qui identifie le sujet général du tableau et qui est utile pour les utilisateurs de lecteurs d\'écran',
       'Table': 'Tableau'
       'Table': 'Tableau'
     };
     };

+ 1 - 1
client/src/entrypoints/documents/document-chooser.js

@@ -54,7 +54,7 @@ function createDocumentChooser(id) {
       $('.action-choose', chooserElement).focus();
       $('.action-choose', chooserElement).focus();
     },
     },
     openChooserModal: () => {
     openChooserModal: () => {
-      // eslint-disable-next-line no-undef, new-cap
+      // eslint-disable-next-line no-undef
       ModalWorkflow({
       ModalWorkflow({
         url: chooserBaseUrl,
         url: chooserBaseUrl,
         // eslint-disable-next-line no-undef
         // eslint-disable-next-line no-undef

+ 1 - 1
client/src/entrypoints/images/image-chooser.js

@@ -66,7 +66,7 @@ function createImageChooser(id) {
     },
     },
 
 
     openChooserModal: () => {
     openChooserModal: () => {
-      // eslint-disable-next-line no-undef, new-cap
+      // eslint-disable-next-line no-undef
       ModalWorkflow({
       ModalWorkflow({
         url: chooserBaseUrl,
         url: chooserBaseUrl,
         // eslint-disable-next-line no-undef
         // eslint-disable-next-line no-undef

+ 1 - 1
client/src/entrypoints/snippets/snippet-chooser.js

@@ -64,7 +64,7 @@ function createSnippetChooser(id) {
         urlQuery = '?locale=' + wagtailConfig.ACTIVE_CONTENT_LOCALE;
         urlQuery = '?locale=' + wagtailConfig.ACTIVE_CONTENT_LOCALE;
       }
       }
 
 
-      // eslint-disable-next-line no-undef, new-cap
+      // eslint-disable-next-line no-undef
       ModalWorkflow({
       ModalWorkflow({
         url: chooserBaseUrl + urlQuery,
         url: chooserBaseUrl + urlQuery,
         // eslint-disable-next-line no-undef
         // eslint-disable-next-line no-undef

+ 1 - 1
client/src/utils/cleanForSlug.js

@@ -2,7 +2,7 @@ export function cleanForSlug(val, useURLify) {
   if (useURLify) {
   if (useURLify) {
     // URLify performs extra processing on the string (e.g. removing stopwords) and is more suitable
     // URLify performs extra processing on the string (e.g. removing stopwords) and is more suitable
     // for creating a slug from the title, rather than sanitising a slug entered manually
     // for creating a slug from the title, rather than sanitising a slug entered manually
-    // eslint-disable-next-line no-undef, new-cap
+    // eslint-disable-next-line no-undef
     const cleaned = URLify(val, 255, window.unicodeSlugsEnabled);
     const cleaned = URLify(val, 255, window.unicodeSlugsEnabled);
 
 
     // if the result is blank (e.g. because the title consisted entirely of stopwords),
     // if the result is blank (e.g. because the title consisted entirely of stopwords),

+ 0 - 5
client/tests/integration/.eslintrc.js

@@ -18,9 +18,4 @@ module.exports = {
     page: 'readonly',
     page: 'readonly',
     TEST_ORIGIN: 'readonly'
     TEST_ORIGIN: 'readonly'
   },
   },
-  settings: {
-    'import/resolver': {
-      webpack: null,
-    }
-  },
 };
 };

+ 3 - 2
package.json

@@ -95,6 +95,7 @@
   },
   },
   "dependencies": {
   "dependencies": {
     "core-js": "^2.5.3",
     "core-js": "^2.5.3",
+    "dateformat": "^2.2.0",
     "draft-js": "^0.10.5",
     "draft-js": "^0.10.5",
     "draftail": "^1.4.1",
     "draftail": "^1.4.1",
     "draftjs-filters": "^2.5.0",
     "draftjs-filters": "^2.5.0",
@@ -126,8 +127,8 @@
     "gulp:prod:build": "gulp build",
     "gulp:prod:build": "gulp build",
     "webpack:dev:watch": "webpack --config ./client/webpack.config.js --mode development --progress --watch",
     "webpack:dev:watch": "webpack --config ./client/webpack.config.js --mode development --progress --watch",
     "webpack:prod:build": "webpack --config ./client/webpack.config.js --mode production",
     "webpack:prod:build": "webpack --config ./client/webpack.config.js --mode production",
-    "fix:js": "eslint --fix ./",
+    "fix:js": "eslint --ext .js,.ts,.tsx --fix ./",
-    "lint:js": "eslint --report-unused-disable-directives ./",
+    "lint:js": "eslint --ext .js,.ts,.tsx --report-unused-disable-directives ./",
     "lint:css": "stylelint **/*.scss",
     "lint:css": "stylelint **/*.scss",
     "lint": "npm run lint:js && npm run lint:css",
     "lint": "npm run lint:js && npm run lint:css",
     "test": "npm run test:unit",
     "test": "npm run test:unit",

+ 1 - 1
wagtail/contrib/modeladmin/static_src/wagtailmodeladmin/js/prepopulate.js

@@ -1,4 +1,4 @@
-/* global URLify*/
+/* global URLify */
 (function($) {
 (function($) {
     $.fn.prepopulate = function(dependencies, maxLength, allowUnicode) {
     $.fn.prepopulate = function(dependencies, maxLength, allowUnicode) {
         /*
         /*