You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by bb...@apache.org on 2023/02/27 20:06:53 UTC

[airflow] branch main updated: Add prettier formatting for www (#29768)

This is an automated email from the ASF dual-hosted git repository.

bbovenzi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 0db38ad1a2 Add prettier formatting for www (#29768)
0db38ad1a2 is described below

commit 0db38ad1a2cf403eb546f027f2e5673610626f47
Author: Pierre Jeambrun <pi...@gmail.com>
AuthorDate: Mon Feb 27 21:06:43 2023 +0100

    Add prettier formatting for www (#29768)
    
    * Add prettier formatter for www
    
    * Ignore markdown as formatted by eslint
    
    * Fix CI
    
    * Update CONTRIBUTING.rst
---
 .codespellignorelines                              |   1 +
 .pre-commit-config.yaml                            |   8 +-
 .rat-excludes                                      |   4 +-
 CONTRIBUTING.rst                                   |  21 +-
 STATIC_CODE_CHECKS.rst                             |   2 +-
 airflow/www/.eslintrc                              |  18 +-
 airflow/www/.prettierignore                        |   5 +
 airflow/www/.prettierrc                            |  10 +
 airflow/www/.stylelintrc                           |   2 +-
 airflow/www/alias-rest-types.js                    | 120 ++--
 airflow/www/babel.config.js                        |   8 +-
 airflow/www/jest-setup.js                          |  40 +-
 airflow/www/jest.config.js                         |  66 +--
 airflow/www/package.json                           |   4 +
 airflow/www/static/css/bootstrap-theme.css         | 375 ++++++++++--
 airflow/www/static/css/dags.css                    |  14 +-
 airflow/www/static/css/flash.css                   |   6 +-
 airflow/www/static/css/main.css                    | 282 +++++++--
 airflow/www/static/css/material-icons.css          |  30 +-
 airflow/www/static/css/switch.css                  |   2 +-
 airflow/www/static/js/App.tsx                      |  35 +-
 airflow/www/static/js/README.md                    |   3 -
 airflow/www/static/js/api/index.ts                 |  48 +-
 airflow/www/static/js/api/useClearRun.ts           |  24 +-
 airflow/www/static/js/api/useClearTask.ts          |  82 ++-
 airflow/www/static/js/api/useConfirmMarkTask.ts    |  48 +-
 airflow/www/static/js/api/useDataset.ts            |  22 +-
 .../www/static/js/api/useDatasetDependencies.ts    | 155 ++---
 airflow/www/static/js/api/useDatasetEvents.ts      |  52 +-
 airflow/www/static/js/api/useDatasets.ts           |  52 +-
 airflow/www/static/js/api/useExtraLinks.ts         |  47 +-
 airflow/www/static/js/api/useGridData.test.ts      |  34 +-
 airflow/www/static/js/api/useGridData.ts           |  47 +-
 airflow/www/static/js/api/useMappedInstances.ts    |  37 +-
 airflow/www/static/js/api/useMarkFailedRun.ts      |  24 +-
 airflow/www/static/js/api/useMarkFailedTask.ts     |  57 +-
 airflow/www/static/js/api/useMarkSuccessRun.ts     |  24 +-
 airflow/www/static/js/api/useMarkSuccessTask.ts    |  57 +-
 airflow/www/static/js/api/useQueueRun.ts           |  24 +-
 airflow/www/static/js/api/useSetDagRunNote.ts      |  45 +-
 .../www/static/js/api/useSetTaskInstanceNote.ts    |  81 +--
 airflow/www/static/js/api/useTaskInstance.ts       |  44 +-
 airflow/www/static/js/api/useTaskLog.ts            |  66 ++-
 .../www/static/js/api/useUpstreamDatasetEvents.ts  |  29 +-
 airflow/www/static/js/calendar.js                  | 252 ++++----
 airflow/www/static/js/callModal.js                 | 253 ++++----
 airflow/www/static/js/components/AutoRefresh.tsx   |  22 +-
 .../www/static/js/components/Clipboard.test.tsx    |  17 +-
 airflow/www/static/js/components/Clipboard.tsx     |  39 +-
 airflow/www/static/js/components/ConfirmDialog.tsx |  35 +-
 airflow/www/static/js/components/InfoTooltip.tsx   |  12 +-
 .../www/static/js/components/LinkButton.test.tsx   |  16 +-
 airflow/www/static/js/components/LinkButton.tsx    |  14 +-
 airflow/www/static/js/components/MultiSelect.tsx   |  14 +-
 airflow/www/static/js/components/ReactMarkdown.tsx | 107 ++--
 airflow/www/static/js/components/RunTypeIcon.tsx   |  30 +-
 .../www/static/js/components/TabWithTooltip.tsx    |  38 +-
 .../www/static/js/components/Table/Cells.test.tsx  |  62 +-
 airflow/www/static/js/components/Table/Cells.tsx   |  98 +--
 .../www/static/js/components/Table/Table.test.tsx  | 107 ++--
 airflow/www/static/js/components/Table/index.tsx   | 200 ++++---
 airflow/www/static/js/components/Time.test.tsx     |  50 +-
 airflow/www/static/js/components/Time.tsx          |  12 +-
 airflow/www/static/js/components/Tooltip.tsx       | 126 ++--
 airflow/www/static/js/connection_form.js           | 153 ++---
 airflow/www/static/js/context/autorefresh.tsx      |  49 +-
 airflow/www/static/js/context/containerRef.tsx     |  10 +-
 airflow/www/static/js/context/timezone.tsx         |  23 +-
 airflow/www/static/js/dag.js                       |  74 +--
 airflow/www/static/js/dag/InstanceTooltip.test.tsx |  97 +--
 airflow/www/static/js/dag/InstanceTooltip.tsx      |  58 +-
 airflow/www/static/js/dag/Main.tsx                 | 192 +++---
 airflow/www/static/js/dag/StatusBox.tsx            |  73 ++-
 .../www/static/js/dag/details/BreadcrumbText.tsx   |  11 +-
 airflow/www/static/js/dag/details/Dag.tsx          | 134 ++---
 airflow/www/static/js/dag/details/Header.tsx       |  74 ++-
 .../static/js/dag/details/NotesAccordion.test.tsx  |  94 ++-
 .../www/static/js/dag/details/NotesAccordion.tsx   |  74 ++-
 .../www/static/js/dag/details/dagRun/ClearRun.tsx  |  18 +-
 .../js/dag/details/dagRun/DatasetTriggerEvents.tsx |  49 +-
 .../static/js/dag/details/dagRun/MarkFailedRun.tsx |  21 +-
 .../js/dag/details/dagRun/MarkSuccessRun.tsx       |  26 +-
 .../www/static/js/dag/details/dagRun/QueueRun.tsx  |  12 +-
 airflow/www/static/js/dag/details/dagRun/index.tsx | 122 ++--
 airflow/www/static/js/dag/details/index.tsx        |  46 +-
 .../dag/details/taskInstance/BackToTaskSummary.tsx |  11 +-
 .../details/taskInstance/DatasetUpdateEvents.tsx   |  63 +-
 .../static/js/dag/details/taskInstance/Details.tsx |  76 +--
 .../js/dag/details/taskInstance/ExtraLinks.tsx     |  21 +-
 .../js/dag/details/taskInstance/Logs/LogBlock.tsx  |  23 +-
 .../dag/details/taskInstance/Logs/LogLink.test.tsx |  42 +-
 .../js/dag/details/taskInstance/Logs/LogLink.tsx   |  50 +-
 .../dag/details/taskInstance/Logs/index.test.tsx   | 161 ++---
 .../js/dag/details/taskInstance/Logs/index.tsx     | 154 ++---
 .../dag/details/taskInstance/Logs/utils.test.tsx   |  79 ++-
 .../js/dag/details/taskInstance/Logs/utils.ts      |  55 +-
 .../dag/details/taskInstance/MappedInstances.tsx   | 107 ++--
 .../www/static/js/dag/details/taskInstance/Nav.tsx | 179 +++---
 .../static/js/dag/details/taskInstance/index.tsx   |  91 +--
 .../taskInstance/taskActions/ActionButton.tsx      |  24 +-
 .../dag/details/taskInstance/taskActions/Clear.tsx |  64 +-
 .../taskInstance/taskActions/MarkFailed.tsx        |  78 ++-
 .../taskInstance/taskActions/MarkSuccess.tsx       |  73 ++-
 .../dag/details/taskInstance/taskActions/index.tsx |  30 +-
 .../dag/details/taskInstance/taskActions/types.ts  |  12 +-
 airflow/www/static/js/dag/grid/ResetRoot.tsx       |  39 +-
 airflow/www/static/js/dag/grid/TaskName.test.tsx   |  39 +-
 airflow/www/static/js/dag/grid/TaskName.tsx        |  39 +-
 airflow/www/static/js/dag/grid/ToggleGroups.tsx    |  24 +-
 airflow/www/static/js/dag/grid/dagRuns/Bar.tsx     | 109 ++--
 airflow/www/static/js/dag/grid/dagRuns/Tooltip.tsx |  44 +-
 .../www/static/js/dag/grid/dagRuns/index.test.tsx  | 154 ++---
 airflow/www/static/js/dag/grid/dagRuns/index.tsx   | 101 ++--
 airflow/www/static/js/dag/grid/index.test.tsx      | 168 +++---
 airflow/www/static/js/dag/grid/index.tsx           |  53 +-
 .../www/static/js/dag/grid/renderTaskRows.test.tsx |  88 +--
 airflow/www/static/js/dag/grid/renderTaskRows.tsx  | 105 ++--
 airflow/www/static/js/dag/index.tsx                |  20 +-
 airflow/www/static/js/dag/nav/FilterBar.tsx        |  60 +-
 airflow/www/static/js/dag/nav/LegendRow.test.tsx   |  33 +-
 airflow/www/static/js/dag/nav/LegendRow.tsx        |  22 +-
 airflow/www/static/js/dag/useFilters.test.tsx      |  59 +-
 airflow/www/static/js/dag/useFilters.tsx           |  56 +-
 airflow/www/static/js/dag/useSelection.test.tsx    |  34 +-
 airflow/www/static/js/dag/useSelection.ts          |  12 +-
 airflow/www/static/js/dag_code.js                  |   8 +-
 airflow/www/static/js/dag_dependencies.js          | 139 +++--
 airflow/www/static/js/dags.js                      | 441 +++++++-------
 airflow/www/static/js/datasetUtils.js              |  78 +--
 airflow/www/static/js/datasets/DatasetEvents.tsx   |  49 +-
 airflow/www/static/js/datasets/Details.tsx         |  31 +-
 airflow/www/static/js/datasets/Graph/DagNode.tsx   |  21 +-
 airflow/www/static/js/datasets/Graph/Edge.tsx      |  17 +-
 airflow/www/static/js/datasets/Graph/Legend.tsx    |  14 +-
 airflow/www/static/js/datasets/Graph/Node.tsx      |  50 +-
 airflow/www/static/js/datasets/Graph/index.tsx     |  64 +-
 airflow/www/static/js/datasets/List.test.tsx       |  68 +--
 airflow/www/static/js/datasets/List.tsx            | 102 ++--
 airflow/www/static/js/datasets/index.tsx           |  54 +-
 airflow/www/static/js/datetime_utils.js            |  63 +-
 airflow/www/static/js/duration_chart.js            |  14 +-
 airflow/www/static/js/gantt.js                     | 334 ++++++-----
 airflow/www/static/js/graph.js                     | 362 +++++++-----
 airflow/www/static/js/index.d.ts                   |   2 +-
 airflow/www/static/js/main.js                      | 155 ++---
 airflow/www/static/js/task.js                      |   8 +-
 airflow/www/static/js/task_instances.js            |  93 +--
 airflow/www/static/js/theme.ts                     |   8 +-
 airflow/www/static/js/ti_log.js                    | 103 ++--
 airflow/www/static/js/trigger.js                   | 112 ++--
 airflow/www/static/js/types/api-generated.ts       | 657 +++++++++++++++------
 airflow/www/static/js/types/index.ts               |  53 +-
 .../www/static/js/types/react-table-config.d.ts    | 131 ++--
 .../www/static/js/utils/URLSearchParamWrapper.ts   |   6 +-
 airflow/www/static/js/utils/index.test.ts          | 111 ++--
 airflow/www/static/js/utils/index.ts               |  63 +-
 airflow/www/static/js/utils/testUtils.tsx          |  28 +-
 airflow/www/static/js/utils/useErrorToast.test.tsx |  34 +-
 airflow/www/static/js/utils/useErrorToast.ts       |  24 +-
 airflow/www/static/js/utils/useOffsetTop.ts        |   7 +-
 airflow/www/static/js/variable_edit.js             |   2 +-
 airflow/www/tsconfig.json                          |  15 +-
 airflow/www/webpack.config.js                      | 140 ++---
 airflow/www/yarn.lock                              |  15 +
 dev/breeze/src/airflow_breeze/pre_commit_ids.py    |   2 +-
 images/breeze/output-commands-hash.txt             |   2 +-
 images/breeze/output-commands.svg                  |  90 +--
 images/breeze/output_static-checks.svg             | 106 ++--
 scripts/ci/pre_commit/pre_commit_www_lint.py       |   1 +
 169 files changed, 6634 insertions(+), 4964 deletions(-)

diff --git a/.codespellignorelines b/.codespellignorelines
index 4b0179fdc9..1234698be3 100644
--- a/.codespellignorelines
+++ b/.codespellignorelines
@@ -3,3 +3,4 @@
     roles = relationship("Role", secondary=assoc_user_role, backref="user", lazy="selectin")
     The platform supports **C**reate, **R**ead, **U**pdate, and **D**elete operations on most resources.
 <pre><code>Code block\ndoes not\nrespect\nnewlines\n</code></pre>
+      "trough",
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index f1ad6949ea..d37e0cbb8e 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -641,7 +641,7 @@ repos:
         language: node
         files: ^airflow/www/.*\.(css|sass|scss)$
         # Keep dependency versions in sync w/ airflow/www/package.json
-        additional_dependencies: ['stylelint@13.3.1', 'stylelint-config-standard@20.0.0']
+        additional_dependencies: ['stylelint@13.3.1', 'stylelint-config-standard@20.0.0', 'stylelint-config-prettier@9.0.5']
       - id: compile-www-assets
         name: Compile www assets
         language: node
@@ -877,10 +877,10 @@ repos:
         additional_dependencies: ['rich>=12.4.4']
         pass_filenames: false
         files: ^tests/.*\.py$
-      - id: ts-compile-and-lint-javascript
-        name: TS types generation and ESLint against current UI files
+      - id: ts-compile-format-lint-www
+        name: TS types generation and ESLint/Prettier against current UI files
         language: node
-        'types_or': [javascript, ts, tsx, yaml]
+        'types_or': [javascript, ts, tsx, yaml, css, json]
         files: ^airflow/www/static/js/|^airflow/api_connexion/openapi/v1\.yaml$
         entry: ./scripts/ci/pre_commit/pre_commit_www_lint.py
         additional_dependencies: ['yarn@1.22.19']
diff --git a/.rat-excludes b/.rat-excludes
index 138e8a0787..0b360664d8 100644
--- a/.rat-excludes
+++ b/.rat-excludes
@@ -13,8 +13,10 @@
 .coveragerc
 .codecov.yml
 .codespellignorelines
-.eslintrc
 .eslintignore
+.eslintrc
+.prettierignore
+.prettierrc
 .rat-excludes
 .stylelintignore
 .stylelintrc
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index ab32efb178..e5d989f61f 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -1179,29 +1179,32 @@ commands:
     yarn run dev
 
 
-Follow JavaScript Style Guide
------------------------------
+Follow Style Guide
+------------------
 
-We try to enforce a more consistent style and follow the JS community
+We try to enforce a more consistent style and follow the Javascript/Typescript community
 guidelines.
 
-Once you add or modify any JavaScript code in the project, please make sure it
+Once you add or modify any JS/TS code in the project, please make sure it
 follows the guidelines defined in `Airbnb
 JavaScript Style Guide <https://github.com/airbnb/javascript>`__.
 
 Apache Airflow uses `ESLint <https://eslint.org/>`__ as a tool for identifying and
-reporting on patterns in JavaScript. To use it, run any of the following
-commands:
+reporting issues in JS/TS, and `Prettier <https://prettier.io/>`__ for code formatting.
+Most IDE directly integrate with these tools, you can also manually run them with any of the following commands:
 
 .. code-block:: bash
 
-    # Check JS code in .js, .jsx, and .html files, and report any errors/warnings
+    # Format code in .js, .jsx, .ts, .tsx, .json, .css, .html files
+    yarn format
+
+    # Check JS/TS code in .js, .jsx, .ts, .tsx, .html files and report any errors/warnings
     yarn run lint
 
-    # Check JS code in .js, .jsx, and .html files, report any errors/warnings and fix them if possible
+    # Check JS/TS code in .js, .jsx, .ts, .tsx, .html files and report any errors/warnings and fix them if possible
     yarn run lint:fix
 
-    # Runs tests for all .test.js and .test.jsx files
+    # Run tests for all .test.js, .test.jsx, .test.ts, test.tsx files
     yarn test
 
 React, JSX and Chakra
diff --git a/STATIC_CODE_CHECKS.rst b/STATIC_CODE_CHECKS.rst
index 814889782c..65808784f3 100644
--- a/STATIC_CODE_CHECKS.rst
+++ b/STATIC_CODE_CHECKS.rst
@@ -305,7 +305,7 @@ require Breeze Docker image to be build locally.
 +-----------------------------------------------------------+------------------------------------------------------------------+---------+
 | trailing-whitespace                                       | Remove trailing whitespace at end of line                        |         |
 +-----------------------------------------------------------+------------------------------------------------------------------+---------+
-| ts-compile-and-lint-javascript                            | TS types generation and ESLint against current UI files          |         |
+| ts-compile-format-lint-www                                | TS types generation and ESLint/Prettier against current UI files |         |
 +-----------------------------------------------------------+------------------------------------------------------------------+---------+
 | update-black-version                                      | Update black versions everywhere                                 |         |
 +-----------------------------------------------------------+------------------------------------------------------------------+---------+
diff --git a/airflow/www/.eslintrc b/airflow/www/.eslintrc
index b1a446a31b..29914c0cd8 100644
--- a/airflow/www/.eslintrc
+++ b/airflow/www/.eslintrc
@@ -1,13 +1,17 @@
 {
-  "extends": ["airbnb", "airbnb/hooks"],
+  "extends": ["airbnb", "airbnb/hooks", "prettier"],
   "parser": "@babel/eslint-parser",
   "parserOptions": {
     "babelOptions": {
-      "presets": ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"],
+      "presets": [
+        "@babel/preset-env",
+        "@babel/preset-react",
+        "@babel/preset-typescript"
+      ],
       "plugins": ["@babel/plugin-transform-runtime"]
     }
   },
-  "plugins": [ "html", "react" ],
+  "plugins": ["html", "react"],
   "rules": {
     "no-param-reassign": 1,
     "react/prop-types": 0,
@@ -21,7 +25,7 @@
         "ts": "never",
         "tsx": "never"
       }
-   ],
+    ],
     "import/no-extraneous-dependencies": [
       "error",
       {
@@ -48,11 +52,9 @@
   "overrides": [
     {
       "files": ["*.ts", "*.tsx"],
-      "extends": [
-        "airbnb-typescript"
-      ],
+      "extends": ["airbnb-typescript", "prettier"],
       "parser": "@typescript-eslint/parser",
-      "plugins": [ "@typescript-eslint" ],
+      "plugins": ["@typescript-eslint"],
       "parserOptions": {
         "project": "./tsconfig.json"
       },
diff --git a/airflow/www/.prettierignore b/airflow/www/.prettierignore
new file mode 100644
index 0000000000..a2bcd8157d
--- /dev/null
+++ b/airflow/www/.prettierignore
@@ -0,0 +1,5 @@
+.mypy_cache/
+templates/**/*.html
+dist/
+*.md
+*.yaml
diff --git a/airflow/www/.prettierrc b/airflow/www/.prettierrc
new file mode 100644
index 0000000000..3574cf79c5
--- /dev/null
+++ b/airflow/www/.prettierrc
@@ -0,0 +1,10 @@
+{
+  "overrides": [
+    {
+      "files": "*.json",
+      "options": {
+        "tabWidth": 2
+      }
+    }
+  ]
+}
diff --git a/airflow/www/.stylelintrc b/airflow/www/.stylelintrc
index 40db42c668..2e8ff5864a 100644
--- a/airflow/www/.stylelintrc
+++ b/airflow/www/.stylelintrc
@@ -1,3 +1,3 @@
 {
-  "extends": "stylelint-config-standard"
+  "extends": ["stylelint-config-standard", "stylelint-config-prettier"]
 }
diff --git a/airflow/www/alias-rest-types.js b/airflow/www/alias-rest-types.js
index 44c49ced78..83ddd89aae 100644
--- a/airflow/www/alias-rest-types.js
+++ b/airflow/www/alias-rest-types.js
@@ -17,8 +17,8 @@
  * under the License.
  */
 
-const ts = require('typescript');
-const fs = require('fs');
+const ts = require("typescript");
+const fs = require("fs");
 
 /* This library does three things to make openapi-typescript generation easier to use.
  * 1. Creates capitalized exports for Paths and Operations
@@ -29,16 +29,16 @@ const fs = require('fs');
  */
 
 /* Finds all words, capitalizes them, and removes all other characters. */
-const toPascalCase = (str) => (
+const toPascalCase = (str) =>
   (str.match(/[a-zA-Z0-9]+/g) || [])
     .map((w) => `${w.charAt(0).toUpperCase()}${w.slice(1)}`)
-    .join('')
-);
+    .join("");
 
 /* Adds a prefix to a type prop as necessary.
  * ('', 'components') => 'components'
  */
-const prefixPath = (rootPrefix, prop) => (rootPrefix ? `${rootPrefix}['${prop}']` : prop);
+const prefixPath = (rootPrefix, prop) =>
+  rootPrefix ? `${rootPrefix}['${prop}']` : prop;
 
 // Recursively find child nodes by name.
 const findNode = (node, ...names) => {
@@ -58,87 +58,114 @@ const findNode = (node, ...names) => {
 // Generate Variable Type Aliases for a given path or operation
 const generateVariableAliases = (node, operationPath, operationName) => {
   const variableTypes = [];
-  const hasPath = !!findNode(node, 'parameters', 'path');
-  const hasQuery = !!findNode(node, 'parameters', 'query');
-  const hasBody = !!findNode(node, 'requestBody', 'content', 'application/json');
+  const hasPath = !!findNode(node, "parameters", "path");
+  const hasQuery = !!findNode(node, "parameters", "query");
+  const hasBody = !!findNode(
+    node,
+    "requestBody",
+    "content",
+    "application/json"
+  );
 
   if (hasPath) variableTypes.push(`${operationPath}['parameters']['path']`);
   if (hasQuery) variableTypes.push(`${operationPath}['parameters']['query']`);
-  if (hasBody) variableTypes.push(`${operationPath}['requestBody']['content']['application/json']`);
+  if (hasBody)
+    variableTypes.push(
+      `${operationPath}['requestBody']['content']['application/json']`
+    );
 
-  if (variableTypes.length === 0) return '';
+  if (variableTypes.length === 0) return "";
   const typeName = `${toPascalCase(operationName)}Variables`;
-  return [typeName, `export type ${typeName} = CamelCasedPropertiesDeep<${variableTypes.join(' & ')}>;`];
+  return [
+    typeName,
+    `export type ${typeName} = CamelCasedPropertiesDeep<${variableTypes.join(
+      " & "
+    )}>;`,
+  ];
 };
 
 // Generate Type Aliases
-const generateAliases = (rootNode, writeText, prefix = '') => {
+const generateAliases = (rootNode, writeText, prefix = "") => {
   // Loop through the root AST nodes of the file
   ts.forEachChild(rootNode, (node) => {
     // Response Data Types
-    if (ts.isInterfaceDeclaration(node) && node.name?.text === 'components') {
-      const schemaMemberNames = findNode(node, 'schemas').type.members.map((n) => n.name?.text);
+    if (ts.isInterfaceDeclaration(node) && node.name?.text === "components") {
+      const schemaMemberNames = findNode(node, "schemas").type.members.map(
+        (n) => n.name?.text
+      );
 
       const types = schemaMemberNames.map((n) => [
         `${n}`,
-        `export type ${n} = CamelCasedPropertiesDeep<${prefixPath(prefix, 'components')}['schemas']['${n}']>;`,
+        `export type ${n} = CamelCasedPropertiesDeep<${prefixPath(
+          prefix,
+          "components"
+        )}['schemas']['${n}']>;`,
       ]);
       if (types.length) {
-        writeText.push(['comment', `Types for returned data ${prefix}`]);
+        writeText.push(["comment", `Types for returned data ${prefix}`]);
         writeText.push(...types);
       }
     }
 
     // Paths referencing an operation are skipped
-    if (node.name?.text === 'paths') {
+    if (node.name?.text === "paths") {
       if (!prefix) {
-        writeText.push(['comment', 'Alias paths to PascalCase.']);
-        writeText.push(['Paths', 'export type Paths = paths;']);
+        writeText.push(["comment", "Alias paths to PascalCase."]);
+        writeText.push(["Paths", "export type Paths = paths;"]);
       }
 
       const types = [];
 
       (node.members || node.type.members).forEach((path) => {
         const methodNames = path.type.members.map((m) => m.name.text);
-        const methodTypes = methodNames.map((m) => (
+        const methodTypes = methodNames.map((m) =>
           generateVariableAliases(
             findNode(path, m),
-            `${prefixPath(prefix, 'paths')}['${path.name?.text}']['${m}']`,
-            `${path.name.text}${toPascalCase(m)}`,
-          )));
+            `${prefixPath(prefix, "paths")}['${path.name?.text}']['${m}']`,
+            `${path.name.text}${toPascalCase(m)}`
+          )
+        );
         types.push(...methodTypes.filter((m) => !!m));
       });
 
       if (types.length) {
-        writeText.push(['comment', `Types for path operation variables ${prefix}`]);
+        writeText.push([
+          "comment",
+          `Types for path operation variables ${prefix}`,
+        ]);
         writeText.push(...types);
       }
     }
 
     // operationIds are defined
-    if (node.name?.text === 'operations') {
+    if (node.name?.text === "operations") {
       if (!prefix) {
-        writeText.push(['comment', 'Alias operations to PascalCase.']);
-        writeText.push(['Operations', 'export type Operations = operations;']);
+        writeText.push(["comment", "Alias operations to PascalCase."]);
+        writeText.push(["Operations", "export type Operations = operations;"]);
       }
 
-      const types = (node.members || node.type.members).map((operation) => (
+      const types = (node.members || node.type.members).map((operation) =>
         generateVariableAliases(
           operation,
-          `${prefixPath(prefix, 'operations')}['${operation.name.text}']`,
-          operation.name.text,
-        )));
+          `${prefixPath(prefix, "operations")}['${operation.name.text}']`,
+          operation.name.text
+        )
+      );
       if (types.length) {
-        writeText.push(['comment', `Types for operation variables ${prefix}`]);
+        writeText.push(["comment", `Types for operation variables ${prefix}`]);
         writeText.push(...types);
-        writeText.push('\n');
+        writeText.push("\n");
       }
     }
 
     // recursively call this for any externals
-    if (ts.isInterfaceDeclaration(node) && node.name?.text === 'external') {
+    if (ts.isInterfaceDeclaration(node) && node.name?.text === "external") {
       node.members.forEach((external) => {
-        generateAliases(external.type, writeText, `external['${external.name.text}']`);
+        generateAliases(
+          external.type,
+          writeText,
+          `external['${external.name.text}']`
+        );
       });
     }
   });
@@ -169,27 +196,32 @@ function generate(file) {
   const program = ts.createProgram([file], { allowJs: true });
   const sourceFile = program.getSourceFile(file);
   const writeText = [];
-  writeText.push(['block', license]);
-  writeText.push(['comment', 'eslint-disable']);
+  writeText.push(["block", license]);
+  writeText.push(["comment", "eslint-disable"]);
   // eslint-disable-next-line quotes
-  writeText.push(['block', `import type { CamelCasedPropertiesDeep } from 'type-fest';`]);
-  writeText.push(['block', sourceFile.text]);
+  writeText.push([
+    "block",
+    `import type { CamelCasedPropertiesDeep } from 'type-fest';`,
+  ]);
+  writeText.push(["block", sourceFile.text]);
   generateAliases(sourceFile, writeText);
 
   const finalText = writeText
     // Deduplicate types
     .map((pair) => {
       // keep all comments and code blocks
-      if (pair[0] === 'comment' || pair[0] === 'block') return pair;
+      if (pair[0] === "comment" || pair[0] === "block") return pair;
       // return the first instance of this key only
       const firstInstance = writeText.find((p) => p[0] === pair[0]);
-      return firstInstance === pair ? pair : ['comment', `Duplicate removed: ${pair[1]}`];
+      return firstInstance === pair
+        ? pair
+        : ["comment", `Duplicate removed: ${pair[1]}`];
     })
     // Remove undefined created above
     .filter((p) => !!p)
     // Escape comments and flatten.
-    .map((pair) => (pair[0] === 'comment' ? `\n/* ${pair[1]} */` : pair[1]))
-    .join('\n');
+    .map((pair) => (pair[0] === "comment" ? `\n/* ${pair[1]} */` : pair[1]))
+    .join("\n");
 
   fs.writeFileSync(file, finalText, (err) => {
     if (err) {
diff --git a/airflow/www/babel.config.js b/airflow/www/babel.config.js
index 7c37d14c86..f38c60b91f 100644
--- a/airflow/www/babel.config.js
+++ b/airflow/www/babel.config.js
@@ -20,9 +20,13 @@
 module.exports = function (api) {
   api.cache(true);
 
-  const presets = ['@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript'];
+  const presets = [
+    "@babel/preset-env",
+    "@babel/preset-react",
+    "@babel/preset-typescript",
+  ];
 
-  const plugins = ['@babel/plugin-transform-runtime'];
+  const plugins = ["@babel/plugin-transform-runtime"];
 
   return {
     presets,
diff --git a/airflow/www/jest-setup.js b/airflow/www/jest-setup.js
index 55a83464e5..0a13d7c644 100644
--- a/airflow/www/jest-setup.js
+++ b/airflow/www/jest-setup.js
@@ -19,18 +19,16 @@
  * under the License.
  */
 
-import '@testing-library/jest-dom';
-import axios from 'axios';
-import { setLogger } from 'react-query';
+import "@testing-library/jest-dom";
+import axios from "axios";
+import { setLogger } from "react-query";
 
 // eslint-disable-next-line import/no-extraneous-dependencies
-import moment from 'moment-timezone';
+import moment from "moment-timezone";
 
-axios.defaults.adapter = require('axios/lib/adapters/http');
+axios.defaults.adapter = require("axios/lib/adapters/http");
 
-axios.interceptors.response.use(
-  (res) => res.data || res,
-);
+axios.interceptors.response.use((res) => res.data || res);
 
 setLogger({
   log: console.log,
@@ -41,19 +39,19 @@ setLogger({
 
 // Mock global objects we use across the app
 global.stateColors = {
-  deferred: 'mediumpurple',
-  failed: 'red',
-  queued: 'gray',
-  removed: 'lightgrey',
-  restarting: 'violet',
-  running: 'lime',
-  scheduled: 'tan',
-  shutdown: 'blue',
-  skipped: 'hotpink',
-  success: 'green',
-  up_for_reschedule: 'turquoise',
-  up_for_retry: 'gold',
-  upstream_failed: 'orange',
+  deferred: "mediumpurple",
+  failed: "red",
+  queued: "gray",
+  removed: "lightgrey",
+  restarting: "violet",
+  running: "lime",
+  scheduled: "tan",
+  shutdown: "blue",
+  skipped: "hotpink",
+  success: "green",
+  up_for_reschedule: "turquoise",
+  up_for_retry: "gold",
+  upstream_failed: "orange",
 };
 
 global.defaultDagRunDisplayNumber = 245;
diff --git a/airflow/www/jest.config.js b/airflow/www/jest.config.js
index 3c35fa9d68..11f8da1c4c 100644
--- a/airflow/www/jest.config.js
+++ b/airflow/www/jest.config.js
@@ -20,42 +20,42 @@
 const config = {
   verbose: true,
   transform: {
-    '^.+\\.[jt]sx?$': 'babel-jest',
+    "^.+\\.[jt]sx?$": "babel-jest",
   },
-  testEnvironment: 'jsdom',
-  setupFilesAfterEnv: ['./jest-setup.js'],
-  moduleDirectories: ['node_modules'],
-  moduleNameMapper: { // Listing all aliases
-    '^src/(.*)$': '<rootDir>/static/js/$1',
+  testEnvironment: "jsdom",
+  setupFilesAfterEnv: ["./jest-setup.js"],
+  moduleDirectories: ["node_modules"],
+  moduleNameMapper: {
+    // Listing all aliases
+    "^src/(.*)$": "<rootDir>/static/js/$1",
   },
   transformIgnorePatterns: [
-    `node_modules/(?!${
-      [ // specify modules that needs to be transformed for jest. (esm modules)
-        'react-markdown',
-        'vfile',
-        'vfile-message',
-        'unist',
-        'unified',
-        'bail',
-        'is-plain-obj',
-        'trough',
-        'remark-parse',
-        'mdast',
-        'micromark',
-        'decode-named-character-reference',
-        'character-entities',
-        'remark-rehype',
-        'trim-lines',
-        'property-information',
-        'hast',
-        'space-separated-tokens',
-        'comma-separated-tokens',
-        'remark-gfm',
-        'ccount',
-        'escape-string-regexp',
-        'markdown-table',
-      ].join('|')
-    })`,
+    `node_modules/(?!${[
+      // specify modules that needs to be transformed for jest. (esm modules)
+      "react-markdown",
+      "vfile",
+      "vfile-message",
+      "unist",
+      "unified",
+      "bail",
+      "is-plain-obj",
+      "trough",
+      "remark-parse",
+      "mdast",
+      "micromark",
+      "decode-named-character-reference",
+      "character-entities",
+      "remark-rehype",
+      "trim-lines",
+      "property-information",
+      "hast",
+      "space-separated-tokens",
+      "comma-separated-tokens",
+      "remark-gfm",
+      "ccount",
+      "escape-string-regexp",
+      "markdown-table",
+    ].join("|")})`,
   ],
 };
 
diff --git a/airflow/www/package.json b/airflow/www/package.json
index c96640723c..bbce06550e 100644
--- a/airflow/www/package.json
+++ b/airflow/www/package.json
@@ -9,6 +9,7 @@
     "build": "NODE_ENV=production webpack --progress --mode production",
     "lint": "eslint --ignore-path=.eslintignore --ext .js,.jsx,.ts,.tsx . && tsc",
     "lint:fix": "eslint --fix --ignore-path=.eslintignore --ext .js,.jsx,.ts,.tsx . && tsc",
+    "format": "yarn prettier --write .",
     "generate-api-types": "npx openapi-typescript \"../api_connexion/openapi/v1.yaml\" --output static/js/types/api-generated.ts && node alias-rest-types.js static/js/types/api-generated.ts"
   },
   "author": "Apache",
@@ -52,6 +53,7 @@
     "eslint": "^8.6.0",
     "eslint-config-airbnb": "^19.0.4",
     "eslint-config-airbnb-typescript": "^17.0.0",
+    "eslint-config-prettier": "^8.6.0",
     "eslint-plugin-html": "^6.0.2",
     "eslint-plugin-import": "^2.25.3",
     "eslint-plugin-jsx-a11y": "^6.5.0",
@@ -68,8 +70,10 @@
     "moment-locales-webpack-plugin": "^1.2.0",
     "nock": "^13.2.4",
     "openapi-typescript": "^5.4.1",
+    "prettier": "^2.8.4",
     "style-loader": "^1.2.1",
     "stylelint": "^13.6.1",
+    "stylelint-config-prettier": "^9.0.5",
     "stylelint-config-standard": "^20.0.0",
     "terser-webpack-plugin": "<5.0.0",
     "typescript": "^4.6.3",
diff --git a/airflow/www/static/css/bootstrap-theme.css b/airflow/www/static/css/bootstrap-theme.css
index 6095498d57..371762d558 100644
--- a/airflow/www/static/css/bootstrap-theme.css
+++ b/airflow/www/static/css/bootstrap-theme.css
@@ -703,7 +703,7 @@ blockquote .small {
 blockquote footer::before,
 blockquote small::before,
 blockquote .small::before {
-  content: '\2014 \00A0';
+  content: "\2014 \00A0";
 }
 .blockquote-reverse,
 blockquote.pull-right {
@@ -719,7 +719,7 @@ blockquote.pull-right footer::before,
 blockquote.pull-right small::before,
 .blockquote-reverse .small::before,
 blockquote.pull-right .small::before {
-  content: '';
+  content: "";
 }
 .blockquote-reverse footer::after,
 blockquote.pull-right footer::after,
@@ -727,7 +727,7 @@ blockquote.pull-right footer::after,
 blockquote.pull-right small::after,
 .blockquote-reverse .small::after,
 blockquote.pull-right .small::after {
-  content: '\00A0 \2014';
+  content: "\00A0 \2014";
 }
 address {
   margin-bottom: 20px;
@@ -1866,15 +1866,18 @@ output {
   border-radius: 4px;
   -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
   box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-  -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
+  -webkit-transition: border-color ease-in-out 0.15s,
+    box-shadow ease-in-out 0.15s;
   -o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
   transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
 }
 .form-control:focus {
   border-color: #66afe9;
   outline: 0;
-  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
-  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
+    0 0 8px rgba(102, 175, 233, 0.6);
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
+    0 0 8px rgba(102, 175, 233, 0.6);
 }
 .form-control::-moz-placeholder {
   color: #999;
@@ -2991,11 +2994,15 @@ tbody.collapse.in {
   border-radius: 0;
 }
 .btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,
-.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {
+.btn-group-vertical
+  > .btn-group:first-child:not(:last-child)
+  > .dropdown-toggle {
   border-bottom-right-radius: 0;
   border-bottom-left-radius: 0;
 }
-.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {
+.btn-group-vertical
+  > .btn-group:last-child:not(:first-child)
+  > .btn:first-child {
   border-top-right-radius: 0;
   border-top-left-radius: 0;
 }
@@ -3598,8 +3605,10 @@ select[multiple].input-group-sm > .input-group-btn > .btn {
   padding: 10px 15px;
   border-top: 1px solid transparent;
   border-bottom: 1px solid transparent;
-  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
-  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1),
+    0 1px 0 rgba(255, 255, 255, 0.1);
+  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1),
+    0 1px 0 rgba(255, 255, 255, 0.1);
   margin-top: 8px;
   margin-bottom: 8px;
 }
@@ -4403,9 +4412,36 @@ a.thumbnail.active {
 }
 .progress-striped .progress-bar,
 .progress-bar-striped {
-  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
-  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
-  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: -webkit-linear-gradient(
+    45deg,
+    rgba(255, 255, 255, 0.15) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, 0.15) 50%,
+    rgba(255, 255, 255, 0.15) 75%,
+    transparent 75%,
+    transparent
+  );
+  background-image: -o-linear-gradient(
+    45deg,
+    rgba(255, 255, 255, 0.15) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, 0.15) 50%,
+    rgba(255, 255, 255, 0.15) 75%,
+    transparent 75%,
+    transparent
+  );
+  background-image: linear-gradient(
+    45deg,
+    rgba(255, 255, 255, 0.15) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, 0.15) 50%,
+    rgba(255, 255, 255, 0.15) 75%,
+    transparent 75%,
+    transparent
+  );
   background-size: 40px 40px;
 }
 .progress.active .progress-bar,
@@ -4418,33 +4454,141 @@ a.thumbnail.active {
   background-color: #00ad46;
 }
 .progress-striped .progress-bar-success {
-  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
-  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
-  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: -webkit-linear-gradient(
+    45deg,
+    rgba(255, 255, 255, 0.15) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, 0.15) 50%,
+    rgba(255, 255, 255, 0.15) 75%,
+    transparent 75%,
+    transparent
+  );
+  background-image: -o-linear-gradient(
+    45deg,
+    rgba(255, 255, 255, 0.15) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, 0.15) 50%,
+    rgba(255, 255, 255, 0.15) 75%,
+    transparent 75%,
+    transparent
+  );
+  background-image: linear-gradient(
+    45deg,
+    rgba(255, 255, 255, 0.15) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, 0.15) 50%,
+    rgba(255, 255, 255, 0.15) 75%,
+    transparent 75%,
+    transparent
+  );
 }
 .progress-bar-info {
   background-color: #00d1c1;
 }
 .progress-striped .progress-bar-info {
-  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
-  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
-  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: -webkit-linear-gradient(
+    45deg,
+    rgba(255, 255, 255, 0.15) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, 0.15) 50%,
+    rgba(255, 255, 255, 0.15) 75%,
+    transparent 75%,
+    transparent
+  );
+  background-image: -o-linear-gradient(
+    45deg,
+    rgba(255, 255, 255, 0.15) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, 0.15) 50%,
+    rgba(255, 255, 255, 0.15) 75%,
+    transparent 75%,
+    transparent
+  );
+  background-image: linear-gradient(
+    45deg,
+    rgba(255, 255, 255, 0.15) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, 0.15) 50%,
+    rgba(255, 255, 255, 0.15) 75%,
+    transparent 75%,
+    transparent
+  );
 }
 .progress-bar-warning {
   background-color: #ffb400;
 }
 .progress-striped .progress-bar-warning {
-  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
-  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
-  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: -webkit-linear-gradient(
+    45deg,
+    rgba(255, 255, 255, 0.15) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, 0.15) 50%,
+    rgba(255, 255, 255, 0.15) 75%,
+    transparent 75%,
+    transparent
+  );
+  background-image: -o-linear-gradient(
+    45deg,
+    rgba(255, 255, 255, 0.15) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, 0.15) 50%,
+    rgba(255, 255, 255, 0.15) 75%,
+    transparent 75%,
+    transparent
+  );
+  background-image: linear-gradient(
+    45deg,
+    rgba(255, 255, 255, 0.15) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, 0.15) 50%,
+    rgba(255, 255, 255, 0.15) 75%,
+    transparent 75%,
+    transparent
+  );
 }
 .progress-bar-danger {
   background-color: #ff5a5f;
 }
 .progress-striped .progress-bar-danger {
-  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
-  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
-  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: -webkit-linear-gradient(
+    45deg,
+    rgba(255, 255, 255, 0.15) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, 0.15) 50%,
+    rgba(255, 255, 255, 0.15) 75%,
+    transparent 75%,
+    transparent
+  );
+  background-image: -o-linear-gradient(
+    45deg,
+    rgba(255, 255, 255, 0.15) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, 0.15) 50%,
+    rgba(255, 255, 255, 0.15) 75%,
+    transparent 75%,
+    transparent
+  );
+  background-image: linear-gradient(
+    45deg,
+    rgba(255, 255, 255, 0.15) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, 0.15) 50%,
+    rgba(255, 255, 255, 0.15) 75%,
+    transparent 75%,
+    transparent
+  );
 }
 .media {
   margin-top: 15px;
@@ -4703,7 +4847,10 @@ a.list-group-item-danger.active:focus {
   border-radius: 0;
 }
 .panel > .list-group:first-child .list-group-item:first-child,
-.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {
+.panel
+  > .panel-collapse
+  > .list-group:first-child
+  .list-group-item:first-child {
   border-top: 0;
   border-top-right-radius: 3px;
   border-top-left-radius: 3px;
@@ -4737,30 +4884,78 @@ a.list-group-item-danger.active:focus {
   border-top-left-radius: 3px;
 }
 .panel > .table:first-child > thead:first-child > tr:first-child,
-.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,
+.panel
+  > .table-responsive:first-child
+  > .table:first-child
+  > thead:first-child
+  > tr:first-child,
 .panel > .table:first-child > tbody:first-child > tr:first-child,
-.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {
+.panel
+  > .table-responsive:first-child
+  > .table:first-child
+  > tbody:first-child
+  > tr:first-child {
   border-top-left-radius: 3px;
   border-top-right-radius: 3px;
 }
 .panel > .table:first-child > thead:first-child > tr:first-child td:first-child,
-.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,
+.panel
+  > .table-responsive:first-child
+  > .table:first-child
+  > thead:first-child
+  > tr:first-child
+  td:first-child,
 .panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,
-.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,
+.panel
+  > .table-responsive:first-child
+  > .table:first-child
+  > tbody:first-child
+  > tr:first-child
+  td:first-child,
 .panel > .table:first-child > thead:first-child > tr:first-child th:first-child,
-.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,
+.panel
+  > .table-responsive:first-child
+  > .table:first-child
+  > thead:first-child
+  > tr:first-child
+  th:first-child,
 .panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,
-.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {
+.panel
+  > .table-responsive:first-child
+  > .table:first-child
+  > tbody:first-child
+  > tr:first-child
+  th:first-child {
   border-top-left-radius: 3px;
 }
 .panel > .table:first-child > thead:first-child > tr:first-child td:last-child,
-.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,
+.panel
+  > .table-responsive:first-child
+  > .table:first-child
+  > thead:first-child
+  > tr:first-child
+  td:last-child,
 .panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,
-.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,
+.panel
+  > .table-responsive:first-child
+  > .table:first-child
+  > tbody:first-child
+  > tr:first-child
+  td:last-child,
 .panel > .table:first-child > thead:first-child > tr:first-child th:last-child,
-.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,
+.panel
+  > .table-responsive:first-child
+  > .table:first-child
+  > thead:first-child
+  > tr:first-child
+  th:last-child,
 .panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,
-.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {
+.panel
+  > .table-responsive:first-child
+  > .table:first-child
+  > tbody:first-child
+  > tr:first-child
+  th:last-child {
   border-top-right-radius: 3px;
 }
 .panel > .table:last-child,
@@ -4769,30 +4964,78 @@ a.list-group-item-danger.active:focus {
   border-bottom-left-radius: 3px;
 }
 .panel > .table:last-child > tbody:last-child > tr:last-child,
-.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,
+.panel
+  > .table-responsive:last-child
+  > .table:last-child
+  > tbody:last-child
+  > tr:last-child,
 .panel > .table:last-child > tfoot:last-child > tr:last-child,
-.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {
+.panel
+  > .table-responsive:last-child
+  > .table:last-child
+  > tfoot:last-child
+  > tr:last-child {
   border-bottom-left-radius: 3px;
   border-bottom-right-radius: 3px;
 }
 .panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,
-.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,
+.panel
+  > .table-responsive:last-child
+  > .table:last-child
+  > tbody:last-child
+  > tr:last-child
+  td:first-child,
 .panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,
-.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,
+.panel
+  > .table-responsive:last-child
+  > .table:last-child
+  > tfoot:last-child
+  > tr:last-child
+  td:first-child,
 .panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,
-.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,
+.panel
+  > .table-responsive:last-child
+  > .table:last-child
+  > tbody:last-child
+  > tr:last-child
+  th:first-child,
 .panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,
-.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {
+.panel
+  > .table-responsive:last-child
+  > .table:last-child
+  > tfoot:last-child
+  > tr:last-child
+  th:first-child {
   border-bottom-left-radius: 3px;
 }
 .panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,
-.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,
+.panel
+  > .table-responsive:last-child
+  > .table:last-child
+  > tbody:last-child
+  > tr:last-child
+  td:last-child,
 .panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,
-.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,
+.panel
+  > .table-responsive:last-child
+  > .table:last-child
+  > tfoot:last-child
+  > tr:last-child
+  td:last-child,
 .panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,
-.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,
+.panel
+  > .table-responsive:last-child
+  > .table:last-child
+  > tbody:last-child
+  > tr:last-child
+  th:last-child,
 .panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,
-.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {
+.panel
+  > .table-responsive:last-child
+  > .table:last-child
+  > tfoot:last-child
+  > tr:last-child
+  th:last-child {
   border-bottom-right-radius: 3px;
 }
 .panel > .panel-body + .table,
@@ -5476,17 +5719,41 @@ button.close {
   text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
 }
 .carousel-control.left {
-  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);
-  background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);
-  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);
+  background-image: -webkit-linear-gradient(
+    left,
+    rgba(0, 0, 0, 0.5) 0%,
+    rgba(0, 0, 0, 0.0001) 100%
+  );
+  background-image: -o-linear-gradient(
+    left,
+    rgba(0, 0, 0, 0.5) 0%,
+    rgba(0, 0, 0, 0.0001) 100%
+  );
+  background-image: linear-gradient(
+    to right,
+    rgba(0, 0, 0, 0.5) 0%,
+    rgba(0, 0, 0, 0.0001) 100%
+  );
   background-repeat: repeat-x;
 }
 .carousel-control.right {
   left: auto;
   right: 0;
-  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);
-  background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);
-  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);
+  background-image: -webkit-linear-gradient(
+    left,
+    rgba(0, 0, 0, 0.0001) 0%,
+    rgba(0, 0, 0, 0.5) 100%
+  );
+  background-image: -o-linear-gradient(
+    left,
+    rgba(0, 0, 0, 0.0001) 0%,
+    rgba(0, 0, 0, 0.5) 100%
+  );
+  background-image: linear-gradient(
+    to right,
+    rgba(0, 0, 0, 0.0001) 0%,
+    rgba(0, 0, 0, 0.5) 100%
+  );
   background-repeat: repeat-x;
 }
 .carousel-control:hover,
@@ -5524,10 +5791,10 @@ button.close {
   font-family: serif;
 }
 .carousel-control .icon-prev::before {
-  content: '\2039';
+  content: "\2039";
 }
 .carousel-control .icon-next::before {
-  content: '\203a';
+  content: "\203a";
 }
 .carousel-indicators {
   position: absolute;
diff --git a/airflow/www/static/css/dags.css b/airflow/www/static/css/dags.css
index 517a538f39..19f1c5ef9f 100644
--- a/airflow/www/static/css/dags.css
+++ b/airflow/www/static/css/dags.css
@@ -72,7 +72,7 @@
 .dags-table-more__menu::before {
   width: 0;
   height: 100%;
-  content: '';
+  content: "";
   pointer-events: none;
   transition: width 0.3s ease-in;
 }
@@ -92,7 +92,11 @@ tr:nth-child(2n) .dags-table-more__menu:hover {
 }
 
 tr:nth-child(2n) .dags-table-more__menu:hover::before {
-  background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, #f9f9f9 100%);
+  background: linear-gradient(
+    to right,
+    rgba(255, 255, 255, 0) 0%,
+    #f9f9f9 100%
+  );
 }
 
 tr:nth-child(odd):hover .dags-table-more__menu:hover,
@@ -102,7 +106,11 @@ tr:nth-child(2n):hover .dags-table-more__menu:hover {
 
 tr:nth-child(odd):hover .dags-table-more__menu:hover::before,
 tr:nth-child(2n):hover .dags-table-more__menu:hover::before {
-  background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, #f5f5f5 100%);
+  background: linear-gradient(
+    to right,
+    rgba(255, 255, 255, 0) 0%,
+    #f5f5f5 100%
+  );
 }
 
 .dags-table-more__links {
diff --git a/airflow/www/static/css/flash.css b/airflow/www/static/css/flash.css
index 272d46c79f..21146948b3 100644
--- a/airflow/www/static/css/flash.css
+++ b/airflow/www/static/css/flash.css
@@ -19,7 +19,7 @@
 
 .panel-heading #alerts-accordion-toggle::after {
   /* symbol for "opening" panels */
-  font-family: FontAwesome;/* stylelint-disable-line font-family-no-missing-generic-family-keyword */
+  font-family: FontAwesome; /* stylelint-disable-line font-family-no-missing-generic-family-keyword */
   content: "\f077";
   float: right;
   color: grey;
@@ -27,7 +27,7 @@
 
 .panel-heading #alerts-accordion-toggle.collapsed::after {
   /* symbol for "closing" panels */
-  font-family: FontAwesome;/* stylelint-disable-line font-family-no-missing-generic-family-keyword */
+  font-family: FontAwesome; /* stylelint-disable-line font-family-no-missing-generic-family-keyword */
   content: "\f078";
   float: right;
   color: grey;
@@ -52,7 +52,7 @@
 
 .dag-import-error::after {
   /* symbol for "opening" panels */
-  font-family: FontAwesome;/* stylelint-disable-line font-family-no-missing-generic-family-keyword */
+  font-family: FontAwesome; /* stylelint-disable-line font-family-no-missing-generic-family-keyword */
   content: "\f078";
   float: right;
   color: #e43921;
diff --git a/airflow/www/static/css/main.css b/airflow/www/static/css/main.css
index 59a3d727c1..c625061c48 100644
--- a/airflow/www/static/css/main.css
+++ b/airflow/www/static/css/main.css
@@ -89,7 +89,7 @@ div.container {
 .brand-logo {
   width: 104px;
   height: 40px;
-  overflow: visible !important;/* Allow for animation */
+  overflow: visible !important; /* Allow for animation */
 }
 
 @keyframes pinSpin {
@@ -207,15 +207,15 @@ table.dataframe.dataTable thead > tr > th {
 }
 
 table.dataTable.dataframe thead .sorting {
-  background: url('./../sort_both.png') no-repeat center right;
+  background: url("./../sort_both.png") no-repeat center right;
 }
 
 table.dataTable.dataframe thead .sorting_desc {
-  background: url('./../sort_desc.png') no-repeat center right;
+  background: url("./../sort_desc.png") no-repeat center right;
 }
 
 table.dataTable.dataframe thead .sorting_asc {
-  background: url('./../sort_asc.png') no-repeat center right;
+  background: url("./../sort_asc.png") no-repeat center right;
 }
 
 .no-wrap {
@@ -304,59 +304,227 @@ label[for="timezone-other"],
   transform: rotate(180deg);
 }
 
-/* stylelint-disable declaration-block-single-line-max-declarations */
-.hll { background-color: #ffc; }
-.c { color: #408080; font-style: italic; } /* Comment */
-.err { border: 1px solid #f00; } /* Error */
-.k { color: #008000; font-weight: bold; } /* Keyword */
-.o { color: #666; } /* Operator */
-.cm { color: #408080; font-style: italic; } /* Comment.Multiline */
-.cp { color: #bc7a00; } /* Comment.Preproc */
-.c1 { color: #408080; font-style: italic; } /* Comment.Single */
-.cs { color: #408080; font-style: italic; } /* Comment.Special */
-.gd { color: #a00000; } /* Generic.Deleted */
-.ge { font-style: italic; } /* Generic.Emph */
-.gr { color: #f00; } /* Generic.Error */
-.gh { color: #000080; font-weight: bold; } /* Generic.Heading */
-.gi { color: #00a000; } /* Generic.Inserted */
-.go { color: #888; } /* Generic.Output */
-.gp { color: #000080; font-weight: bold; } /* Generic.Prompt */
-.gs { font-weight: bold; } /* Generic.Strong */
-.gu { color: #800080; font-weight: bold; } /* Generic.Subheading */
-.gt { color: #04d; } /* Generic.Traceback */
-.kc { color: #008000; font-weight: bold; } /* Keyword.Constant */
-.kd { color: #008000; font-weight: bold; } /* Keyword.Declaration */
-.kn { color: #008000; font-weight: bold; } /* Keyword.Namespace */
-.kp { color: #008000; } /* Keyword.Pseudo */
-.kr { color: #008000; font-weight: bold; } /* Keyword.Reserved */
-.kt { color: #b00040; } /* Keyword.Type */
-.m { color: #666; } /* Literal.Number */
-.s { color: #ba2121; } /* Literal.String */
-.na { color: #7d9029; } /* Name.Attribute */
-.nb { color: #008000; } /* Name.Builtin */
-.nc { color: #00f; font-weight: bold; } /* Name.Class */
-.no { color: #800; } /* Name.Constant */
-.nd { color: #a2f; } /* Name.Decorator */
-.ni { color: #999; font-weight: bold; } /* Name.Entity */
-.ne { color: #d2413a; font-weight: bold; } /* Name.Exception */
-.nf { color: #00f; } /* Name.Function */
-.nl { color: #a0a000; } /* Name.Label */
-.nn { color: #00f; font-weight: bold; } /* Name.Namespace */
-.nt { color: #008000; font-weight: bold; } /* Name.Tag */
-.nv { color: #19177c; } /* Name.Variable */
-.ow { color: #a2f; font-weight: bold; } /* Operator.Word */
-.w { color: #bbb; } /* Text.Whitespace */
-.mb { color: #666; } /* Literal.Number.Bin */
-.mf { color: #666; } /* Literal.Number.Float */
-.mh { color: #666; } /* Literal.Number.Hex */
-.mi { color: #666; } /* Literal.Number.Integer */
-.mo { color: #666; } /* Literal.Number.Oct */
-.sb { color: #ba2121; } /* Literal.String.Backtick */
-.sc { color: #ba2121; } /* Literal.String.Char */
-.sd { color: #ba2121; font-style: italic; } /* Literal.String.Doc */
-.s2 { color: #ba2121; } /* Literal.String.Double */
-.s1 { color: #ba2121; } /* Literal.String.Single */
-/* stylelint-enable declaration-block-single-line-max-declarations */
+.hll {
+  background-color: #ffc;
+}
+
+.c {
+  color: #408080;
+  font-style: italic;
+} /* Comment */
+
+.err {
+  border: 1px solid #f00;
+} /* Error */
+
+.k {
+  color: #008000;
+  font-weight: bold;
+} /* Keyword */
+
+.o {
+  color: #666;
+} /* Operator */
+
+.cm {
+  color: #408080;
+  font-style: italic;
+} /* Comment.Multiline */
+
+.cp {
+  color: #bc7a00;
+} /* Comment.Preproc */
+
+.c1 {
+  color: #408080;
+  font-style: italic;
+} /* Comment.Single */
+
+.cs {
+  color: #408080;
+  font-style: italic;
+} /* Comment.Special */
+
+.gd {
+  color: #a00000;
+} /* Generic.Deleted */
+.ge {
+  font-style: italic;
+} /* Generic.Emph */
+
+.gr {
+  color: #f00;
+} /* Generic.Error */
+
+.gh {
+  color: #000080;
+  font-weight: bold;
+} /* Generic.Heading */
+
+.gi {
+  color: #00a000;
+} /* Generic.Inserted */
+
+.go {
+  color: #888;
+} /* Generic.Output */
+
+.gp {
+  color: #000080;
+  font-weight: bold;
+} /* Generic.Prompt */
+
+.gs {
+  font-weight: bold;
+} /* Generic.Strong */
+
+.gu {
+  color: #800080;
+  font-weight: bold;
+} /* Generic.Subheading */
+
+.gt {
+  color: #04d;
+} /* Generic.Traceback */
+
+.kc {
+  color: #008000;
+  font-weight: bold;
+} /* Keyword.Constant */
+
+.kd {
+  color: #008000;
+  font-weight: bold;
+} /* Keyword.Declaration */
+
+.kn {
+  color: #008000;
+  font-weight: bold;
+} /* Keyword.Namespace */
+
+.kp {
+  color: #008000;
+} /* Keyword.Pseudo */
+
+.kr {
+  color: #008000;
+  font-weight: bold;
+} /* Keyword.Reserved */
+
+.kt {
+  color: #b00040;
+} /* Keyword.Type */
+
+.m {
+  color: #666;
+} /* Literal.Number */
+
+.s {
+  color: #ba2121;
+} /* Literal.String */
+
+.na {
+  color: #7d9029;
+} /* Name.Attribute */
+
+.nb {
+  color: #008000;
+} /* Name.Builtin */
+
+.nc {
+  color: #00f;
+  font-weight: bold;
+} /* Name.Class */
+
+.no {
+  color: #800;
+} /* Name.Constant */
+
+.nd {
+  color: #a2f;
+} /* Name.Decorator */
+
+.ni {
+  color: #999;
+  font-weight: bold;
+} /* Name.Entity */
+
+.ne {
+  color: #d2413a;
+  font-weight: bold;
+} /* Name.Exception */
+
+.nf {
+  color: #00f;
+} /* Name.Function */
+
+.nl {
+  color: #a0a000;
+} /* Name.Label */
+
+.nn {
+  color: #00f;
+  font-weight: bold;
+} /* Name.Namespace */
+
+.nt {
+  color: #008000;
+  font-weight: bold;
+} /* Name.Tag */
+
+.nv {
+  color: #19177c;
+} /* Name.Variable */
+
+.ow {
+  color: #a2f;
+  font-weight: bold;
+} /* Operator.Word */
+
+.w {
+  color: #bbb;
+} /* Text.Whitespace */
+
+.mb {
+  color: #666;
+} /* Literal.Number.Bin */
+
+.mf {
+  color: #666;
+} /* Literal.Number.Float */
+
+.mh {
+  color: #666;
+} /* Literal.Number.Hex */
+
+.mi {
+  color: #666;
+} /* Literal.Number.Integer */
+
+.mo {
+  color: #666;
+} /* Literal.Number.Oct */
+
+.sb {
+  color: #ba2121;
+} /* Literal.String.Backtick */
+
+.sc {
+  color: #ba2121;
+} /* Literal.String.Char */
+
+.sd {
+  color: #ba2121;
+  font-style: italic;
+} /* Literal.String.Doc */
+
+.s2 {
+  color: #ba2121;
+} /* Literal.String.Double */
+
+.s1 {
+  color: #ba2121;
+} /* Literal.String.Single */
 
 .footer {
   display: flex;
diff --git a/airflow/www/static/css/material-icons.css b/airflow/www/static/css/material-icons.css
index 12a44a42fa..9ab8255f90 100644
--- a/airflow/www/static/css/material-icons.css
+++ b/airflow/www/static/css/material-icons.css
@@ -18,17 +18,18 @@
  */
 
 @font-face {
-  font-family: 'Material Icons';
+  font-family: "Material Icons";
   font-style: normal;
   font-weight: 400;
-  src: url(data:font/woff2;base64,d09GMgABAAAAAUI8AA4AAAADjOAAAUHkAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhwbEByCzyoGYACwGhEICorJeIiAFAuwWAABNgIkA5gwBCAFgnoHIFuvznIDpyBryeWaoZ2jbV/h8F/QPjQVyrZdLHXexkto0GNMDdumQfSgOyhSwX2lL/v//89PKjJm20uyrrBtgAcBf70XMBlJPMc4ThIlIFVaT46i3HvL0aKMLvNMab5I12Ve1V2SfJsb7np3ScN7mPsRax9dxfBqO6Fz6tPkk0QcgdrGiFygr7T3/dJeUfRMt8IuknUFvw4YHYSVSIvQCSiexI3nU/9ARmj+Cg1SQI1DUV+UvEIe7pLu8yPlrKtRXgomrPXYSnkbMbh+g8Ucb37qPw1546MhtGBP9j9wq+diYLqeCtIikuEgaZG0SCpIJCZokFQUQCVIyLm75TABEy [...]
+  src: url(data:font/woff2;base64,d09GMgABAAAAAUI8AA4AAAADjOAAAUHkAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhwbEByCzyoGYACwGhEICorJeIiAFAuwWAABNgIkA5gwBCAFgnoHIFuvznIDpyBryeWaoZ2jbV/h8F/QPjQVyrZdLHXexkto0GNMDdumQfSgOyhSwX2lL/v//89PKjJm20uyrrBtgAcBf70XMBlJPMc4ThIlIFVaT46i3HvL0aKMLvNMab5I12Ve1V2SfJsb7np3ScN7mPsRax9dxfBqO6Fz6tPkk0QcgdrGiFygr7T3/dJeUfRMt8IuknUFvw4YHYSVSIvQCSiexI3nU/9ARmj+Cg1SQI1DUV+UvEIe7pLu8yPlrKtRXgomrPXYSnkbMbh+g8Ucb37qPw1546MhtGBP9j9wq+diYLqeCtIikuEgaZG0SCpIJCZokFQUQCVIyLm75TABEy [...]
+    format("woff2");
 }
 
 .material-icons {
-  font-family: 'Material Icons'; /* stylelint-disable font-family-no-missing-generic-family-keyword */
+  font-family: "Material Icons"; /* stylelint-disable font-family-no-missing-generic-family-keyword */
   font-weight: normal;
   font-style: normal;
-  font-size: 20px;  /* Default icon size */
+  font-size: 20px; /* Default icon size */
   display: inline-block;
   line-height: 1;
   text-transform: none;
@@ -50,13 +51,24 @@
   -moz-osx-font-smoothing: grayscale;
 
   /* Support for IE. */
-  font-feature-settings: 'liga';
+  font-feature-settings: "liga";
 }
 
-.material-icons.md-18 { font-size: 18px; }
-.material-icons.md-24 { font-size: 24px; }
-.material-icons.md-36 { font-size: 36px; }
-.material-icons.md-48 { font-size: 48px; }
+.material-icons.md-18 {
+  font-size: 18px;
+}
+
+.material-icons.md-24 {
+  font-size: 24px;
+}
+
+.material-icons.md-36 {
+  font-size: 36px;
+}
+
+.material-icons.md-48 {
+  font-size: 48px;
+}
 
 .dropdown-menu > li > a > .material-icons {
   margin-right: 6px;
diff --git a/airflow/www/static/css/switch.css b/airflow/www/static/css/switch.css
index 4890641f55..39d5db4461 100644
--- a/airflow/www/static/css/switch.css
+++ b/airflow/www/static/css/switch.css
@@ -57,7 +57,7 @@
   border-radius: 50%;
   width: 1.5rem;
   height: 1.5rem;
-  content: '';
+  content: "";
   background-color: #edecec;
   transition-timing-function: ease-in-out;
   transition-duration: 0.25s;
diff --git a/airflow/www/static/js/App.tsx b/airflow/www/static/js/App.tsx
index cbdd3fa096..6602a0ae9e 100644
--- a/airflow/www/static/js/App.tsx
+++ b/airflow/www/static/js/App.tsx
@@ -21,22 +21,22 @@
   Base setup for anywhere we add react to the UI
 */
 
-import React, { PropsWithChildren } from 'react';
-import { BrowserRouter } from 'react-router-dom';
-import { ChakraProvider } from '@chakra-ui/react';
-import { CacheProvider } from '@emotion/react';
-import type { EmotionCache } from '@emotion/cache';
-import { QueryClient, QueryClientProvider } from 'react-query';
+import React, { PropsWithChildren } from "react";
+import { BrowserRouter } from "react-router-dom";
+import { ChakraProvider } from "@chakra-ui/react";
+import { CacheProvider } from "@emotion/react";
+import type { EmotionCache } from "@emotion/cache";
+import { QueryClient, QueryClientProvider } from "react-query";
 
-import theme from './theme';
-import { ContainerRefProvider, useContainerRef } from './context/containerRef';
-import { TimezoneProvider } from './context/timezone';
-import { AutoRefreshProvider } from './context/autorefresh';
+import theme from "./theme";
+import { ContainerRefProvider, useContainerRef } from "./context/containerRef";
+import { TimezoneProvider } from "./context/timezone";
+import { AutoRefreshProvider } from "./context/autorefresh";
 
 const queryClient = new QueryClient({
   defaultOptions: {
     queries: {
-      notifyOnChangeProps: 'tracked',
+      notifyOnChangeProps: "tracked",
       refetchOnWindowFocus: false,
       retry: 1,
       retryDelay: 500,
@@ -59,13 +59,14 @@ interface AppProps extends PropsWithChildren {
 const ChakraApp = ({ children }: PropsWithChildren) => {
   const containerRef = useContainerRef();
   return (
-    <ChakraProvider theme={theme} toastOptions={{ portalProps: { containerRef } }}>
+    <ChakraProvider
+      theme={theme}
+      toastOptions={{ portalProps: { containerRef } }}
+    >
       <QueryClientProvider client={queryClient}>
         <TimezoneProvider>
           <AutoRefreshProvider>
-            <BrowserRouter>
-              {children}
-            </BrowserRouter>
+            <BrowserRouter>{children}</BrowserRouter>
           </AutoRefreshProvider>
         </TimezoneProvider>
       </QueryClientProvider>
@@ -78,9 +79,7 @@ function App({ children, cache }: AppProps) {
     <React.StrictMode>
       <CacheProvider value={cache}>
         <ContainerRefProvider>
-          <ChakraApp>
-            {children}
-          </ChakraApp>
+          <ChakraApp>{children}</ChakraApp>
         </ContainerRefProvider>
       </CacheProvider>
     </React.StrictMode>
diff --git a/airflow/www/static/js/README.md b/airflow/www/static/js/README.md
index c17c584977..1b4c6607a6 100644
--- a/airflow/www/static/js/README.md
+++ b/airflow/www/static/js/README.md
@@ -27,20 +27,17 @@ The most popular javascript framework for building user interfaces with reusable
 Written as javascript and html together in `.jsx` files.
 In-component state can be managed via `useState()`, application state that spans many components can be managed via a context provider (see `/context` for examples), API state can be managed by React Query (see below)
 
-
 ## [Chakra UI](https://chakra-ui.com/)
 
 A good component and helper function library. Tooltips, modals, toasts, switches, etc are all out of the box
 Styles are applied via global theme when initializing the app or inline with individual components like `<Box padding="5px" />`
 
-
 ## [React Query](https://react-query.tanstack.com/)
 
 A powerful async data handler that makes it easy to manage loading/error states as well as caching, refetching, background updates, etc.
 This is our state management for any data that comes from an API.
 Each API request is its own hook. Ie `useTasks` will get all the tasks for a DAG
 
-
 ## [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/)
 
 Easily write tests for react components and hooks
diff --git a/airflow/www/static/js/api/index.ts b/airflow/www/static/js/api/index.ts
index d055f737d9..0749565f6f 100644
--- a/airflow/www/static/js/api/index.ts
+++ b/airflow/www/static/js/api/index.ts
@@ -17,34 +17,34 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from 'axios';
-import camelcaseKeys from 'camelcase-keys';
+import axios, { AxiosResponse } from "axios";
+import camelcaseKeys from "camelcase-keys";
 
-import useClearRun from './useClearRun';
-import useQueueRun from './useQueueRun';
-import useMarkFailedRun from './useMarkFailedRun';
-import useMarkSuccessRun from './useMarkSuccessRun';
-import useClearTask from './useClearTask';
-import useMarkFailedTask from './useMarkFailedTask';
-import useMarkSuccessTask from './useMarkSuccessTask';
-import useExtraLinks from './useExtraLinks';
-import useConfirmMarkTask from './useConfirmMarkTask';
-import useGridData from './useGridData';
-import useMappedInstances from './useMappedInstances';
-import useDatasets from './useDatasets';
-import useDataset from './useDataset';
-import useDatasetDependencies from './useDatasetDependencies';
-import useDatasetEvents from './useDatasetEvents';
-import useSetDagRunNote from './useSetDagRunNote';
-import useSetTaskInstanceNote from './useSetTaskInstanceNote';
-import useUpstreamDatasetEvents from './useUpstreamDatasetEvents';
-import useTaskInstance from './useTaskInstance';
+import useClearRun from "./useClearRun";
+import useQueueRun from "./useQueueRun";
+import useMarkFailedRun from "./useMarkFailedRun";
+import useMarkSuccessRun from "./useMarkSuccessRun";
+import useClearTask from "./useClearTask";
+import useMarkFailedTask from "./useMarkFailedTask";
+import useMarkSuccessTask from "./useMarkSuccessTask";
+import useExtraLinks from "./useExtraLinks";
+import useConfirmMarkTask from "./useConfirmMarkTask";
+import useGridData from "./useGridData";
+import useMappedInstances from "./useMappedInstances";
+import useDatasets from "./useDatasets";
+import useDataset from "./useDataset";
+import useDatasetDependencies from "./useDatasetDependencies";
+import useDatasetEvents from "./useDatasetEvents";
+import useSetDagRunNote from "./useSetDagRunNote";
+import useSetTaskInstanceNote from "./useSetTaskInstanceNote";
+import useUpstreamDatasetEvents from "./useUpstreamDatasetEvents";
+import useTaskInstance from "./useTaskInstance";
 
-axios.interceptors.response.use(
-  (res: AxiosResponse) => (res.data ? camelcaseKeys(res.data, { deep: true }) : res),
+axios.interceptors.response.use((res: AxiosResponse) =>
+  res.data ? camelcaseKeys(res.data, { deep: true }) : res
 );
 
-axios.defaults.headers.common.Accept = 'application/json';
+axios.defaults.headers.common.Accept = "application/json";
 
 export {
   useClearRun,
diff --git a/airflow/www/static/js/api/useClearRun.ts b/airflow/www/static/js/api/useClearRun.ts
index c7b32b79fb..bc61729054 100644
--- a/airflow/www/static/js/api/useClearRun.ts
+++ b/airflow/www/static/js/api/useClearRun.ts
@@ -17,23 +17,23 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from 'axios';
-import { useMutation, useQueryClient } from 'react-query';
+import axios, { AxiosResponse } from "axios";
+import { useMutation, useQueryClient } from "react-query";
 
-import URLSearchParamsWrapper from 'src/utils/URLSearchParamWrapper';
-import { getMetaValue } from 'src/utils';
-import { useAutoRefresh } from 'src/context/autorefresh';
-import useErrorToast from 'src/utils/useErrorToast';
+import URLSearchParamsWrapper from "src/utils/URLSearchParamWrapper";
+import { getMetaValue } from "src/utils";
+import { useAutoRefresh } from "src/context/autorefresh";
+import useErrorToast from "src/utils/useErrorToast";
 
-const csrfToken = getMetaValue('csrf_token');
-const clearRunUrl = getMetaValue('dagrun_clear_url');
+const csrfToken = getMetaValue("csrf_token");
+const clearRunUrl = getMetaValue("dagrun_clear_url");
 
 export default function useClearRun(dagId: string, runId: string) {
   const queryClient = useQueryClient();
   const errorToast = useErrorToast();
   const { startRefresh } = useAutoRefresh();
   return useMutation(
-    ['dagRunClear', dagId, runId],
+    ["dagRunClear", dagId, runId],
     ({ confirmed = false }: { confirmed: boolean }) => {
       const params = new URLSearchParamsWrapper({
         csrf_token: csrfToken,
@@ -44,18 +44,18 @@ export default function useClearRun(dagId: string, runId: string) {
 
       return axios.post<AxiosResponse, string[]>(clearRunUrl, params, {
         headers: {
-          'Content-Type': 'application/x-www-form-urlencoded',
+          "Content-Type": "application/x-www-form-urlencoded",
         },
       });
     },
     {
       onSuccess: (_, { confirmed }) => {
         if (confirmed) {
-          queryClient.invalidateQueries('gridData');
+          queryClient.invalidateQueries("gridData");
           startRefresh();
         }
       },
       onError: (error: Error) => errorToast({ error }),
-    },
+    }
   );
 }
diff --git a/airflow/www/static/js/api/useClearTask.ts b/airflow/www/static/js/api/useClearTask.ts
index b6c10f1637..ebe3b14b28 100644
--- a/airflow/www/static/js/api/useClearTask.ts
+++ b/airflow/www/static/js/api/useClearTask.ts
@@ -17,39 +17,54 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from 'axios';
-import { useMutation, useQueryClient } from 'react-query';
-import URLSearchParamsWrapper from 'src/utils/URLSearchParamWrapper';
-import { getMetaValue } from '../utils';
-import { useAutoRefresh } from '../context/autorefresh';
-import useErrorToast from '../utils/useErrorToast';
+import axios, { AxiosResponse } from "axios";
+import { useMutation, useQueryClient } from "react-query";
+import URLSearchParamsWrapper from "src/utils/URLSearchParamWrapper";
+import { getMetaValue } from "../utils";
+import { useAutoRefresh } from "../context/autorefresh";
+import useErrorToast from "../utils/useErrorToast";
 
-const csrfToken = getMetaValue('csrf_token');
-const clearUrl = getMetaValue('clear_url');
+const csrfToken = getMetaValue("csrf_token");
+const clearUrl = getMetaValue("clear_url");
 
 export default function useClearTask({
-  dagId, runId, taskId, executionDate, isGroup,
-}: { dagId: string,
-  runId: string,
-  taskId: string,
-  executionDate: string,
-  isGroup: boolean }) {
+  dagId,
+  runId,
+  taskId,
+  executionDate,
+  isGroup,
+}: {
+  dagId: string;
+  runId: string;
+  taskId: string;
+  executionDate: string;
+  isGroup: boolean;
+}) {
   const queryClient = useQueryClient();
   const errorToast = useErrorToast();
   const { startRefresh } = useAutoRefresh();
 
   return useMutation(
-    ['clearTask', dagId, runId, taskId],
+    ["clearTask", dagId, runId, taskId],
     ({
-      past, future, upstream, downstream, recursive, failed, confirmed, mapIndexes = [],
-    }: { past: boolean,
-      future: boolean,
-      upstream: boolean,
-      downstream: boolean,
-      recursive: boolean,
-      failed: boolean,
-      confirmed: boolean,
-      mapIndexes: number[] }) => {
+      past,
+      future,
+      upstream,
+      downstream,
+      recursive,
+      failed,
+      confirmed,
+      mapIndexes = [],
+    }: {
+      past: boolean;
+      future: boolean;
+      upstream: boolean;
+      downstream: boolean;
+      recursive: boolean;
+      failed: boolean;
+      confirmed: boolean;
+      mapIndexes: number[];
+    }) => {
       const params = new URLSearchParamsWrapper({
         csrf_token: csrfToken,
         dag_id: dagId,
@@ -65,30 +80,35 @@ export default function useClearTask({
       });
 
       if (isGroup) {
-        params.append('group_id', taskId);
+        params.append("group_id", taskId);
       } else {
-        params.append('task_id', taskId);
+        params.append("task_id", taskId);
       }
 
       mapIndexes.forEach((mi: number) => {
-        params.append('map_index', mi.toString());
+        params.append("map_index", mi.toString());
       });
 
       return axios.post<AxiosResponse, string[]>(clearUrl, params.toString(), {
         headers: {
-          'Content-Type': 'application/x-www-form-urlencoded',
+          "Content-Type": "application/x-www-form-urlencoded",
         },
       });
     },
     {
       onSuccess: (_, { confirmed }) => {
         if (confirmed) {
-          queryClient.invalidateQueries('gridData');
-          queryClient.invalidateQueries(['mappedInstances', dagId, runId, taskId]);
+          queryClient.invalidateQueries("gridData");
+          queryClient.invalidateQueries([
+            "mappedInstances",
+            dagId,
+            runId,
+            taskId,
+          ]);
           startRefresh();
         }
       },
       onError: (error: Error) => errorToast({ error }),
-    },
+    }
   );
 }
diff --git a/airflow/www/static/js/api/useConfirmMarkTask.ts b/airflow/www/static/js/api/useConfirmMarkTask.ts
index ba654798c4..4e69875ffb 100644
--- a/airflow/www/static/js/api/useConfirmMarkTask.ts
+++ b/airflow/www/static/js/api/useConfirmMarkTask.ts
@@ -17,29 +17,41 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from 'axios';
-import { useMutation } from 'react-query';
-import type { TaskState } from 'src/types';
-import URLSearchParamsWrapper from 'src/utils/URLSearchParamWrapper';
-import { getMetaValue } from '../utils';
-import useErrorToast from '../utils/useErrorToast';
+import axios, { AxiosResponse } from "axios";
+import { useMutation } from "react-query";
+import type { TaskState } from "src/types";
+import URLSearchParamsWrapper from "src/utils/URLSearchParamWrapper";
+import { getMetaValue } from "../utils";
+import useErrorToast from "../utils/useErrorToast";
 
-const confirmUrl = getMetaValue('confirm_url');
+const confirmUrl = getMetaValue("confirm_url");
 
 export default function useConfirmMarkTask({
-  dagId, runId, taskId, state,
-}: { dagId: string, runId: string, taskId: string, state: TaskState }) {
+  dagId,
+  runId,
+  taskId,
+  state,
+}: {
+  dagId: string;
+  runId: string;
+  taskId: string;
+  state: TaskState;
+}) {
   const errorToast = useErrorToast();
   return useMutation(
-    ['confirmStateChange', dagId, runId, taskId, state],
+    ["confirmStateChange", dagId, runId, taskId, state],
     ({
-      past, future, upstream, downstream, mapIndexes = [],
+      past,
+      future,
+      upstream,
+      downstream,
+      mapIndexes = [],
     }: {
-      past: boolean,
-      future: boolean,
-      upstream: boolean,
-      downstream: boolean,
-      mapIndexes: number[],
+      past: boolean;
+      future: boolean;
+      upstream: boolean;
+      downstream: boolean;
+      mapIndexes: number[];
     }) => {
       const params = new URLSearchParamsWrapper({
         dag_id: dagId,
@@ -53,12 +65,12 @@ export default function useConfirmMarkTask({
       });
 
       mapIndexes.forEach((mi: number) => {
-        params.append('map_index', mi.toString());
+        params.append("map_index", mi.toString());
       });
       return axios.get<AxiosResponse, string[]>(confirmUrl, { params });
     },
     {
       onError: (error: Error) => errorToast({ error }),
-    },
+    }
   );
 }
diff --git a/airflow/www/static/js/api/useDataset.ts b/airflow/www/static/js/api/useDataset.ts
index 4633b47242..4793464fac 100644
--- a/airflow/www/static/js/api/useDataset.ts
+++ b/airflow/www/static/js/api/useDataset.ts
@@ -17,22 +17,22 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from 'axios';
-import { useQuery } from 'react-query';
+import axios, { AxiosResponse } from "axios";
+import { useQuery } from "react-query";
 
-import { getMetaValue } from 'src/utils';
-import type { API } from 'src/types';
+import { getMetaValue } from "src/utils";
+import type { API } from "src/types";
 
 interface Props {
   uri: string;
 }
 
 export default function useDataset({ uri }: Props) {
-  return useQuery(
-    ['dataset', uri],
-    () => {
-      const datasetUrl = getMetaValue('dataset_api').replace('__URI__', encodeURIComponent(uri));
-      return axios.get<AxiosResponse, API.Dataset>(datasetUrl);
-    },
-  );
+  return useQuery(["dataset", uri], () => {
+    const datasetUrl = getMetaValue("dataset_api").replace(
+      "__URI__",
+      encodeURIComponent(uri)
+    );
+    return axios.get<AxiosResponse, API.Dataset>(datasetUrl);
+  });
 }
diff --git a/airflow/www/static/js/api/useDatasetDependencies.ts b/airflow/www/static/js/api/useDatasetDependencies.ts
index a4167c6f8f..12e46c266b 100644
--- a/airflow/www/static/js/api/useDatasetDependencies.ts
+++ b/airflow/www/static/js/api/useDatasetDependencies.ts
@@ -17,13 +17,13 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from 'axios';
-import { useQuery } from 'react-query';
-import ELK, { ElkShape, ElkExtendedEdge } from 'elkjs';
+import axios, { AxiosResponse } from "axios";
+import { useQuery } from "react-query";
+import ELK, { ElkShape, ElkExtendedEdge } from "elkjs";
 
-import { getMetaValue } from 'src/utils';
-import type { DepEdge, DepNode } from 'src/types';
-import type { NodeType } from 'src/datasets/Graph/Node';
+import { getMetaValue } from "src/utils";
+import type { DepEdge, DepNode } from "src/types";
+import type { NodeType } from "src/datasets/Graph/Node";
 
 interface DatasetDependencies {
   edges: DepEdge[];
@@ -52,7 +52,7 @@ interface Data {
 
 // Take text and font to calculate how long each node should be
 function getTextWidth(text: string, font: string) {
-  const context = document.createElement('canvas').getContext('2d');
+  const context = document.createElement("canvas").getContext("2d");
   if (context) {
     context.font = font;
     const metrics = context.measureText(text);
@@ -62,18 +62,18 @@ function getTextWidth(text: string, font: string) {
 }
 
 const generateGraph = ({ nodes, edges, font }: GenerateProps) => ({
-  id: 'root',
+  id: "root",
   layoutOptions: {
-    'spacing.nodeNodeBetweenLayers': '40.0',
-    'spacing.edgeNodeBetweenLayers': '10.0',
-    'layering.strategy': 'INTERACTIVE',
-    algorithm: 'layered',
-    'crossingMinimization.semiInteractive': 'true',
-    'spacing.edgeEdgeBetweenLayers': '10.0',
-    'spacing.edgeNode': '10.0',
-    'spacing.edgeEdge': '10.0',
-    'spacing.nodeNode': '20.0',
-    'elk.direction': 'DOWN',
+    "spacing.nodeNodeBetweenLayers": "40.0",
+    "spacing.edgeNodeBetweenLayers": "10.0",
+    "layering.strategy": "INTERACTIVE",
+    algorithm: "layered",
+    "crossingMinimization.semiInteractive": "true",
+    "spacing.edgeEdgeBetweenLayers": "10.0",
+    "spacing.edgeNode": "10.0",
+    "spacing.edgeEdge": "10.0",
+    "spacing.nodeNode": "20.0",
+    "elk.direction": "DOWN",
   },
   children: nodes.map(({ id, value }) => ({
     id,
@@ -82,7 +82,11 @@ const generateGraph = ({ nodes, edges, font }: GenerateProps) => ({
     height: 40,
     value,
   })),
-  edges: edges.map((e) => ({ id: `${e.source}-${e.target}`, sources: [e.source], targets: [e.target] })),
+  edges: edges.map((e) => ({
+    id: `${e.source}-${e.target}`,
+    sources: [e.source],
+    targets: [e.target],
+  })),
 });
 
 interface SeparateGraphsProps {
@@ -91,43 +95,49 @@ interface SeparateGraphsProps {
 }
 
 // find the downstream graph of each upstream edge
-const findDownstreamGraph = (
-  { edges, graphs = [] }: SeparateGraphsProps,
-): EdgeGroup[] => {
+const findDownstreamGraph = ({
+  edges,
+  graphs = [],
+}: SeparateGraphsProps): EdgeGroup[] => {
   let unassignedEdges = [...edges];
 
   const mergedGraphs = graphs
-    .reduce(
-      (newGraphs, graph) => {
-        const otherGroupIndex = newGraphs.findIndex(
-          (otherGroup) => otherGroup.edges.some(
-            (otherEdge) => graph.edges.some(
-              (edge) => edge.target === otherEdge.target,
-            ),
-          ),
-        );
-        if (otherGroupIndex === -1) {
-          return [...newGraphs, graph];
-        }
-
-        const mergedEdges = [...newGraphs[otherGroupIndex].edges, ...graph.edges]
-          .filter((edge, edgeIndex, otherEdges) => (
-            edgeIndex === otherEdges.findIndex(
-              (otherEdge) => otherEdge.source === edge.source && otherEdge.target === edge.target,
-            )
-          ));
-        return [
-          ...newGraphs.filter((_, newGraphIndex) => newGraphIndex !== otherGroupIndex),
-          { edges: mergedEdges },
-        ];
-      },
-      [] as EdgeGroup[],
-    )
+    .reduce((newGraphs, graph) => {
+      const otherGroupIndex = newGraphs.findIndex((otherGroup) =>
+        otherGroup.edges.some((otherEdge) =>
+          graph.edges.some((edge) => edge.target === otherEdge.target)
+        )
+      );
+      if (otherGroupIndex === -1) {
+        return [...newGraphs, graph];
+      }
+
+      const mergedEdges = [
+        ...newGraphs[otherGroupIndex].edges,
+        ...graph.edges,
+      ].filter(
+        (edge, edgeIndex, otherEdges) =>
+          edgeIndex ===
+          otherEdges.findIndex(
+            (otherEdge) =>
+              otherEdge.source === edge.source &&
+              otherEdge.target === edge.target
+          )
+      );
+      return [
+        ...newGraphs.filter(
+          (_, newGraphIndex) => newGraphIndex !== otherGroupIndex
+        ),
+        { edges: mergedEdges },
+      ];
+    }, [] as EdgeGroup[])
     .map((graph) => {
       // find the next set of downstream edges and filter them out of the unassigned edges list
       const downstreamEdges: DepEdge[] = [];
       unassignedEdges = unassignedEdges.filter((edge) => {
-        const isDownstream = graph.edges.some((graphEdge) => graphEdge.target === edge.source);
+        const isDownstream = graph.edges.some(
+          (graphEdge) => graphEdge.target === edge.source
+        );
         if (isDownstream) downstreamEdges.push(edge);
         return !isDownstream;
       });
@@ -144,7 +154,10 @@ const findDownstreamGraph = (
 };
 
 // separate the list of nodes/edges into distinct dataset pipeline graphs
-const separateGraphs = ({ edges, nodes }: DatasetDependencies): DatasetDependencies[] => {
+const separateGraphs = ({
+  edges,
+  nodes,
+}: DatasetDependencies): DatasetDependencies[] => {
   const separatedGraphs: EdgeGroup[] = [];
   const remainingEdges: DepEdge[] = [];
 
@@ -157,19 +170,20 @@ const separateGraphs = ({ edges, nodes }: DatasetDependencies): DatasetDependenc
     }
   });
 
-  const edgeGraphs = findDownstreamGraph({ edges: remainingEdges, graphs: separatedGraphs });
+  const edgeGraphs = findDownstreamGraph({
+    edges: remainingEdges,
+    graphs: separatedGraphs,
+  });
 
   // once all the edges are found, add the nodes
   return edgeGraphs.map((eg) => {
-    const graphNodes = nodes.filter(
-      (n) => eg.edges.some(
-        (e) => e.target === n.id || e.source === n.id,
-      ),
+    const graphNodes = nodes.filter((n) =>
+      eg.edges.some((e) => e.target === n.id || e.source === n.id)
     );
-    return ({
+    return {
       edges: eg.edges,
       nodes: graphNodes,
-    });
+    };
   });
 };
 
@@ -179,12 +193,16 @@ const formatDependencies = async ({ edges, nodes }: DatasetDependencies) => {
   const graphs = separateGraphs({ edges, nodes });
 
   // get computed style to calculate how large each node should be
-  const font = `bold ${16}px ${window.getComputedStyle(document.body).fontFamily}`;
+  const font = `bold ${16}px ${
+    window.getComputedStyle(document.body).fontFamily
+  }`;
 
   // Finally generate the graph data with elk
-  const subGraphs = await Promise.all(graphs.map(async (g) => (
-    elk.layout(generateGraph({ nodes: g.nodes, edges: g.edges, font }))
-  )));
+  const subGraphs = await Promise.all(
+    graphs.map(async (g) =>
+      elk.layout(generateGraph({ nodes: g.nodes, edges: g.edges, font }))
+    )
+  );
   const fullGraph = await elk.layout(generateGraph({ nodes, edges, font }));
 
   return {
@@ -194,12 +212,11 @@ const formatDependencies = async ({ edges, nodes }: DatasetDependencies) => {
 };
 
 export default function useDatasetDependencies() {
-  return useQuery(
-    'datasetDependencies',
-    async () => {
-      const datasetDepsUrl = getMetaValue('dataset_dependencies_url');
-      const rawData = await axios.get<AxiosResponse, DatasetDependencies>(datasetDepsUrl);
-      return formatDependencies(rawData);
-    },
-  );
+  return useQuery("datasetDependencies", async () => {
+    const datasetDepsUrl = getMetaValue("dataset_dependencies_url");
+    const rawData = await axios.get<AxiosResponse, DatasetDependencies>(
+      datasetDepsUrl
+    );
+    return formatDependencies(rawData);
+  });
 }
diff --git a/airflow/www/static/js/api/useDatasetEvents.ts b/airflow/www/static/js/api/useDatasetEvents.ts
index b67423a0ba..8caccd202c 100644
--- a/airflow/www/static/js/api/useDatasetEvents.ts
+++ b/airflow/www/static/js/api/useDatasetEvents.ts
@@ -17,31 +17,49 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from 'axios';
-import { useQuery } from 'react-query';
+import axios, { AxiosResponse } from "axios";
+import { useQuery } from "react-query";
 
-import { getMetaValue } from 'src/utils';
-import type { API } from 'src/types';
-import URLSearchParamsWrapper from 'src/utils/URLSearchParamWrapper';
+import { getMetaValue } from "src/utils";
+import type { API } from "src/types";
+import URLSearchParamsWrapper from "src/utils/URLSearchParamWrapper";
 
 export default function useDatasetEvents({
-  datasetId, sourceDagId, sourceRunId, sourceTaskId, sourceMapIndex, limit, offset, orderBy,
+  datasetId,
+  sourceDagId,
+  sourceRunId,
+  sourceTaskId,
+  sourceMapIndex,
+  limit,
+  offset,
+  orderBy,
 }: API.GetDatasetEventsVariables) {
   const query = useQuery(
-    ['datasets-events', datasetId, sourceDagId, sourceRunId, sourceTaskId, sourceMapIndex, limit, offset, orderBy],
+    [
+      "datasets-events",
+      datasetId,
+      sourceDagId,
+      sourceRunId,
+      sourceTaskId,
+      sourceMapIndex,
+      limit,
+      offset,
+      orderBy,
+    ],
     () => {
-      const datasetsUrl = getMetaValue('dataset_events_api');
+      const datasetsUrl = getMetaValue("dataset_events_api");
 
       const params = new URLSearchParamsWrapper();
 
-      if (limit) params.set('limit', limit.toString());
-      if (offset) params.set('offset', offset.toString());
-      if (orderBy) params.set('order_by', orderBy);
-      if (datasetId) params.set('dataset_id', datasetId.toString());
-      if (sourceDagId) params.set('source_dag_id', sourceDagId);
-      if (sourceRunId) params.set('source_run_id', sourceRunId);
-      if (sourceTaskId) params.set('source_task_id', sourceTaskId);
-      if (sourceMapIndex) params.set('source_map_index', sourceMapIndex.toString());
+      if (limit) params.set("limit", limit.toString());
+      if (offset) params.set("offset", offset.toString());
+      if (orderBy) params.set("order_by", orderBy);
+      if (datasetId) params.set("dataset_id", datasetId.toString());
+      if (sourceDagId) params.set("source_dag_id", sourceDagId);
+      if (sourceRunId) params.set("source_run_id", sourceRunId);
+      if (sourceTaskId) params.set("source_task_id", sourceTaskId);
+      if (sourceMapIndex)
+        params.set("source_map_index", sourceMapIndex.toString());
 
       return axios.get<AxiosResponse, API.DatasetEventCollection>(datasetsUrl, {
         params,
@@ -49,7 +67,7 @@ export default function useDatasetEvents({
     },
     {
       keepPreviousData: true,
-    },
+    }
   );
   return {
     ...query,
diff --git a/airflow/www/static/js/api/useDatasets.ts b/airflow/www/static/js/api/useDatasets.ts
index 6c459a15b4..379d3c3e61 100644
--- a/airflow/www/static/js/api/useDatasets.ts
+++ b/airflow/www/static/js/api/useDatasets.ts
@@ -17,12 +17,12 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from 'axios';
-import { useQuery } from 'react-query';
+import axios, { AxiosResponse } from "axios";
+import { useQuery } from "react-query";
 
-import { getMetaValue } from 'src/utils';
-import type { DatasetListItem } from 'src/types';
-import type { unitOfTime } from 'moment';
+import { getMetaValue } from "src/utils";
+import type { DatasetListItem } from "src/types";
+import type { unitOfTime } from "moment";
 
 interface DatasetsData {
   datasets: DatasetListItem[];
@@ -43,33 +43,39 @@ interface Props {
 }
 
 export default function useDatasets({
-  limit, offset, order, uri, updatedAfter,
+  limit,
+  offset,
+  order,
+  uri,
+  updatedAfter,
 }: Props) {
   const query = useQuery(
-    ['datasets', limit, offset, order, uri, updatedAfter],
+    ["datasets", limit, offset, order, uri, updatedAfter],
     () => {
-      const datasetsUrl = getMetaValue('datasets_api');
+      const datasetsUrl = getMetaValue("datasets_api");
       const orderParam = order ? { order_by: order } : {};
       const uriParam = uri ? { uri_pattern: uri } : {};
-      const updatedAfterParam = updatedAfter && updatedAfter.count && updatedAfter.unit
-        ? { updated_after: moment().subtract(updatedAfter.count, updatedAfter.unit).toISOString() }
-        : {};
-      return axios.get<AxiosResponse, DatasetsData>(
-        datasetsUrl,
-        {
-          params: {
-            offset,
-            limit,
-            ...orderParam,
-            ...uriParam,
-            ...updatedAfterParam,
-          },
+      const updatedAfterParam =
+        updatedAfter && updatedAfter.count && updatedAfter.unit
+          ? {
+              updated_after: moment()
+                .subtract(updatedAfter.count, updatedAfter.unit)
+                .toISOString(),
+            }
+          : {};
+      return axios.get<AxiosResponse, DatasetsData>(datasetsUrl, {
+        params: {
+          offset,
+          limit,
+          ...orderParam,
+          ...uriParam,
+          ...updatedAfterParam,
         },
-      );
+      });
     },
     {
       keepPreviousData: true,
-    },
+    }
   );
   return {
     ...query,
diff --git a/airflow/www/static/js/api/useExtraLinks.ts b/airflow/www/static/js/api/useExtraLinks.ts
index 76fe4610bb..a76cb2540c 100644
--- a/airflow/www/static/js/api/useExtraLinks.ts
+++ b/airflow/www/static/js/api/useExtraLinks.ts
@@ -17,11 +17,11 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from 'axios';
-import { useQuery } from 'react-query';
-import { getMetaValue } from '../utils';
+import axios, { AxiosResponse } from "axios";
+import { useQuery } from "react-query";
+import { getMetaValue } from "../utils";
 
-const extraLinksUrl = getMetaValue('extra_links_url');
+const extraLinksUrl = getMetaValue("extra_links_url");
 
 interface LinkData {
   url: string | null;
@@ -29,19 +29,26 @@ interface LinkData {
 }
 
 export default function useExtraLinks({
-  dagId, taskId, executionDate, extraLinks,
+  dagId,
+  taskId,
+  executionDate,
+  extraLinks,
 }: {
-  dagId: string, taskId: string, executionDate: string, extraLinks: string[],
+  dagId: string;
+  taskId: string;
+  executionDate: string;
+  extraLinks: string[];
 }) {
-  return useQuery(
-    ['extraLinks', dagId, taskId, executionDate],
-    async () => {
-      const data = await Promise.all(extraLinks.map(async (link) => {
-        const url = `${extraLinksUrl
-        }?task_id=${encodeURIComponent(taskId)
-        }&dag_id=${encodeURIComponent(dagId)
-        }&execution_date=${encodeURIComponent(executionDate)
-        }&link_name=${encodeURIComponent(link)}`;
+  return useQuery(["extraLinks", dagId, taskId, executionDate], async () => {
+    const data = await Promise.all(
+      extraLinks.map(async (link) => {
+        const url = `${extraLinksUrl}?task_id=${encodeURIComponent(
+          taskId
+        )}&dag_id=${encodeURIComponent(
+          dagId
+        )}&execution_date=${encodeURIComponent(
+          executionDate
+        )}&link_name=${encodeURIComponent(link)}`;
         try {
           const datum = await axios.get<AxiosResponse, LinkData>(url);
           return {
@@ -52,11 +59,11 @@ export default function useExtraLinks({
           console.error(e);
           return {
             name: link,
-            url: '',
+            url: "",
           };
         }
-      }));
-      return data;
-    },
-  );
+      })
+    );
+    return data;
+  });
 }
diff --git a/airflow/www/static/js/api/useGridData.test.ts b/airflow/www/static/js/api/useGridData.test.ts
index d84abf67e6..929303efe1 100644
--- a/airflow/www/static/js/api/useGridData.test.ts
+++ b/airflow/www/static/js/api/useGridData.test.ts
@@ -19,15 +19,15 @@
 
 /* global describe, test, expect */
 
-import type { DagRun } from 'src/types';
-import { areActiveRuns } from './useGridData';
+import type { DagRun } from "src/types";
+import { areActiveRuns } from "./useGridData";
 
 const commonDagRunParams = {
-  runId: 'runId',
-  executionDate: '2022-01-01T10:00+00:00',
-  dataIntervalStart: '2022-01-01T05:00+00:00',
-  dataIntervalEnd: '2022-01-01T10:00+00:00',
-  runType: 'scheduled' as DagRun['runType'],
+  runId: "runId",
+  executionDate: "2022-01-01T10:00+00:00",
+  dataIntervalStart: "2022-01-01T05:00+00:00",
+  dataIntervalEnd: "2022-01-01T10:00+00:00",
+  runType: "scheduled" as DagRun["runType"],
   queuedAt: null,
   startDate: null,
   endDate: null,
@@ -35,29 +35,29 @@ const commonDagRunParams = {
   externalTrigger: false,
   conf: null,
   confIsJson: false,
-  note: '',
+  note: "",
 };
 
-describe('Test areActiveRuns()', () => {
-  test('Correctly detects active runs', () => {
+describe("Test areActiveRuns()", () => {
+  test("Correctly detects active runs", () => {
     const runs: DagRun[] = [
-      { state: 'success', ...commonDagRunParams },
-      { state: 'queued', ...commonDagRunParams },
+      { state: "success", ...commonDagRunParams },
+      { state: "queued", ...commonDagRunParams },
     ];
     expect(areActiveRuns(runs)).toBe(true);
   });
 
-  test('Returns false when all runs are resolved', () => {
+  test("Returns false when all runs are resolved", () => {
     const runs: DagRun[] = [
-      { state: 'success', ...commonDagRunParams },
-      { state: 'failed', ...commonDagRunParams },
-      { state: 'failed', ...commonDagRunParams },
+      { state: "success", ...commonDagRunParams },
+      { state: "failed", ...commonDagRunParams },
+      { state: "failed", ...commonDagRunParams },
     ];
     const result = areActiveRuns(runs);
     expect(result).toBe(false);
   });
 
-  test('Returns false when there are no runs', () => {
+  test("Returns false when there are no runs", () => {
     const result = areActiveRuns();
     expect(result).toBe(false);
   });
diff --git a/airflow/www/static/js/api/useGridData.ts b/airflow/www/static/js/api/useGridData.ts
index 091127fee2..7be2e0d2cf 100644
--- a/airflow/www/static/js/api/useGridData.ts
+++ b/airflow/www/static/js/api/useGridData.ts
@@ -17,24 +17,28 @@
  * under the License.
  */
 
-import { useQuery } from 'react-query';
-import axios, { AxiosResponse } from 'axios';
+import { useQuery } from "react-query";
+import axios, { AxiosResponse } from "axios";
 
-import { getMetaValue } from 'src/utils';
-import { useAutoRefresh } from 'src/context/autorefresh';
-import useErrorToast from 'src/utils/useErrorToast';
+import { getMetaValue } from "src/utils";
+import { useAutoRefresh } from "src/context/autorefresh";
+import useErrorToast from "src/utils/useErrorToast";
 import useFilters, {
-  BASE_DATE_PARAM, NUM_RUNS_PARAM, RUN_STATE_PARAM, RUN_TYPE_PARAM, now,
-} from 'src/dag/useFilters';
-import type { Task, DagRun, RunOrdering } from 'src/types';
-import { camelCase } from 'lodash';
+  BASE_DATE_PARAM,
+  NUM_RUNS_PARAM,
+  RUN_STATE_PARAM,
+  RUN_TYPE_PARAM,
+  now,
+} from "src/dag/useFilters";
+import type { Task, DagRun, RunOrdering } from "src/types";
+import { camelCase } from "lodash";
 
-const DAG_ID_PARAM = 'dag_id';
+const DAG_ID_PARAM = "dag_id";
 
 // dagId comes from dag.html
 const dagId = getMetaValue(DAG_ID_PARAM);
-const gridDataUrl = getMetaValue('grid_data_url');
-const urlRoot = getMetaValue('root');
+const gridDataUrl = getMetaValue("grid_data_url");
+const urlRoot = getMetaValue("root");
 
 export interface GridData {
   dagRuns: DagRun[];
@@ -57,19 +61,18 @@ const formatOrdering = (data: GridData) => ({
   ordering: data.ordering.map((o: string) => camelCase(o)) as RunOrdering,
 });
 
-export const areActiveRuns = (runs: DagRun[] = []) => runs.filter((run) => ['queued', 'running'].includes(run.state)).length > 0;
+export const areActiveRuns = (runs: DagRun[] = []) =>
+  runs.filter((run) => ["queued", "running"].includes(run.state)).length > 0;
 
 const useGridData = () => {
   const { isRefreshOn, stopRefresh } = useAutoRefresh();
   const errorToast = useErrorToast();
   const {
-    filters: {
-      baseDate, numRuns, runType, runState,
-    },
+    filters: { baseDate, numRuns, runType, runState },
   } = useFilters();
 
   const query = useQuery(
-    ['gridData', baseDate, numRuns, runType, runState],
+    ["gridData", baseDate, numRuns, runType, runState],
     async () => {
       const params = {
         root: urlRoot || undefined,
@@ -79,7 +82,9 @@ const useGridData = () => {
         [RUN_TYPE_PARAM]: runType,
         [RUN_STATE_PARAM]: runState,
       };
-      const response = await axios.get<AxiosResponse, GridData>(gridDataUrl, { params });
+      const response = await axios.get<AxiosResponse, GridData>(gridDataUrl, {
+        params,
+      });
       // turn off auto refresh if there are no active runs
       if (!areActiveRuns(response.dagRuns)) stopRefresh();
       return response;
@@ -91,13 +96,13 @@ const useGridData = () => {
       onError: (error: Error) => {
         stopRefresh();
         errorToast({
-          title: 'Auto-refresh Error',
+          title: "Auto-refresh Error",
           error,
         });
-        throw (error);
+        throw error;
       },
       select: formatOrdering,
-    },
+    }
   );
   return {
     ...query,
diff --git a/airflow/www/static/js/api/useMappedInstances.ts b/airflow/www/static/js/api/useMappedInstances.ts
index c14557a03a..7a8c820113 100644
--- a/airflow/www/static/js/api/useMappedInstances.ts
+++ b/airflow/www/static/js/api/useMappedInstances.ts
@@ -17,32 +17,41 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from 'axios';
-import { useQuery } from 'react-query';
+import axios, { AxiosResponse } from "axios";
+import { useQuery } from "react-query";
 
-import { getMetaValue } from 'src/utils';
-import { useAutoRefresh } from 'src/context/autorefresh';
-import type { API } from 'src/types';
+import { getMetaValue } from "src/utils";
+import { useAutoRefresh } from "src/context/autorefresh";
+import type { API } from "src/types";
 
-const mappedInstancesUrl = getMetaValue('mapped_instances_api');
+const mappedInstancesUrl = getMetaValue("mapped_instances_api");
 
 export default function useMappedInstances({
-  dagId, dagRunId, taskId, limit, offset, orderBy,
+  dagId,
+  dagRunId,
+  taskId,
+  limit,
+  offset,
+  orderBy,
 }: API.GetMappedTaskInstancesVariables) {
-  const url = mappedInstancesUrl.replace('_DAG_RUN_ID_', dagRunId).replace('_TASK_ID_', taskId);
-  const orderParam = orderBy && orderBy !== 'map_index' ? { order_by: orderBy } : {};
+  const url = mappedInstancesUrl
+    .replace("_DAG_RUN_ID_", dagRunId)
+    .replace("_TASK_ID_", taskId);
+  const orderParam =
+    orderBy && orderBy !== "map_index" ? { order_by: orderBy } : {};
   const { isRefreshOn } = useAutoRefresh();
   return useQuery(
-    ['mappedInstances', dagId, dagRunId, taskId, offset, orderBy],
-    () => axios.get<AxiosResponse, API.TaskInstanceCollection>(url, {
-      params: { offset, limit, ...orderParam },
-    }),
+    ["mappedInstances", dagId, dagRunId, taskId, offset, orderBy],
+    () =>
+      axios.get<AxiosResponse, API.TaskInstanceCollection>(url, {
+        params: { offset, limit, ...orderParam },
+      }),
     {
       keepPreviousData: true,
       initialData: { taskInstances: [], totalEntries: 0 },
       refetchInterval: isRefreshOn && (autoRefreshInterval || 1) * 1000,
       // staleTime should be similar to the refresh interval
       staleTime: (autoRefreshInterval || 1) * 1000,
-    },
+    }
   );
 }
diff --git a/airflow/www/static/js/api/useMarkFailedRun.ts b/airflow/www/static/js/api/useMarkFailedRun.ts
index e1b7f2a21e..11a94e4264 100644
--- a/airflow/www/static/js/api/useMarkFailedRun.ts
+++ b/airflow/www/static/js/api/useMarkFailedRun.ts
@@ -17,23 +17,23 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from 'axios';
-import { useMutation, useQueryClient } from 'react-query';
+import axios, { AxiosResponse } from "axios";
+import { useMutation, useQueryClient } from "react-query";
 
-import URLSearchParamsWrapper from 'src/utils/URLSearchParamWrapper';
-import { getMetaValue } from 'src/utils';
-import { useAutoRefresh } from 'src/context/autorefresh';
-import useErrorToast from 'src/utils/useErrorToast';
+import URLSearchParamsWrapper from "src/utils/URLSearchParamWrapper";
+import { getMetaValue } from "src/utils";
+import { useAutoRefresh } from "src/context/autorefresh";
+import useErrorToast from "src/utils/useErrorToast";
 
-const csrfToken = getMetaValue('csrf_token');
-const markFailedUrl = getMetaValue('dagrun_failed_url');
+const csrfToken = getMetaValue("csrf_token");
+const markFailedUrl = getMetaValue("dagrun_failed_url");
 
 export default function useMarkFailedRun(dagId: string, runId: string) {
   const queryClient = useQueryClient();
   const errorToast = useErrorToast();
   const { startRefresh } = useAutoRefresh();
   return useMutation(
-    ['dagRunFailed', dagId, runId],
+    ["dagRunFailed", dagId, runId],
     ({ confirmed = false }: { confirmed: boolean }) => {
       const params = new URLSearchParamsWrapper({
         csrf_token: csrfToken,
@@ -44,18 +44,18 @@ export default function useMarkFailedRun(dagId: string, runId: string) {
 
       return axios.post<AxiosResponse, string[]>(markFailedUrl, params, {
         headers: {
-          'Content-Type': 'application/x-www-form-urlencoded',
+          "Content-Type": "application/x-www-form-urlencoded",
         },
       });
     },
     {
       onSuccess: (_, { confirmed }) => {
         if (confirmed) {
-          queryClient.invalidateQueries('gridData');
+          queryClient.invalidateQueries("gridData");
           startRefresh();
         }
       },
       onError: (error: Error) => errorToast({ error }),
-    },
+    }
   );
 }
diff --git a/airflow/www/static/js/api/useMarkFailedTask.ts b/airflow/www/static/js/api/useMarkFailedTask.ts
index caac2fcd9d..23bbda60ab 100644
--- a/airflow/www/static/js/api/useMarkFailedTask.ts
+++ b/airflow/www/static/js/api/useMarkFailedTask.ts
@@ -17,34 +17,42 @@
  * under the License.
  */
 
-import axios from 'axios';
-import { useMutation, useQueryClient } from 'react-query';
-import URLSearchParamsWrapper from 'src/utils/URLSearchParamWrapper';
-import { getMetaValue } from '../utils';
-import { useAutoRefresh } from '../context/autorefresh';
-import useErrorToast from '../utils/useErrorToast';
+import axios from "axios";
+import { useMutation, useQueryClient } from "react-query";
+import URLSearchParamsWrapper from "src/utils/URLSearchParamWrapper";
+import { getMetaValue } from "../utils";
+import { useAutoRefresh } from "../context/autorefresh";
+import useErrorToast from "../utils/useErrorToast";
 
-const failedUrl = getMetaValue('failed_url');
-const csrfToken = getMetaValue('csrf_token');
+const failedUrl = getMetaValue("failed_url");
+const csrfToken = getMetaValue("csrf_token");
 
 export default function useMarkFailedTask({
-  dagId, runId, taskId,
+  dagId,
+  runId,
+  taskId,
 }: {
-  dagId: string, runId: string, taskId: string,
+  dagId: string;
+  runId: string;
+  taskId: string;
 }) {
   const queryClient = useQueryClient();
   const errorToast = useErrorToast();
   const { startRefresh } = useAutoRefresh();
   return useMutation(
-    ['markFailed', dagId, runId, taskId],
+    ["markFailed", dagId, runId, taskId],
     ({
-      past, future, upstream, downstream, mapIndexes = [],
+      past,
+      future,
+      upstream,
+      downstream,
+      mapIndexes = [],
     }: {
-      past: boolean,
-      future: boolean,
-      upstream: boolean,
-      downstream: boolean,
-      mapIndexes: number[]
+      past: boolean;
+      future: boolean;
+      upstream: boolean;
+      downstream: boolean;
+      mapIndexes: number[];
     }) => {
       const params = new URLSearchParamsWrapper({
         csrf_token: csrfToken,
@@ -59,22 +67,27 @@ export default function useMarkFailedTask({
       });
 
       mapIndexes.forEach((mi: number) => {
-        params.append('map_index', mi.toString());
+        params.append("map_index", mi.toString());
       });
 
       return axios.post(failedUrl, params.toString(), {
         headers: {
-          'Content-Type': 'application/x-www-form-urlencoded',
+          "Content-Type": "application/x-www-form-urlencoded",
         },
       });
     },
     {
       onSuccess: () => {
-        queryClient.invalidateQueries('gridData');
-        queryClient.invalidateQueries(['mappedInstances', dagId, runId, taskId]);
+        queryClient.invalidateQueries("gridData");
+        queryClient.invalidateQueries([
+          "mappedInstances",
+          dagId,
+          runId,
+          taskId,
+        ]);
         startRefresh();
       },
       onError: (error: Error) => errorToast({ error }),
-    },
+    }
   );
 }
diff --git a/airflow/www/static/js/api/useMarkSuccessRun.ts b/airflow/www/static/js/api/useMarkSuccessRun.ts
index 545e42e917..119fb29839 100644
--- a/airflow/www/static/js/api/useMarkSuccessRun.ts
+++ b/airflow/www/static/js/api/useMarkSuccessRun.ts
@@ -17,22 +17,22 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from 'axios';
-import { useMutation, useQueryClient } from 'react-query';
-import URLSearchParamsWrapper from 'src/utils/URLSearchParamWrapper';
-import { getMetaValue } from '../utils';
-import { useAutoRefresh } from '../context/autorefresh';
-import useErrorToast from '../utils/useErrorToast';
+import axios, { AxiosResponse } from "axios";
+import { useMutation, useQueryClient } from "react-query";
+import URLSearchParamsWrapper from "src/utils/URLSearchParamWrapper";
+import { getMetaValue } from "../utils";
+import { useAutoRefresh } from "../context/autorefresh";
+import useErrorToast from "../utils/useErrorToast";
 
-const markSuccessUrl = getMetaValue('dagrun_success_url');
-const csrfToken = getMetaValue('csrf_token');
+const markSuccessUrl = getMetaValue("dagrun_success_url");
+const csrfToken = getMetaValue("csrf_token");
 
 export default function useMarkSuccessRun(dagId: string, runId: string) {
   const queryClient = useQueryClient();
   const errorToast = useErrorToast();
   const { startRefresh } = useAutoRefresh();
   return useMutation(
-    ['dagRunSuccess', dagId, runId],
+    ["dagRunSuccess", dagId, runId],
     ({ confirmed = false }: { confirmed: boolean }) => {
       const params = new URLSearchParamsWrapper({
         csrf_token: csrfToken,
@@ -43,18 +43,18 @@ export default function useMarkSuccessRun(dagId: string, runId: string) {
 
       return axios.post<AxiosResponse, string[]>(markSuccessUrl, params, {
         headers: {
-          'Content-Type': 'application/x-www-form-urlencoded',
+          "Content-Type": "application/x-www-form-urlencoded",
         },
       });
     },
     {
       onSuccess: (_, { confirmed }) => {
         if (confirmed) {
-          queryClient.invalidateQueries('gridData');
+          queryClient.invalidateQueries("gridData");
           startRefresh();
         }
       },
       onError: (error: Error) => errorToast({ error }),
-    },
+    }
   );
 }
diff --git a/airflow/www/static/js/api/useMarkSuccessTask.ts b/airflow/www/static/js/api/useMarkSuccessTask.ts
index 8120b58edf..2605a92526 100644
--- a/airflow/www/static/js/api/useMarkSuccessTask.ts
+++ b/airflow/www/static/js/api/useMarkSuccessTask.ts
@@ -17,34 +17,42 @@
  * under the License.
  */
 
-import axios from 'axios';
-import { useMutation, useQueryClient } from 'react-query';
-import URLSearchParamsWrapper from 'src/utils/URLSearchParamWrapper';
-import { getMetaValue } from '../utils';
-import { useAutoRefresh } from '../context/autorefresh';
-import useErrorToast from '../utils/useErrorToast';
+import axios from "axios";
+import { useMutation, useQueryClient } from "react-query";
+import URLSearchParamsWrapper from "src/utils/URLSearchParamWrapper";
+import { getMetaValue } from "../utils";
+import { useAutoRefresh } from "../context/autorefresh";
+import useErrorToast from "../utils/useErrorToast";
 
-const csrfToken = getMetaValue('csrf_token');
-const successUrl = getMetaValue('success_url');
+const csrfToken = getMetaValue("csrf_token");
+const successUrl = getMetaValue("success_url");
 
 export default function useMarkSuccessTask({
-  dagId, runId, taskId,
+  dagId,
+  runId,
+  taskId,
 }: {
-  dagId: string, runId: string, taskId: string,
+  dagId: string;
+  runId: string;
+  taskId: string;
 }) {
   const queryClient = useQueryClient();
   const errorToast = useErrorToast();
   const { startRefresh } = useAutoRefresh();
   return useMutation(
-    ['markSuccess', dagId, runId, taskId],
+    ["markSuccess", dagId, runId, taskId],
     ({
-      past, future, upstream, downstream, mapIndexes = [],
+      past,
+      future,
+      upstream,
+      downstream,
+      mapIndexes = [],
     }: {
-      past: boolean,
-      future: boolean,
-      upstream: boolean,
-      downstream: boolean,
-      mapIndexes: number[]
+      past: boolean;
+      future: boolean;
+      upstream: boolean;
+      downstream: boolean;
+      mapIndexes: number[];
     }) => {
       const params = new URLSearchParamsWrapper({
         csrf_token: csrfToken,
@@ -59,22 +67,27 @@ export default function useMarkSuccessTask({
       });
 
       mapIndexes.forEach((mi: number) => {
-        params.append('map_index', mi.toString());
+        params.append("map_index", mi.toString());
       });
 
       return axios.post(successUrl, params.toString(), {
         headers: {
-          'Content-Type': 'application/x-www-form-urlencoded',
+          "Content-Type": "application/x-www-form-urlencoded",
         },
       });
     },
     {
       onSuccess: () => {
-        queryClient.invalidateQueries('gridData');
-        queryClient.invalidateQueries(['mappedInstances', dagId, runId, taskId]);
+        queryClient.invalidateQueries("gridData");
+        queryClient.invalidateQueries([
+          "mappedInstances",
+          dagId,
+          runId,
+          taskId,
+        ]);
         startRefresh();
       },
       onError: (error: Error) => errorToast({ error }),
-    },
+    }
   );
 }
diff --git a/airflow/www/static/js/api/useQueueRun.ts b/airflow/www/static/js/api/useQueueRun.ts
index 6438d28568..28157a1879 100644
--- a/airflow/www/static/js/api/useQueueRun.ts
+++ b/airflow/www/static/js/api/useQueueRun.ts
@@ -17,22 +17,22 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from 'axios';
-import { useMutation, useQueryClient } from 'react-query';
-import URLSearchParamsWrapper from 'src/utils/URLSearchParamWrapper';
-import { getMetaValue } from '../utils';
-import { useAutoRefresh } from '../context/autorefresh';
-import useErrorToast from '../utils/useErrorToast';
+import axios, { AxiosResponse } from "axios";
+import { useMutation, useQueryClient } from "react-query";
+import URLSearchParamsWrapper from "src/utils/URLSearchParamWrapper";
+import { getMetaValue } from "../utils";
+import { useAutoRefresh } from "../context/autorefresh";
+import useErrorToast from "../utils/useErrorToast";
 
-const csrfToken = getMetaValue('csrf_token');
-const queuedUrl = getMetaValue('dagrun_queued_url');
+const csrfToken = getMetaValue("csrf_token");
+const queuedUrl = getMetaValue("dagrun_queued_url");
 
 export default function useQueueRun(dagId: string, runId: string) {
   const queryClient = useQueryClient();
   const errorToast = useErrorToast();
   const { startRefresh } = useAutoRefresh();
   return useMutation(
-    ['dagRunQueue', dagId, runId],
+    ["dagRunQueue", dagId, runId],
     ({ confirmed = false }: { confirmed: boolean }) => {
       const params = new URLSearchParamsWrapper({
         csrf_token: csrfToken,
@@ -42,18 +42,18 @@ export default function useQueueRun(dagId: string, runId: string) {
       }).toString();
       return axios.post<AxiosResponse, string[]>(queuedUrl, params, {
         headers: {
-          'Content-Type': 'application/x-www-form-urlencoded',
+          "Content-Type": "application/x-www-form-urlencoded",
         },
       });
     },
     {
       onSuccess: (_, { confirmed }) => {
         if (confirmed) {
-          queryClient.invalidateQueries('gridData');
+          queryClient.invalidateQueries("gridData");
           startRefresh();
         }
       },
       onError: (error: Error) => errorToast({ error }),
-    },
+    }
   );
 }
diff --git a/airflow/www/static/js/api/useSetDagRunNote.ts b/airflow/www/static/js/api/useSetDagRunNote.ts
index 927adb0ae2..7f561d47fe 100644
--- a/airflow/www/static/js/api/useSetDagRunNote.ts
+++ b/airflow/www/static/js/api/useSetDagRunNote.ts
@@ -17,51 +17,50 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from 'axios';
-import { useMutation, useQueryClient } from 'react-query';
+import axios, { AxiosResponse } from "axios";
+import { useMutation, useQueryClient } from "react-query";
 
-import { getMetaValue } from 'src/utils';
-import type { API } from 'src/types';
-import useErrorToast from 'src/utils/useErrorToast';
+import { getMetaValue } from "src/utils";
+import type { API } from "src/types";
+import useErrorToast from "src/utils/useErrorToast";
 
-import { emptyGridData } from './useGridData';
-import type { GridData } from './useGridData';
+import { emptyGridData } from "./useGridData";
+import type { GridData } from "./useGridData";
 
-const setDagRunNoteURI = getMetaValue('set_dag_run_note');
+const setDagRunNoteURI = getMetaValue("set_dag_run_note");
 
 interface Props {
   dagId: string;
   runId: string;
 }
 
-export default function useSetDagRunNote({
-  dagId, runId,
-}: Props) {
+export default function useSetDagRunNote({ dagId, runId }: Props) {
   const queryClient = useQueryClient();
   const errorToast = useErrorToast();
-  const setDagRunNote = setDagRunNoteURI.replace('_DAG_RUN_ID_', runId);
+  const setDagRunNote = setDagRunNoteURI.replace("_DAG_RUN_ID_", runId);
 
   return useMutation(
-    ['setDagRunNote', dagId, runId],
-    (note: string | null) => axios.patch<AxiosResponse, API.DAGRun>(setDagRunNote, { note }),
+    ["setDagRunNote", dagId, runId],
+    (note: string | null) =>
+      axios.patch<AxiosResponse, API.DAGRun>(setDagRunNote, { note }),
     {
       onSuccess: async (data) => {
         const note = data.note ?? null;
 
-        const updateGridData = (oldGridData: GridData | undefined) => (
+        const updateGridData = (oldGridData: GridData | undefined) =>
           !oldGridData
             ? emptyGridData
             : {
-              ...oldGridData,
-              dagRuns: oldGridData.dagRuns.map((dr) => (
-                dr.runId === runId ? { ...dr, note } : dr)),
-            }
-        );
+                ...oldGridData,
+                dagRuns: oldGridData.dagRuns.map((dr) =>
+                  dr.runId === runId ? { ...dr, note } : dr
+                ),
+              };
 
-        await queryClient.cancelQueries('gridData');
-        queryClient.setQueriesData('gridData', updateGridData);
+        await queryClient.cancelQueries("gridData");
+        queryClient.setQueriesData("gridData", updateGridData);
       },
       onError: (error: Error) => errorToast({ error }),
-    },
+    }
   );
 }
diff --git a/airflow/www/static/js/api/useSetTaskInstanceNote.ts b/airflow/www/static/js/api/useSetTaskInstanceNote.ts
index d35cedd696..caca6c2806 100644
--- a/airflow/www/static/js/api/useSetTaskInstanceNote.ts
+++ b/airflow/www/static/js/api/useSetTaskInstanceNote.ts
@@ -17,16 +17,18 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from 'axios';
-import { useMutation, useQueryClient } from 'react-query';
+import axios, { AxiosResponse } from "axios";
+import { useMutation, useQueryClient } from "react-query";
 
-import { getMetaValue } from 'src/utils';
-import useErrorToast from 'src/utils/useErrorToast';
+import { getMetaValue } from "src/utils";
+import useErrorToast from "src/utils/useErrorToast";
 
-import type { API } from 'src/types';
+import type { API } from "src/types";
 
-const setTaskInstancesNoteURI = getMetaValue('set_task_instance_note');
-const setMappedTaskInstancesNoteURI = getMetaValue('set_mapped_task_instance_note');
+const setTaskInstancesNoteURI = getMetaValue("set_task_instance_note");
+const setMappedTaskInstancesNoteURI = getMetaValue(
+  "set_mapped_task_instance_note"
+);
 
 interface Props {
   dagId: string;
@@ -36,26 +38,34 @@ interface Props {
 }
 
 export default function useSetTaskInstanceNote({
-  dagId, runId, taskId, mapIndex = -1,
+  dagId,
+  runId,
+  taskId,
+  mapIndex = -1,
 }: Props) {
   const queryClient = useQueryClient();
   const errorToast = useErrorToast();
   // Note: Werkzeug does not like the META URL on dag.html with an integer. It can not put
   // _MAP_INDEX_ there as it interprets that as the integer. Hence, we pass 0 as the integer.
   // To avoid we replace other stuff, we add the surrounding strings to the replacement query.
-  const url = (mapIndex >= 0 ? setMappedTaskInstancesNoteURI : setTaskInstancesNoteURI)
-    .replace('_DAG_RUN_ID_', runId)
-    .replace('_TASK_ID_/0/setNote', `_TASK_ID_/${mapIndex}/setNote`)
-    .replace('_TASK_ID_', taskId);
+  const url = (
+    mapIndex >= 0 ? setMappedTaskInstancesNoteURI : setTaskInstancesNoteURI
+  )
+    .replace("_DAG_RUN_ID_", runId)
+    .replace("_TASK_ID_/0/setNote", `_TASK_ID_/${mapIndex}/setNote`)
+    .replace("_TASK_ID_", taskId);
 
   return useMutation(
-    ['setTaskInstanceNotes', dagId, runId, taskId, mapIndex],
-    (note: string | null) => axios.patch<AxiosResponse, API.TaskInstance>(url, { note }),
+    ["setTaskInstanceNotes", dagId, runId, taskId, mapIndex],
+    (note: string | null) =>
+      axios.patch<AxiosResponse, API.TaskInstance>(url, { note }),
     {
       onSuccess: async (data) => {
         const note = data.note ?? null;
 
-        const updateMappedInstancesResult = (oldMappedInstances?: API.TaskInstanceCollection) => {
+        const updateMappedInstancesResult = (
+          oldMappedInstances?: API.TaskInstanceCollection
+        ) => {
           if (!oldMappedInstances) {
             return {
               taskInstances: [],
@@ -65,23 +75,25 @@ export default function useSetTaskInstanceNote({
           if (mapIndex === undefined || mapIndex < 0) return oldMappedInstances;
           return {
             ...oldMappedInstances,
-            taskInstances: oldMappedInstances.taskInstances?.map((ti) => (
-              ti.dagRunId === runId && ti.taskId === taskId && ti.mapIndex === mapIndex
+            taskInstances: oldMappedInstances.taskInstances?.map((ti) =>
+              ti.dagRunId === runId &&
+              ti.taskId === taskId &&
+              ti.mapIndex === mapIndex
                 ? { ...ti, note }
                 : ti
-            )),
+            ),
           };
         };
 
-        const updateTaskInstanceResult = (oldTaskInstance?: API.TaskInstance) => {
-          if (!oldTaskInstance) throw new Error('Unknown value...');
+        const updateTaskInstanceResult = (
+          oldTaskInstance?: API.TaskInstance
+        ) => {
+          if (!oldTaskInstance) throw new Error("Unknown value...");
           if (
-            oldTaskInstance.dagRunId === runId
-            && oldTaskInstance.taskId === taskId
-            && (
-              (oldTaskInstance.mapIndex == null && mapIndex < 0)
-              || oldTaskInstance.mapIndex === mapIndex
-            )
+            oldTaskInstance.dagRunId === runId &&
+            oldTaskInstance.taskId === taskId &&
+            ((oldTaskInstance.mapIndex == null && mapIndex < 0) ||
+              oldTaskInstance.mapIndex === mapIndex)
           ) {
             return {
               ...oldTaskInstance,
@@ -95,20 +107,23 @@ export default function useSetTaskInstanceNote({
           Mutating the nested object is quite complicated,
           we should simplify the gridData API object first
         */
-        await queryClient.invalidateQueries('gridData');
+        await queryClient.invalidateQueries("gridData");
 
         if (mapIndex >= 0) {
-          await queryClient.cancelQueries('mappedInstances');
-          queryClient.setQueriesData('mappedInstances', updateMappedInstancesResult);
+          await queryClient.cancelQueries("mappedInstances");
+          queryClient.setQueriesData(
+            "mappedInstances",
+            updateMappedInstancesResult
+          );
         }
 
-        await queryClient.cancelQueries('taskInstance');
+        await queryClient.cancelQueries("taskInstance");
         queryClient.setQueriesData(
-          ['taskInstance', dagId, runId, taskId, mapIndex],
-          updateTaskInstanceResult,
+          ["taskInstance", dagId, runId, taskId, mapIndex],
+          updateTaskInstanceResult
         );
       },
       onError: (error: Error) => errorToast({ error }),
-    },
+    }
   );
 }
diff --git a/airflow/www/static/js/api/useTaskInstance.ts b/airflow/www/static/js/api/useTaskInstance.ts
index 3a7f5419e9..2b2818bc21 100644
--- a/airflow/www/static/js/api/useTaskInstance.ts
+++ b/airflow/www/static/js/api/useTaskInstance.ts
@@ -17,35 +17,40 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from 'axios';
-import type { API, TaskInstance } from 'src/types';
-import { useQuery } from 'react-query';
-import { useAutoRefresh } from 'src/context/autorefresh';
+import axios, { AxiosResponse } from "axios";
+import type { API, TaskInstance } from "src/types";
+import { useQuery } from "react-query";
+import { useAutoRefresh } from "src/context/autorefresh";
 
-import { getMetaValue } from 'src/utils';
-import type { SetOptional } from 'type-fest';
+import { getMetaValue } from "src/utils";
+import type { SetOptional } from "type-fest";
 
 /* GridData.TaskInstance and API.TaskInstance are not compatible at the moment.
  * Remove this function when changing the api response for grid_data_url to comply
  * with API.TaskInstance.
  */
-const convertTaskInstance = (
-  ti:
-  API.TaskInstance,
-) => ({ ...ti, runId: ti.dagRunId }) as TaskInstance;
+const convertTaskInstance = (ti: API.TaskInstance) =>
+  ({ ...ti, runId: ti.dagRunId } as TaskInstance);
 
-const taskInstanceApi = getMetaValue('task_instance_api');
+const taskInstanceApi = getMetaValue("task_instance_api");
 
-interface Props extends SetOptional<API.GetMappedTaskInstanceVariables, 'mapIndex'> {
+interface Props
+  extends SetOptional<API.GetMappedTaskInstanceVariables, "mapIndex"> {
   enabled: boolean;
 }
 
 const useTaskInstance = ({
-  dagId, dagRunId, taskId, mapIndex, enabled,
+  dagId,
+  dagRunId,
+  taskId,
+  mapIndex,
+  enabled,
 }: Props) => {
-  let url: string = '';
+  let url: string = "";
   if (taskInstanceApi) {
-    url = taskInstanceApi.replace('_DAG_RUN_ID_', dagRunId).replace('_TASK_ID_', taskId || '');
+    url = taskInstanceApi
+      .replace("_DAG_RUN_ID_", dagRunId)
+      .replace("_TASK_ID_", taskId || "");
   }
 
   if (mapIndex !== undefined && mapIndex >= 0) {
@@ -55,14 +60,17 @@ const useTaskInstance = ({
   const { isRefreshOn } = useAutoRefresh();
 
   return useQuery(
-    ['taskInstance', dagId, dagRunId, taskId, mapIndex],
-    () => axios.get<AxiosResponse, API.TaskInstance>(url, { headers: { Accept: 'text/plain' } }),
+    ["taskInstance", dagId, dagRunId, taskId, mapIndex],
+    () =>
+      axios.get<AxiosResponse, API.TaskInstance>(url, {
+        headers: { Accept: "text/plain" },
+      }),
     {
       placeholderData: {},
       refetchInterval: isRefreshOn && (autoRefreshInterval || 1) * 1000,
       enabled,
       select: convertTaskInstance,
-    },
+    }
   );
 };
 
diff --git a/airflow/www/static/js/api/useTaskLog.ts b/airflow/www/static/js/api/useTaskLog.ts
index 53e0e57ca4..fb8c6ee727 100644
--- a/airflow/www/static/js/api/useTaskLog.ts
+++ b/airflow/www/static/js/api/useTaskLog.ts
@@ -17,59 +17,67 @@
  * under the License.
  */
 
-import { useState } from 'react';
-import axios, { AxiosResponse } from 'axios';
-import { useQuery } from 'react-query';
-import { useAutoRefresh } from 'src/context/autorefresh';
-import type { API, TaskInstance } from 'src/types';
+import { useState } from "react";
+import axios, { AxiosResponse } from "axios";
+import { useQuery } from "react-query";
+import { useAutoRefresh } from "src/context/autorefresh";
+import type { API, TaskInstance } from "src/types";
 
-import { getMetaValue } from 'src/utils';
+import { getMetaValue } from "src/utils";
 
-const taskLogApi = getMetaValue('task_log_api');
+const taskLogApi = getMetaValue("task_log_api");
 
 interface Props extends API.GetLogVariables {
-  state?: TaskInstance['state'];
+  state?: TaskInstance["state"];
 }
 
 const useTaskLog = ({
-  dagId, dagRunId, taskId, taskTryNumber, mapIndex, fullContent = false, state,
+  dagId,
+  dagRunId,
+  taskId,
+  taskTryNumber,
+  mapIndex,
+  fullContent = false,
+  state,
 }: Props) => {
-  let url: string = '';
+  let url: string = "";
   const [isPreviousStatePending, setPrevState] = useState(true);
   if (taskLogApi) {
-    url = taskLogApi.replace('_DAG_RUN_ID_', dagRunId).replace('_TASK_ID_', taskId).replace(/-1$/, taskTryNumber.toString());
+    url = taskLogApi
+      .replace("_DAG_RUN_ID_", dagRunId)
+      .replace("_TASK_ID_", taskId)
+      .replace(/-1$/, taskTryNumber.toString());
   }
 
   const { isRefreshOn } = useAutoRefresh();
 
   // Only refresh is the state is pending
-  const isStatePending = state === 'deferred'
-    || state === 'scheduled'
-    || state === 'running'
-    || state === 'up_for_reschedule'
-    || state === 'up_for_retry'
-    || state === 'queued'
-    || state === 'restarting';
+  const isStatePending =
+    state === "deferred" ||
+    state === "scheduled" ||
+    state === "running" ||
+    state === "up_for_reschedule" ||
+    state === "up_for_retry" ||
+    state === "queued" ||
+    state === "restarting";
 
   // We also want to get the last log when the task was finished
   const expectingLogs = isStatePending || isPreviousStatePending;
 
   return useQuery(
-    ['taskLogs', dagId, dagRunId, taskId, mapIndex, taskTryNumber, fullContent],
+    ["taskLogs", dagId, dagRunId, taskId, mapIndex, taskTryNumber, fullContent],
     () => {
       setPrevState(isStatePending);
-      return axios.get<AxiosResponse, string>(
-        url,
-        {
-          headers: { Accept: 'text/plain' },
-          params: { map_index: mapIndex, full_content: fullContent },
-        },
-      );
+      return axios.get<AxiosResponse, string>(url, {
+        headers: { Accept: "text/plain" },
+        params: { map_index: mapIndex, full_content: fullContent },
+      });
     },
     {
-      placeholderData: '',
-      refetchInterval: expectingLogs && isRefreshOn && (autoRefreshInterval || 1) * 1000,
-    },
+      placeholderData: "",
+      refetchInterval:
+        expectingLogs && isRefreshOn && (autoRefreshInterval || 1) * 1000,
+    }
   );
 };
 
diff --git a/airflow/www/static/js/api/useUpstreamDatasetEvents.ts b/airflow/www/static/js/api/useUpstreamDatasetEvents.ts
index 4fa1b76517..995941613c 100644
--- a/airflow/www/static/js/api/useUpstreamDatasetEvents.ts
+++ b/airflow/www/static/js/api/useUpstreamDatasetEvents.ts
@@ -17,28 +17,27 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from 'axios';
-import { useQuery } from 'react-query';
+import axios, { AxiosResponse } from "axios";
+import { useQuery } from "react-query";
 
-import { getMetaValue } from 'src/utils';
-import type { API } from 'src/types';
+import { getMetaValue } from "src/utils";
+import type { API } from "src/types";
 
 interface Props {
   runId: string;
 }
 
 export default function useUpstreamDatasetEvents({ runId }: Props) {
-  const query = useQuery(
-    ['upstreamDatasetEvents', runId],
-    () => {
-      const dagId = getMetaValue('dag_id');
-      const upstreamEventsUrl = (
-        getMetaValue('upstream_dataset_events_api')
-          || `api/v1/dags/${dagId}/dagRuns/_DAG_RUN_ID_/upstreamDatasetEvents`
-      ).replace('_DAG_RUN_ID_', runId);
-      return axios.get<AxiosResponse, API.DatasetEventCollection>(upstreamEventsUrl);
-    },
-  );
+  const query = useQuery(["upstreamDatasetEvents", runId], () => {
+    const dagId = getMetaValue("dag_id");
+    const upstreamEventsUrl = (
+      getMetaValue("upstream_dataset_events_api") ||
+      `api/v1/dags/${dagId}/dagRuns/_DAG_RUN_ID_/upstreamDatasetEvents`
+    ).replace("_DAG_RUN_ID_", runId);
+    return axios.get<AxiosResponse, API.DatasetEventCollection>(
+      upstreamEventsUrl
+    );
+  });
   return {
     ...query,
     data: query.data || { datasetEvents: [], totalEntries: 0 },
diff --git a/airflow/www/static/js/calendar.js b/airflow/www/static/js/calendar.js
index bb4b6e73cf..330bd47344 100644
--- a/airflow/www/static/js/calendar.js
+++ b/airflow/www/static/js/calendar.js
@@ -18,9 +18,9 @@
  */
 
 /* global calendarData, statesColors, document, window, $, d3, moment */
-import { getMetaValue } from './utils';
+import { getMetaValue } from "./utils";
 
-const gridUrl = getMetaValue('grid_url');
+const gridUrl = getMetaValue("grid_url");
 
 function getGridViewURL(d) {
   return `${gridUrl}?base_date=${encodeURIComponent(d.toISOString())}`;
@@ -28,7 +28,7 @@ function getGridViewURL(d) {
 
 // date helpers
 function formatDay(d) {
-  return ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][d];
+  return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][d];
 }
 
 function toMoment(y, m, d) {
@@ -48,7 +48,7 @@ function weekOfYear(y, m) {
 }
 
 function daysInMonth(y, m) {
-  const lastDay = toMoment(y, m, 1).add(1, 'month').subtract(1, 'day');
+  const lastDay = toMoment(y, m, 1).add(1, "month").subtract(1, "day");
   return lastDay.date();
 }
 
@@ -58,16 +58,17 @@ function weeksInMonth(y, m) {
   return Math.floor((daysInMonth(y, m) + monthOffset) / 7) + 1;
 }
 
-const dateFormat = 'YYYY-MM-DD';
+const dateFormat = "YYYY-MM-DD";
 
-document.addEventListener('DOMContentLoaded', () => {
-  $('span.status_square').tooltip({ html: true });
+document.addEventListener("DOMContentLoaded", () => {
+  $("span.status_square").tooltip({ html: true });
 
   // JSON.parse is faster for large payloads than an object literal
   const rootData = JSON.parse(calendarData);
 
-  const dayTip = d3.tip()
-    .attr('class', 'tooltip d3-tip')
+  const dayTip = d3
+    .tip()
+    .attr("class", "tooltip d3-tip")
     .html((toolTipHtml) => toolTipHtml);
 
   // draw the calendar
@@ -112,140 +113,165 @@ document.addEventListener('DOMContentLoaded', () => {
       .sort((data) => data.year);
 
     // root SVG element
-    const fullWidth = (
-      leftRightMargin * 2 + yearLabelWidth + dayLabelWidth
-      + maxWeeksInYear * cellSize
-    );
-    const yearsHeight = (yearHeight + yearPadding) * dagStates.length + yearPadding;
+    const fullWidth =
+      leftRightMargin * 2 +
+      yearLabelWidth +
+      dayLabelWidth +
+      maxWeeksInYear * cellSize;
+    const yearsHeight =
+      (yearHeight + yearPadding) * dagStates.length + yearPadding;
     const fullHeight = titleHeight + legendHeight + yearsHeight;
 
     const svg = d3
-      .select('#calendar-svg')
-      .attr('width', fullWidth)
-      .attr('height', fullHeight)
+      .select("#calendar-svg")
+      .attr("width", fullWidth)
+      .attr("height", fullHeight)
       .call(dayTip);
 
     // Add the legend
     const legend = svg
-      .append('g')
-      .attr('transform', `translate(0, ${titleHeight + legendHeight / 2})`);
+      .append("g")
+      .attr("transform", `translate(0, ${titleHeight + legendHeight / 2})`);
 
     let legendXOffset = fullWidth - leftRightMargin;
 
-    function drawLegend(rightState, leftState, numSwatches = 1, swatchesWidth = cellSize) {
+    function drawLegend(
+      rightState,
+      leftState,
+      numSwatches = 1,
+      swatchesWidth = cellSize
+    ) {
       const startColor = statesColors[leftState || rightState];
       const endColor = statesColors[rightState];
 
       legendXOffset -= legendSwtchesTextWidth;
       legend
-        .append('text')
-        .attr('x', legendXOffset)
-        .attr('y', cellSize / 2)
-        .attr('text-anchor', 'start')
-        .attr('class', 'status-label')
-        .attr('alignment-baseline', 'middle')
+        .append("text")
+        .attr("x", legendXOffset)
+        .attr("y", cellSize / 2)
+        .attr("text-anchor", "start")
+        .attr("class", "status-label")
+        .attr("alignment-baseline", "middle")
         .text(rightState);
       legendXOffset -= legendSwatchesPadding;
 
       legendXOffset -= swatchesWidth;
       legend
-        .append('g')
-        .attr('transform', `translate(${legendXOffset}, 0)`)
-        .selectAll('g')
+        .append("g")
+        .attr("transform", `translate(${legendXOffset}, 0)`)
+        .selectAll("g")
         .data(d3.range(numSwatches))
         .enter()
-        .append('rect')
-        .attr('x', (v) => v * (swatchesWidth / numSwatches))
-        .attr('width', swatchesWidth / numSwatches)
-        .attr('height', cellSize)
-        .attr('class', 'day')
-        .attr('fill', (v) => (startColor.startsWith('url') ? startColor : d3.interpolateHsl(startColor, endColor)(v / numSwatches)));
+        .append("rect")
+        .attr("x", (v) => v * (swatchesWidth / numSwatches))
+        .attr("width", swatchesWidth / numSwatches)
+        .attr("height", cellSize)
+        .attr("class", "day")
+        .attr("fill", (v) =>
+          startColor.startsWith("url")
+            ? startColor
+            : d3.interpolateHsl(startColor, endColor)(v / numSwatches)
+        );
       legendXOffset -= legendSwatchesPadding;
 
       if (leftState !== undefined) {
         legend
-          .append('text')
-          .attr('x', legendXOffset)
-          .attr('y', cellSize / 2)
-          .attr('text-anchor', 'end')
-          .attr('class', 'status-label')
-          .attr('alignment-baseline', 'middle')
+          .append("text")
+          .attr("x", legendXOffset)
+          .attr("y", cellSize / 2)
+          .attr("text-anchor", "end")
+          .attr("class", "status-label")
+          .attr("alignment-baseline", "middle")
           .text(leftState);
         legendXOffset -= legendSwtchesTextWidth;
       }
     }
 
-    drawLegend('no_status');
-    drawLegend('planned');
-    drawLegend('running');
-    drawLegend('failed', 'success', 10, 100);
+    drawLegend("no_status");
+    drawLegend("planned");
+    drawLegend("running");
+    drawLegend("failed", "success", 10, 100);
 
     // Add the years groups, each holding one year of data.
     const years = svg
-      .append('g')
-      .attr('transform', `translate(${leftRightMargin}, ${titleHeight + legendHeight})`);
+      .append("g")
+      .attr(
+        "transform",
+        `translate(${leftRightMargin}, ${titleHeight + legendHeight})`
+      );
 
     const year = years
-      .selectAll('g')
+      .selectAll("g")
       .data(dagStates)
       .enter()
-      .append('g')
-      .attr('transform', (d, i) => `translate(0, ${yearPadding + (yearHeight + yearPadding) * i})`);
+      .append("g")
+      .attr(
+        "transform",
+        (d, i) =>
+          `translate(0, ${yearPadding + (yearHeight + yearPadding) * i})`
+      );
 
     year
-      .append('text')
-      .attr('x', -yearHeight * 0.5)
-      .attr('transform', 'rotate(270)')
-      .attr('text-anchor', 'middle')
-      .attr('class', 'year-label')
+      .append("text")
+      .attr("x", -yearHeight * 0.5)
+      .attr("transform", "rotate(270)")
+      .attr("text-anchor", "middle")
+      .attr("class", "year-label")
       .text((d) => d.year);
 
     // write day names
     year
-      .append('g')
-      .attr('transform', `translate(${yearLabelWidth}, ${dayLabelPadding})`)
-      .attr('text-anchor', 'end')
-      .selectAll('g')
+      .append("g")
+      .attr("transform", `translate(${yearLabelWidth}, ${dayLabelPadding})`)
+      .attr("text-anchor", "end")
+      .selectAll("g")
       .data(d3.range(7))
       .enter()
-      .append('text')
-      .attr('y', (i) => (i + 0.5) * cellSize)
-      .attr('class', 'day-label')
+      .append("text")
+      .attr("y", (i) => (i + 0.5) * cellSize)
+      .attr("class", "day-label")
       .text(formatDay);
 
     // create months groups to old the individual day cells & month outline for each month.
     const months = year
-      .append('g')
-      .attr('transform', `translate(${yearLabelWidth + dayLabelWidth}, 0)`);
+      .append("g")
+      .attr("transform", `translate(${yearLabelWidth + dayLabelWidth}, 0)`);
 
     const month = months
-      .append('g')
-      .selectAll('g')
-      .data((data) => d3
-        .range(12)
-        .map((i) => ({
+      .append("g")
+      .selectAll("g")
+      .data((data) =>
+        d3.range(12).map((i) => ({
           year: data.year,
           month: i,
           dagStates: data.dagStates[i] || {},
-        })))
+        }))
+      )
       .enter()
-      .append('g')
-      .attr('transform', (data) => `translate(${weekOfYear(data.year, data.month) * cellSize}, 0)`);
+      .append("g")
+      .attr(
+        "transform",
+        (data) =>
+          `translate(${weekOfYear(data.year, data.month) * cellSize}, 0)`
+      );
 
     const tipHtml = (data) => {
-      const stateCounts = d3.entries(data.dagStates).map((kv) => `${kv.value[0].count} ${kv.key}`);
+      const stateCounts = d3
+        .entries(data.dagStates)
+        .map((kv) => `${kv.value[0].count} ${kv.key}`);
       const date = toMoment(data.year, data.month, data.day);
       const daySr = formatDay(date.day());
       const dateStr = date.format(dateFormat);
-      return `<strong>${daySr} ${dateStr}</strong><br>${stateCounts.join('<br>')}`;
+      return `<strong>${daySr} ${dateStr}</strong><br>${stateCounts.join(
+        "<br>"
+      )}`;
     };
 
     // Create the day cells
     month
-      .selectAll('g')
-      .data((data) => d3
-        .range(daysInMonth(data.year, data.month))
-        .map((i) => {
+      .selectAll("g")
+      .data((data) =>
+        d3.range(daysInMonth(data.year, data.month)).map((i) => {
           const day = i + 1;
           const dagRunsByState = data.dagStates[day] || {};
           return {
@@ -254,23 +280,31 @@ document.addEventListener('DOMContentLoaded', () => {
             day,
             dagStates: dagRunsByState,
           };
-        }))
+        })
+      )
       .enter()
-      .append('rect')
-      .attr('x', (data) => weekOfMonth(data.year, data.month, data.day) * cellSize)
-      .attr('y', (data) => toMoment(data.year, data.month, data.day).day() * cellSize)
-      .attr('width', cellSize)
-      .attr('height', cellSize)
-      .attr('class', 'day')
-      .attr('fill', (data) => {
-        const getCount = (state) => (data.dagStates[state] || [{ count: 0 }])[0].count;
-        const runningCount = getCount('running');
+      .append("rect")
+      .attr(
+        "x",
+        (data) => weekOfMonth(data.year, data.month, data.day) * cellSize
+      )
+      .attr(
+        "y",
+        (data) => toMoment(data.year, data.month, data.day).day() * cellSize
+      )
+      .attr("width", cellSize)
+      .attr("height", cellSize)
+      .attr("class", "day")
+      .attr("fill", (data) => {
+        const getCount = (state) =>
+          (data.dagStates[state] || [{ count: 0 }])[0].count;
+        const runningCount = getCount("running");
         if (runningCount > 0) return statesColors.running;
 
-        const successCount = getCount('success');
-        const failedCount = getCount('failed');
+        const successCount = getCount("success");
+        const failedCount = getCount("failed");
         if (successCount + failedCount === 0) {
-          const plannedCount = getCount('planned');
+          const plannedCount = getCount("planned");
           if (plannedCount > 0) return statesColors.planned;
           return statesColors.no_status;
         }
@@ -281,36 +315,44 @@ document.addEventListener('DOMContentLoaded', () => {
           // We use a minimum color interpolation floor, so that days with low failures ratios
           // don't appear almost as green as days with not failure at all.
           const floor = 0.5;
-          ratioFailures = floor + (failedCount / (failedCount + successCount)) * (1 - floor);
+          ratioFailures =
+            floor + (failedCount / (failedCount + successCount)) * (1 - floor);
         }
-        return d3.interpolateHsl(statesColors.success, statesColors.failed)(ratioFailures);
+        return d3.interpolateHsl(
+          statesColors.success,
+          statesColors.failed
+        )(ratioFailures);
       })
-      .on('click', (data) => {
+      .on("click", (data) => {
         window.location.href = getGridViewURL(
           // add 1 day and subtract 1 ms to not show any run from the next day.
-          toMoment(data.year, data.month, data.day).add(1, 'day').subtract(1, 'ms'),
+          toMoment(data.year, data.month, data.day)
+            .add(1, "day")
+            .subtract(1, "ms")
         );
       })
-      .on('mouseover', function showTip(data) {
+      .on("mouseover", function showTip(data) {
         const tt = tipHtml(data);
-        dayTip.direction('n');
+        dayTip.direction("n");
         dayTip.show(tt, this);
       })
-      .on('mouseout', function hideTip(data) {
+      .on("mouseout", function hideTip(data) {
         dayTip.hide(data, this);
       });
 
     // add outline (path) around month
     month
-      .selectAll('g')
+      .selectAll("g")
       .data((data) => [data])
       .enter()
-      .append('path')
-      .attr('class', 'month')
-      .style('fill', 'none')
-      .attr('d', (data) => {
+      .append("path")
+      .attr("class", "month")
+      .style("fill", "none")
+      .attr("d", (data) => {
         const firstDayOffset = toMoment(data.year, data.month, 1).day();
-        const lastDayOffset = toMoment(data.year, data.month, 1).add(1, 'month').day();
+        const lastDayOffset = toMoment(data.year, data.month, 1)
+          .add(1, "month")
+          .day();
         const weeks = weeksInMonth(data.year, data.month);
         return d3.svg.line()([
           [0, firstDayOffset * cellSize],
@@ -327,7 +369,7 @@ document.addEventListener('DOMContentLoaded', () => {
   }
 
   function update() {
-    $('#loading').remove();
+    $("#loading").remove();
     draw();
   }
 
diff --git a/airflow/www/static/js/callModal.js b/airflow/www/static/js/callModal.js
index d537c6256b..a2cac9f26b 100644
--- a/airflow/www/static/js/callModal.js
+++ b/airflow/www/static/js/callModal.js
@@ -19,12 +19,12 @@
 
 /* global document, window, $ */
 
-import { getMetaValue } from './utils';
-import { formatDateTime } from './datetime_utils';
+import { getMetaValue } from "./utils";
+import { formatDateTime } from "./datetime_utils";
 
 function updateQueryStringParameter(uri, key, value) {
-  const re = new RegExp(`([?&])${key}=.*?(&|$)`, 'i');
-  const separator = uri.indexOf('?') !== -1 ? '&' : '?';
+  const re = new RegExp(`([?&])${key}=.*?(&|$)`, "i");
+  const separator = uri.indexOf("?") !== -1 ? "&" : "?";
   if (uri.match(re)) {
     return uri.replace(re, `$1${key}=${value}$2`);
   }
@@ -33,19 +33,30 @@ function updateQueryStringParameter(uri, key, value) {
 }
 
 function updateUriToFilterTasks(uri, taskId, filterUpstream, filterDownstream) {
-  const uriWithRoot = updateQueryStringParameter(uri, 'root', taskId);
-  const uriWithFilterUpstreamQuery = updateQueryStringParameter(uriWithRoot, 'filter_upstream', filterUpstream);
-  return updateQueryStringParameter(uriWithFilterUpstreamQuery, 'filter_downstream', filterDownstream);
+  const uriWithRoot = updateQueryStringParameter(uri, "root", taskId);
+  const uriWithFilterUpstreamQuery = updateQueryStringParameter(
+    uriWithRoot,
+    "filter_upstream",
+    filterUpstream
+  );
+  return updateQueryStringParameter(
+    uriWithFilterUpstreamQuery,
+    "filter_downstream",
+    filterDownstream
+  );
 }
 
-const dagId = getMetaValue('dag_id');
-const logsWithMetadataUrl = getMetaValue('logs_with_metadata_url');
-const externalLogUrl = getMetaValue('external_log_url');
-const extraLinksUrl = getMetaValue('extra_links_url');
-const showExternalLogRedirect = getMetaValue('show_external_log_redirect') === 'True';
-
-const buttons = Array.from(document.querySelectorAll('a[id^="btn_"][data-base-url]')).reduce((obj, elm) => {
-  obj[elm.id.replace('btn_', '')] = elm;
+const dagId = getMetaValue("dag_id");
+const logsWithMetadataUrl = getMetaValue("logs_with_metadata_url");
+const externalLogUrl = getMetaValue("external_log_url");
+const extraLinksUrl = getMetaValue("extra_links_url");
+const showExternalLogRedirect =
+  getMetaValue("show_external_log_redirect") === "True";
+
+const buttons = Array.from(
+  document.querySelectorAll('a[id^="btn_"][data-base-url]')
+).reduce((obj, elm) => {
+  obj[elm.id.replace("btn_", "")] = elm;
   return obj;
 }, {});
 
@@ -55,10 +66,13 @@ function updateButtonUrl(elm, params) {
     url = url.replace(dagId, params.dag_id);
     delete params.dag_id;
   }
-  if (Object.prototype.hasOwnProperty.call(params, 'map_index') && params.map_index === undefined) {
+  if (
+    Object.prototype.hasOwnProperty.call(params, "map_index") &&
+    params.map_index === undefined
+  ) {
     delete params.map_index;
   }
-  elm.setAttribute('href', `${url}?${$.param(params)}`);
+  elm.setAttribute("href", `${url}?${$.param(params)}`);
 }
 
 function updateModalUrls({
@@ -91,7 +105,7 @@ function updateModalUrls({
     _flt_3_dag_id: dagId,
     _flt_3_task_id: taskId,
     _flt_3_run_id: dagRunId,
-    _oc_TaskInstanceModelView: 'map_index',
+    _oc_TaskInstanceModelView: "map_index",
   });
 
   if (buttons.rendered_k8s) {
@@ -106,7 +120,7 @@ function updateModalUrls({
   const tiButtonParams = {
     _flt_3_dag_id: dagId,
     _flt_3_task_id: taskId,
-    _oc_TaskInstanceModelView: 'dag_run.execution_date',
+    _oc_TaskInstanceModelView: "dag_run.execution_date",
   };
   // eslint-disable-next-line no-underscore-dangle
   if (mapIndex >= 0) tiButtonParams._flt_0_map_index = mapIndex;
@@ -139,92 +153,95 @@ function callModal({
   mappedStates = [],
 }) {
   // Turn off previous event listeners
-  $('.map_index_item').off('click');
-  $('form[data-action]').off('submit');
+  $(".map_index_item").off("click");
+  $("form[data-action]").off("submit");
 
   const location = String(window.location);
-  $('#btn_filter_upstream').on('click', () => {
-    window.location = updateUriToFilterTasks(location, taskId, 'true', 'false');
+  $("#btn_filter_upstream").on("click", () => {
+    window.location = updateUriToFilterTasks(location, taskId, "true", "false");
   });
-  $('#btn_filter_downstream').on('click', () => {
-    window.location = updateUriToFilterTasks(location, taskId, 'false', 'true');
+  $("#btn_filter_downstream").on("click", () => {
+    window.location = updateUriToFilterTasks(location, taskId, "false", "true");
   });
-  $('#btn_filter_upstream_downstream').on('click', () => {
-    window.location = updateUriToFilterTasks(location, taskId, 'true', 'true');
+  $("#btn_filter_upstream_downstream").on("click", () => {
+    window.location = updateUriToFilterTasks(location, taskId, "true", "true");
   });
-  $('#dag_run_id').text(dagRunId);
-  $('#task_id').text(taskId);
-  $('#execution_date').text(formatDateTime(executionDate));
-  $('#taskInstanceModal').modal({});
-  $('#taskInstanceModal').css('margin-top', '0');
-  $('#extra_links').prev('hr').hide();
-  $('#extra_links').empty().hide();
+  $("#dag_run_id").text(dagRunId);
+  $("#task_id").text(taskId);
+  $("#execution_date").text(formatDateTime(executionDate));
+  $("#taskInstanceModal").modal({});
+  $("#taskInstanceModal").css("margin-top", "0");
+  $("#extra_links").prev("hr").hide();
+  $("#extra_links").empty().hide();
   if (mapIndex >= 0) {
-    $('#modal_map_index').show();
-    $('#modal_map_index .value').text(mapIndex);
+    $("#modal_map_index").show();
+    $("#modal_map_index .value").text(mapIndex);
   } else {
-    $('#modal_map_index').hide();
-    $('#modal_map_index .value').text('');
+    $("#modal_map_index").hide();
+    $("#modal_map_index .value").text("");
   }
 
   let subDagId;
   if (isSubDag) {
-    $('#div_btn_subdag').show();
+    $("#div_btn_subdag").show();
     subDagId = `${dagId}.${taskId}`;
   } else {
-    $('#div_btn_subdag').hide();
+    $("#div_btn_subdag").hide();
   }
 
   // Show a span or dropdown for mapIndex
   if (mapIndex >= 0 && !mappedStates.length) {
-    $('#modal_map_index').show();
-    $('#modal_map_index .value').text(mapIndex);
-    $('#mapped_dropdown').hide();
+    $("#modal_map_index").show();
+    $("#modal_map_index .value").text(mapIndex);
+    $("#mapped_dropdown").hide();
   } else if (mapIndex >= 0 || isMapped) {
-    $('#modal_map_index').show();
-    $('#modal_map_index .value').text('');
-    $('#mapped_dropdown').show();
-
-    const dropdownText = mapIndex > -1
-      ? mapIndex
-      : `All  ${mappedStates.length} Mapped Instances`;
-    $('#mapped_dropdown #dropdown-label').text(dropdownText);
-    $('#mapped_dropdown .dropdown-menu').empty();
-    $('#mapped_dropdown .dropdown-menu')
-      .append(`<li><a href="#" class="map_index_item" data-mapIndex="all">All ${mappedStates.length} Mapped Instances</a></li>`);
+    $("#modal_map_index").show();
+    $("#modal_map_index .value").text("");
+    $("#mapped_dropdown").show();
+
+    const dropdownText =
+      mapIndex > -1 ? mapIndex : `All  ${mappedStates.length} Mapped Instances`;
+    $("#mapped_dropdown #dropdown-label").text(dropdownText);
+    $("#mapped_dropdown .dropdown-menu").empty();
+    $("#mapped_dropdown .dropdown-menu").append(
+      `<li><a href="#" class="map_index_item" data-mapIndex="all">All ${mappedStates.length} Mapped Instances</a></li>`
+    );
     mappedStates.forEach((state, i) => {
-      $('#mapped_dropdown .dropdown-menu')
-        .append(`<li><a href="#" class="map_index_item" data-mapIndex="${i}">${i} - ${state}</a></li>`);
+      $("#mapped_dropdown .dropdown-menu").append(
+        `<li><a href="#" class="map_index_item" data-mapIndex="${i}">${i} - ${state}</a></li>`
+      );
     });
   } else {
-    $('#modal_map_index').hide();
-    $('#modal_map_index .value').text('');
-    $('#mapped_dropdown').hide();
+    $("#modal_map_index").hide();
+    $("#modal_map_index .value").text("");
+    $("#mapped_dropdown").hide();
   }
 
   if (isMapped) {
-    $('#task_actions').text(`Task Actions for all ${mappedStates.length} instances`);
-    $('#btn_mapped').show();
-    $('#mapped_dropdown').css('display', 'inline-block');
-    $('#btn_rendered').hide();
-    $('#btn_xcom').hide();
-    $('#btn_log').hide();
-    $('#btn_task').hide();
+    $("#task_actions").text(
+      `Task Actions for all ${mappedStates.length} instances`
+    );
+    $("#btn_mapped").show();
+    $("#mapped_dropdown").css("display", "inline-block");
+    $("#btn_rendered").hide();
+    $("#btn_xcom").hide();
+    $("#btn_log").hide();
+    $("#btn_task").hide();
   } else {
-    $('#task_actions').text('Task Actions');
-    $('#btn_rendered').show();
-    $('#btn_xcom').show();
-    $('#btn_log').show();
-    $('#btn_mapped').hide();
-    $('#btn_task').show();
+    $("#task_actions").text("Task Actions");
+    $("#btn_rendered").show();
+    $("#btn_xcom").show();
+    $("#btn_log").show();
+    $("#btn_mapped").hide();
+    $("#btn_task").show();
   }
 
-  $('#dag_dl_logs').hide();
-  $('#dag_redir_logs').hide();
+  $("#dag_dl_logs").hide();
+  $("#dag_redir_logs").hide();
   if (tryNumber > 0 && !isMapped) {
-    $('#dag_dl_logs').show();
+    $("#dag_dl_logs").show();
     if (showExternalLogRedirect) {
-      $('#dag_redir_logs').show();
+      $("#dag_redir_logs").show();
     }
   }
 
@@ -236,83 +253,89 @@ function callModal({
     dagRunId,
   });
 
-  $('#try_index > li').remove();
-  $('#redir_log_try_index > li').remove();
-  const startIndex = (tryNumber > 2 ? 0 : 1);
+  $("#try_index > li").remove();
+  $("#redir_log_try_index > li").remove();
+  const startIndex = tryNumber > 2 ? 0 : 1;
 
   const query = new URLSearchParams({
     dag_id: dagId,
     task_id: taskId,
     execution_date: executionDate,
-    metadata: 'null',
+    metadata: "null",
   });
   if (mapIndex !== undefined) {
-    query.set('map_index', mapIndex);
+    query.set("map_index", mapIndex);
   }
   for (let index = startIndex; index < tryNumber; index += 1) {
     let showLabel = index;
     if (index !== 0) {
-      query.set('try_number', index);
+      query.set("try_number", index);
     } else {
-      showLabel = 'All';
+      showLabel = "All";
     }
 
-    $('#try_index').append(`<li role="presentation" style="display:inline">
+    $("#try_index").append(`<li role="presentation" style="display:inline">
       <a href="${logsWithMetadataUrl}?${query}&format=file"> ${showLabel} </a>
       </li>`);
 
     if (index !== 0 || showExternalLogRedirect) {
-      $('#redir_log_try_index').append(`<li role="presentation" style="display:inline">
+      $("#redir_log_try_index")
+        .append(`<li role="presentation" style="display:inline">
       <a href="${externalLogUrl}?${query}"> ${showLabel} </a>
       </li>`);
     }
   }
-  query.delete('try_number');
+  query.delete("try_number");
 
   if (!isMapped && extraLinks && extraLinks.length > 0) {
     const markupArr = [];
     extraLinks.sort();
     $.each(extraLinks, (i, link) => {
-      query.set('link_name', link);
-      const externalLink = $('<a href="#" class="btn btn-primary disabled"></a>');
-      const linkTooltip = $('<span class="tool-tip" data-toggle="tooltip" style="padding-right: 2px; padding-left: 3px" data-placement="top" '
-        + 'title="link not yet available"></span>');
+      query.set("link_name", link);
+      const externalLink = $(
+        '<a href="#" class="btn btn-primary disabled"></a>'
+      );
+      const linkTooltip = $(
+        '<span class="tool-tip" data-toggle="tooltip" style="padding-right: 2px; padding-left: 3px" data-placement="top" ' +
+          'title="link not yet available"></span>'
+      );
       linkTooltip.append(externalLink);
       externalLink.text(link);
 
-      $.ajax(
-        {
-          url: `${extraLinksUrl}?${query}`,
-          cache: false,
-          success(data) {
-            externalLink.attr('href', data.url);
-            // open absolute (external) links in a new tab/window and relative (local) links
-            // directly
-            if (/^(?:[a-z]+:)?\/\//.test(data.url)) {
-              externalLink.attr('target', '_blank');
-            }
-            externalLink.removeClass('disabled');
-            linkTooltip.tooltip('disable');
-          },
-          error(data) {
-            linkTooltip.tooltip('hide').attr('title', data.responseJSON.error).tooltip('fixTitle');
-          },
+      $.ajax({
+        url: `${extraLinksUrl}?${query}`,
+        cache: false,
+        success(data) {
+          externalLink.attr("href", data.url);
+          // open absolute (external) links in a new tab/window and relative (local) links
+          // directly
+          if (/^(?:[a-z]+:)?\/\//.test(data.url)) {
+            externalLink.attr("target", "_blank");
+          }
+          externalLink.removeClass("disabled");
+          linkTooltip.tooltip("disable");
         },
-      );
+        error(data) {
+          linkTooltip
+            .tooltip("hide")
+            .attr("title", data.responseJSON.error)
+            .tooltip("fixTitle");
+        },
+      });
 
       markupArr.push(linkTooltip);
     });
 
-    const extraLinksSpan = $('#extra_links');
-    extraLinksSpan.prev('hr').show();
+    const extraLinksSpan = $("#extra_links");
+    extraLinksSpan.prev("hr").show();
     extraLinksSpan.append(markupArr).show();
     extraLinksSpan.find('[data-toggle="tooltip"]').tooltip();
   }
 
   // Switch the modal from a mapped task summary to a specific mapped task instance
   function switchMapItem() {
-    const mi = $(this).attr('data-mapIndex');
-    if (mi === 'all') {
+    const mi = $(this).attr("data-mapIndex");
+    if (mi === "all") {
       callModal({
         taskId,
         executionDate,
@@ -353,13 +376,13 @@ function callModal({
       } else if (form.map_index) {
         form.map_index.remove();
       }
-      form.action = $(this).data('action');
+      form.action = $(this).data("action");
       form.submit();
     }
   }
 
-  $('form[data-action]').on('submit', submit);
-  $('.map_index_item').on('click', switchMapItem);
+  $("form[data-action]").on("submit", submit);
+  $(".map_index_item").on("click", switchMapItem);
 }
 
 export default callModal;
diff --git a/airflow/www/static/js/components/AutoRefresh.tsx b/airflow/www/static/js/components/AutoRefresh.tsx
index 7eb59bca04..9e1e9644c6 100644
--- a/airflow/www/static/js/components/AutoRefresh.tsx
+++ b/airflow/www/static/js/components/AutoRefresh.tsx
@@ -17,22 +17,22 @@
  * under the License.
  */
 
-import React from 'react';
-import {
-  Switch,
-  FormControl,
-  FormLabel,
-  Spinner,
-} from '@chakra-ui/react';
+import React from "react";
+import { Switch, FormControl, FormLabel, Spinner } from "@chakra-ui/react";
 
-import { useAutoRefresh } from 'src/context/autorefresh';
+import { useAutoRefresh } from "src/context/autorefresh";
 
 const AutoRefresh = () => {
   const { isRefreshOn, toggleRefresh, isPaused } = useAutoRefresh();
 
   return (
     <FormControl display="flex" width="auto" mr={2} alignItems="center">
-      <Spinner color="blue.500" speed="1s" mr="4px" visibility={isRefreshOn ? 'visible' : 'hidden'} />
+      <Spinner
+        color="blue.500"
+        speed="1s"
+        mr="4px"
+        visibility={isRefreshOn ? "visible" : "hidden"}
+      />
       <FormLabel
         htmlFor="auto-refresh"
         mb={0}
@@ -48,7 +48,9 @@ const AutoRefresh = () => {
         isDisabled={isPaused}
         isChecked={isRefreshOn}
         size="lg"
-        title={isPaused ? 'Autorefresh is disabled while the DAG is paused' : ''}
+        title={
+          isPaused ? "Autorefresh is disabled while the DAG is paused" : ""
+        }
       />
     </FormControl>
   );
diff --git a/airflow/www/static/js/components/Clipboard.test.tsx b/airflow/www/static/js/components/Clipboard.test.tsx
index a27cdf16ce..1d7b39bdb4 100644
--- a/airflow/www/static/js/components/Clipboard.test.tsx
+++ b/airflow/www/static/js/components/Clipboard.test.tsx
@@ -19,21 +19,24 @@
 
 /* global describe, test, expect, jest, window */
 
-import React from 'react';
-import '@testing-library/jest-dom';
-import { render, fireEvent } from '@testing-library/react';
+import React from "react";
+import "@testing-library/jest-dom";
+import { render, fireEvent } from "@testing-library/react";
 
-import { ClipboardButton } from './Clipboard';
+import { ClipboardButton } from "./Clipboard";
 
-describe('ClipboardButton', () => {
-  test('Loads button', async () => {
+describe("ClipboardButton", () => {
+  test("Loads button", async () => {
     const windowPrompt = window.prompt;
     window.prompt = jest.fn();
     const { getByText } = render(<ClipboardButton value="lorem ipsum" />);
 
     const button = getByText(/copy/i);
     fireEvent.click(button);
-    expect(window.prompt).toHaveBeenCalledWith('Copy to clipboard: Ctrl+C, Enter', 'lorem ipsum');
+    expect(window.prompt).toHaveBeenCalledWith(
+      "Copy to clipboard: Ctrl+C, Enter",
+      "lorem ipsum"
+    );
     window.prompt = windowPrompt;
   });
 });
diff --git a/airflow/www/static/js/components/Clipboard.tsx b/airflow/www/static/js/components/Clipboard.tsx
index eca0e94dbf..0e0a5d451b 100644
--- a/airflow/www/static/js/components/Clipboard.tsx
+++ b/airflow/www/static/js/components/Clipboard.tsx
@@ -17,31 +17,31 @@
  * under the License.
  */
 
-import React from 'react';
+import React from "react";
 import {
   Button,
   IconButton,
   Tooltip,
   useClipboard,
   forwardRef,
-} from '@chakra-ui/react';
-import { FiCopy } from 'react-icons/fi';
+} from "@chakra-ui/react";
+import { FiCopy } from "react-icons/fi";
 
-import { useContainerRef } from 'src/context/containerRef';
+import { useContainerRef } from "src/context/containerRef";
 
 export const ClipboardButton = forwardRef(
   (
     {
       value,
-      variant = 'outline',
+      variant = "outline",
       iconOnly = false,
-      label = 'copy',
-      title = 'Copy',
-      colorScheme = 'blue',
-      'aria-label': ariaLabel = 'Copy',
+      label = "copy",
+      title = "Copy",
+      colorScheme = "blue",
+      "aria-label": ariaLabel = "Copy",
       ...rest
     },
-    ref,
+    ref
   ) => {
     const { hasCopied, onCopy } = useClipboard(value);
     const containerRef = useContainerRef();
@@ -64,7 +64,11 @@ export const ClipboardButton = forwardRef(
         portalProps={{ containerRef }}
       >
         {iconOnly ? (
-          <IconButton icon={<FiCopy />} aria-label={ariaLabel} {...commonProps} />
+          <IconButton
+            icon={<FiCopy />}
+            aria-label={ariaLabel}
+            {...commonProps}
+          />
         ) : (
           <Button leftIcon={<FiCopy />} {...commonProps}>
             {label}
@@ -72,16 +76,23 @@ export const ClipboardButton = forwardRef(
         )}
       </Tooltip>
     );
-  },
+  }
 );
 
 interface Props {
-  value: string
+  value: string;
 }
 
 export const ClipboardText = ({ value }: Props) => (
   <>
     {value}
-    <ClipboardButton value={value} iconOnly variant="ghost" size="xs" fontSize="xl" ml={1} />
+    <ClipboardButton
+      value={value}
+      iconOnly
+      variant="ghost"
+      size="xs"
+      fontSize="xl"
+      ml={1}
+    />
   </>
 );
diff --git a/airflow/www/static/js/components/ConfirmDialog.tsx b/airflow/www/static/js/components/ConfirmDialog.tsx
index 055186e6a2..4826f11ff5 100644
--- a/airflow/www/static/js/components/ConfirmDialog.tsx
+++ b/airflow/www/static/js/components/ConfirmDialog.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import React, { PropsWithChildren, useRef } from 'react';
+import React, { PropsWithChildren, useRef } from "react";
 import {
   AlertDialog,
   AlertDialogBody,
@@ -28,9 +28,9 @@ import {
   Button,
   Code,
   Text,
-} from '@chakra-ui/react';
+} from "@chakra-ui/react";
 
-import { useContainerRef } from 'src/context/containerRef';
+import { useContainerRef } from "src/context/containerRef";
 
 interface Props extends PropsWithChildren {
   isOpen: boolean;
@@ -43,7 +43,14 @@ interface Props extends PropsWithChildren {
 }
 
 const ConfirmDialog = ({
-  isOpen, onClose, title = 'Wait a minute', description, affectedTasks, onConfirm, isLoading = false, children,
+  isOpen,
+  onClose,
+  title = "Wait a minute",
+  description,
+  affectedTasks,
+  onConfirm,
+  isLoading = false,
+  children,
 }: Props) => {
   const initialFocusRef = useRef<HTMLButtonElement>(null);
   const containerRef = useContainerRef();
@@ -68,18 +75,22 @@ const ConfirmDialog = ({
             {children}
             <Text mb={2}>{description}</Text>
             {affectedTasks.map((ti) => (
-              <Code width="100%" key={ti} fontSize="lg">{ti}</Code>
+              <Code width="100%" key={ti} fontSize="lg">
+                {ti}
+              </Code>
             ))}
-            {!affectedTasks.length && (
-              <Text>No task instances to change.</Text>
-            )}
+            {!affectedTasks.length && <Text>No task instances to change.</Text>}
           </AlertDialogBody>
 
           <AlertDialogFooter>
-            <Button onClick={onClose}>
-              Cancel
-            </Button>
-            <Button colorScheme="blue" onClick={onConfirm} ml={3} ref={initialFocusRef} isLoading={isLoading}>
+            <Button onClick={onClose}>Cancel</Button>
+            <Button
+              colorScheme="blue"
+              onClick={onConfirm}
+              ml={3}
+              ref={initialFocusRef}
+              isLoading={isLoading}
+            >
               Confirm
             </Button>
           </AlertDialogFooter>
diff --git a/airflow/www/static/js/components/InfoTooltip.tsx b/airflow/www/static/js/components/InfoTooltip.tsx
index 0cf593d466..b0c7263593 100644
--- a/airflow/www/static/js/components/InfoTooltip.tsx
+++ b/airflow/www/static/js/components/InfoTooltip.tsx
@@ -17,13 +17,11 @@
  * under the License.
  */
 
-import React, { ReactNode } from 'react';
-import {
-  Box, Tooltip,
-} from '@chakra-ui/react';
-import { MdInfo } from 'react-icons/md';
-import { useContainerRef } from 'src/context/containerRef';
-import type { IconBaseProps } from 'react-icons';
+import React, { ReactNode } from "react";
+import { Box, Tooltip } from "@chakra-ui/react";
+import { MdInfo } from "react-icons/md";
+import { useContainerRef } from "src/context/containerRef";
+import type { IconBaseProps } from "react-icons";
 
 interface InfoTooltipProps extends IconBaseProps {
   label: ReactNode;
diff --git a/airflow/www/static/js/components/LinkButton.test.tsx b/airflow/www/static/js/components/LinkButton.test.tsx
index 0fc9181a58..fe11d8e8bf 100644
--- a/airflow/www/static/js/components/LinkButton.test.tsx
+++ b/airflow/www/static/js/components/LinkButton.test.tsx
@@ -19,20 +19,20 @@
 
 /* global describe, test, expect */
 
-import React from 'react';
-import { render } from '@testing-library/react';
+import React from "react";
+import { render } from "@testing-library/react";
 
-import LinkButton from './LinkButton';
+import LinkButton from "./LinkButton";
 
-describe('Test LinkButton Component.', () => {
-  test('LinkButton should be rendered as a link.', () => {
+describe("Test LinkButton Component.", () => {
+  test("LinkButton should be rendered as a link.", () => {
     const { getByText, container } = render(
       <LinkButton>
         <div>The link</div>
-      </LinkButton>,
+      </LinkButton>
     );
 
-    expect(getByText('The link')).toBeDefined();
-    expect(container.querySelector('a')).not.toBeNull();
+    expect(getByText("The link")).toBeDefined();
+    expect(container.querySelector("a")).not.toBeNull();
   });
 });
diff --git a/airflow/www/static/js/components/LinkButton.tsx b/airflow/www/static/js/components/LinkButton.tsx
index 787157c694..5bd665a2d5 100644
--- a/airflow/www/static/js/components/LinkButton.tsx
+++ b/airflow/www/static/js/components/LinkButton.tsx
@@ -17,18 +17,18 @@
  * under the License.
  */
 
-import React from 'react';
-import {
-  Button,
-  ButtonProps,
-  Link,
-} from '@chakra-ui/react';
+import React from "react";
+import { Button, ButtonProps, Link } from "@chakra-ui/react";
 
 interface Props extends ButtonProps {
   href?: string;
   target?: string;
 }
 
-const LinkButton = ({ children, ...rest }: Props) => (<Button as={Link} variant="ghost" colorScheme="blue" {...rest}>{children}</Button>);
+const LinkButton = ({ children, ...rest }: Props) => (
+  <Button as={Link} variant="ghost" colorScheme="blue" {...rest}>
+    {children}
+  </Button>
+);
 
 export default LinkButton;
diff --git a/airflow/www/static/js/components/MultiSelect.tsx b/airflow/www/static/js/components/MultiSelect.tsx
index f326c78e5d..7c5899bf32 100644
--- a/airflow/www/static/js/components/MultiSelect.tsx
+++ b/airflow/www/static/js/components/MultiSelect.tsx
@@ -17,9 +17,9 @@
  * under the License.
  */
 
-import React from 'react';
-import { Select } from 'chakra-react-select';
-import type { SelectComponent } from 'chakra-react-select';
+import React from "react";
+import { Select } from "chakra-react-select";
+import type { SelectComponent } from "chakra-react-select";
 
 const MultiSelect: SelectComponent = ({ chakraStyles, ...props }) => (
   <Select
@@ -29,13 +29,13 @@ const MultiSelect: SelectComponent = ({ chakraStyles, ...props }) => (
     chakraStyles={{
       dropdownIndicator: (provided) => ({
         ...provided,
-        bg: 'transparent',
+        bg: "transparent",
         px: 2,
-        cursor: 'inherit',
+        cursor: "inherit",
       }),
       indicatorSeparator: (provided) => ({
         ...provided,
-        display: 'none',
+        display: "none",
       }),
       menuList: (provided) => ({
         ...provided,
@@ -43,7 +43,7 @@ const MultiSelect: SelectComponent = ({ chakraStyles, ...props }) => (
       }),
       placeholder: (provided) => ({
         ...provided,
-        color: 'inherit',
+        color: "inherit",
       }),
       ...chakraStyles,
     }}
diff --git a/airflow/www/static/js/components/ReactMarkdown.tsx b/airflow/www/static/js/components/ReactMarkdown.tsx
index cdf785fa81..b6c6532772 100644
--- a/airflow/www/static/js/components/ReactMarkdown.tsx
+++ b/airflow/www/static/js/components/ReactMarkdown.tsx
@@ -35,35 +35,33 @@ import {
   Thead,
   Tr,
   UnorderedList,
-} from '@chakra-ui/react';
-import React, { PropsWithChildren, ReactNode } from 'react';
+} from "@chakra-ui/react";
+import React, { PropsWithChildren, ReactNode } from "react";
 
-import type { Components } from 'react-markdown';
+import type { Components } from "react-markdown";
 
-import ReactMD from 'react-markdown';
-import type { ReactMarkdownOptions } from 'react-markdown/lib/react-markdown';
+import ReactMD from "react-markdown";
+import type { ReactMarkdownOptions } from "react-markdown/lib/react-markdown";
 
-import remarkGfm from 'remark-gfm';
+import remarkGfm from "remark-gfm";
 
 const fontSizeMapping = {
-  h1: '1.5em',
-  h2: '1.25em',
-  h3: '1.125em',
-  h4: '1em',
-  h5: '0.875em',
-  h6: '0.75em',
+  h1: "1.5em",
+  h2: "1.25em",
+  h3: "1.125em",
+  h4: "1em",
+  h5: "0.875em",
+  h6: "0.75em",
 };
 
-const makeHeading = (h: keyof typeof fontSizeMapping) => (
-  {
-    children,
-    ...props
-  }: PropsWithChildren,
-) => (
-  <Heading as={h} fontSize={fontSizeMapping[h]} {...props} my={3}>
-    {children}
-  </Heading>
-);
+const makeHeading =
+  (h: keyof typeof fontSizeMapping) =>
+  ({ children, ...props }: PropsWithChildren) =>
+    (
+      <Heading as={h} fontSize={fontSizeMapping[h]} {...props} my={3}>
+        {children}
+      </Heading>
+    );
 
 const components = {
   p: ({ children }: PropsWithChildren) => <Text>{children}</Text>,
@@ -80,8 +78,15 @@ const components = {
       {children}
     </Box>
   ),
-  code: ({ inline, className, children }: {
-    inline?: boolean, className?: string, children: ReactNode }) => {
+  code: ({
+    inline,
+    className,
+    children,
+  }: {
+    inline?: boolean;
+    className?: string;
+    children: ReactNode;
+  }) => {
     if (inline) {
       return <Code p={2}>{children}</Code>;
     }
@@ -100,44 +105,38 @@ const components = {
   },
   del: ({ children }: PropsWithChildren) => <Text as="del">{children}</Text>,
   hr: () => <Divider my={3} />,
-  a: ({ href, title, children }: { href: string, title?: string, children: ReactNode }) => <Link fontWeight="bold" color="blue.600" href={href} title={title}>{children}</Link>,
+  a: ({
+    href,
+    title,
+    children,
+  }: {
+    href: string;
+    title?: string;
+    children: ReactNode;
+  }) => (
+    <Link fontWeight="bold" color="blue.600" href={href} title={title}>
+      {children}
+    </Link>
+  ),
   img: (props: ImageProps) => <Image my={3} {...props} maxWidth="300px" />,
   text: ({ children }: PropsWithChildren) => <Text as="span">{children}</Text>,
-  ul: ({
-    children,
-  }: PropsWithChildren) => (
-    <UnorderedList
-      spacing={1}
-      pl={4}
-      mb={3}
-    >
+  ul: ({ children }: PropsWithChildren) => (
+    <UnorderedList spacing={1} pl={4} mb={3}>
       {children}
     </UnorderedList>
   ),
-  ol: ({
-    children,
-  }: PropsWithChildren) => (
-    <OrderedList
-      spacing={1}
-      pl={4}
-      mb={3}
-    >
+  ol: ({ children }: PropsWithChildren) => (
+    <OrderedList spacing={1} pl={4} mb={3}>
       {children}
     </OrderedList>
   ),
-  li: ({
-    children,
-  }: PropsWithChildren) => (
-    <ListItem>
-      {children}
-    </ListItem>
-  ),
-  h1: makeHeading('h1'),
-  h2: makeHeading('h2'),
-  h3: makeHeading('h3'),
-  h4: makeHeading('h4'),
-  h5: makeHeading('h5'),
-  h6: makeHeading('h6'),
+  li: ({ children }: PropsWithChildren) => <ListItem>{children}</ListItem>,
+  h1: makeHeading("h1"),
+  h2: makeHeading("h2"),
+  h3: makeHeading("h3"),
+  h4: makeHeading("h4"),
+  h5: makeHeading("h5"),
+  h6: makeHeading("h6"),
   pre: ({ children }: PropsWithChildren) => <Code my={3}>{children}</Code>,
   table: ({ children }: PropsWithChildren) => <Table mb={3}>{children}</Table>,
   thead: Thead,
diff --git a/airflow/www/static/js/components/RunTypeIcon.tsx b/airflow/www/static/js/components/RunTypeIcon.tsx
index d4212a9492..4618562630 100644
--- a/airflow/www/static/js/components/RunTypeIcon.tsx
+++ b/airflow/www/static/js/components/RunTypeIcon.tsx
@@ -17,28 +17,28 @@
  * under the License.
  */
 
-import React from 'react';
-import { MdPlayArrow, MdOutlineSchedule } from 'react-icons/md';
-import { RiArrowGoBackFill } from 'react-icons/ri';
-import { HiDatabase } from 'react-icons/hi';
+import React from "react";
+import { MdPlayArrow, MdOutlineSchedule } from "react-icons/md";
+import { RiArrowGoBackFill } from "react-icons/ri";
+import { HiDatabase } from "react-icons/hi";
 
-import type { IconBaseProps } from 'react-icons';
-import type { DagRun } from 'src/types';
+import type { IconBaseProps } from "react-icons";
+import type { DagRun } from "src/types";
 
 interface Props extends IconBaseProps {
-  runType: DagRun['runType'];
+  runType: DagRun["runType"];
 }
 
 const DagRunTypeIcon = ({ runType, ...rest }: Props) => {
   switch (runType) {
-    case 'manual':
-      return <MdPlayArrow style={{ display: 'inline' }} {...rest} />;
-    case 'backfill':
-      return <RiArrowGoBackFill style={{ display: 'inline' }} {...rest} />;
-    case 'scheduled':
-      return <MdOutlineSchedule style={{ display: 'inline' }} {...rest} />;
-    case 'dataset_triggered':
-      return <HiDatabase style={{ display: 'inline' }} {...rest} />;
+    case "manual":
+      return <MdPlayArrow style={{ display: "inline" }} {...rest} />;
+    case "backfill":
+      return <RiArrowGoBackFill style={{ display: "inline" }} {...rest} />;
+    case "scheduled":
+      return <MdOutlineSchedule style={{ display: "inline" }} {...rest} />;
+    case "dataset_triggered":
+      return <HiDatabase style={{ display: "inline" }} {...rest} />;
     default:
       return null;
   }
diff --git a/airflow/www/static/js/components/TabWithTooltip.tsx b/airflow/www/static/js/components/TabWithTooltip.tsx
index c27b5f6b84..dfe2ffaa59 100644
--- a/airflow/www/static/js/components/TabWithTooltip.tsx
+++ b/airflow/www/static/js/components/TabWithTooltip.tsx
@@ -17,31 +17,33 @@
  * under the License.
  */
 
-import React from 'react';
+import React from "react";
 import {
   Box,
   useTab,
   useMultiStyleConfig,
   Button,
   TabProps,
-} from '@chakra-ui/react';
+} from "@chakra-ui/react";
 
-const TabWithTooltip = React.forwardRef<HTMLDivElement, TabProps>((props, ref) => {
-  const tabProps = useTab({ ...props, ref });
-  const styles = useMultiStyleConfig('Tabs', tabProps);
+const TabWithTooltip = React.forwardRef<HTMLDivElement, TabProps>(
+  (props, ref) => {
+    const tabProps = useTab({ ...props, ref });
+    const styles = useMultiStyleConfig("Tabs", tabProps);
 
-  return (
-    <Box {...tabProps}>
-      <Button
-        __css={styles.tab}
-        {...tabProps}
-        pointerEvents={props.isDisabled ? 'none' : 'auto'}
-        py={3}
-      >
-        {tabProps.children}
-      </Button>
-    </Box>
-  );
-});
+    return (
+      <Box {...tabProps}>
+        <Button
+          __css={styles.tab}
+          {...tabProps}
+          pointerEvents={props.isDisabled ? "none" : "auto"}
+          py={3}
+        >
+          {tabProps.children}
+        </Button>
+      </Box>
+    );
+  }
+);
 
 export default TabWithTooltip;
diff --git a/airflow/www/static/js/components/Table/Cells.test.tsx b/airflow/www/static/js/components/Table/Cells.test.tsx
index e535e53719..ac1273b3e1 100644
--- a/airflow/www/static/js/components/Table/Cells.test.tsx
+++ b/airflow/www/static/js/components/Table/Cells.test.tsx
@@ -19,27 +19,25 @@
 
 /* global describe, test, expect */
 
-import React from 'react';
-import '@testing-library/jest-dom';
-import { render } from '@testing-library/react';
+import React from "react";
+import "@testing-library/jest-dom";
+import { render } from "@testing-library/react";
 
-import { ChakraWrapper } from 'src/utils/testUtils';
-import * as utils from 'src/utils';
-import { TaskInstanceLink } from './Cells';
+import { ChakraWrapper } from "src/utils/testUtils";
+import * as utils from "src/utils";
+import { TaskInstanceLink } from "./Cells";
 
-const taskId = 'task_id';
-const sourceDagId = 'source_dag_id';
-const sourceRunId = 'source_run_id';
-const originalDagId = 'og_dag_id';
+const taskId = "task_id";
+const sourceDagId = "source_dag_id";
+const sourceRunId = "source_run_id";
+const originalDagId = "og_dag_id";
 
-describe('Test TaskInstanceLink', () => {
-  test('Replaces __DAG_ID__ url param correctly', async () => {
-    jest.spyOn(utils, 'getMetaValue').mockImplementation(
-      (meta) => {
-        if (meta === 'grid_url') return '/dags/__DAG_ID__/grid';
-        return '';
-      },
-    );
+describe("Test TaskInstanceLink", () => {
+  test("Replaces __DAG_ID__ url param correctly", async () => {
+    jest.spyOn(utils, "getMetaValue").mockImplementation((meta) => {
+      if (meta === "grid_url") return "/dags/__DAG_ID__/grid";
+      return "";
+    });
 
     const { getByText } = render(
       <TaskInstanceLink
@@ -54,22 +52,23 @@ describe('Test TaskInstanceLink', () => {
           },
         }}
       />,
-      { wrapper: ChakraWrapper },
+      { wrapper: ChakraWrapper }
     );
 
     const link = getByText(`${sourceDagId}.${taskId}`);
     expect(link).toBeInTheDocument();
-    expect(link).toHaveAttribute('href', `/dags/${sourceDagId}/grid?dag_run_id=${sourceRunId}&task_id=${taskId}`);
+    expect(link).toHaveAttribute(
+      "href",
+      `/dags/${sourceDagId}/grid?dag_run_id=${sourceRunId}&task_id=${taskId}`
+    );
   });
 
-  test('Replaces existing dag id url param correctly', async () => {
-    jest.spyOn(utils, 'getMetaValue').mockImplementation(
-      (meta) => {
-        if (meta === 'dag_id') return originalDagId;
-        if (meta === 'grid_url') return `/dags/${originalDagId}/grid`;
-        return '';
-      },
-    );
+  test("Replaces existing dag id url param correctly", async () => {
+    jest.spyOn(utils, "getMetaValue").mockImplementation((meta) => {
+      if (meta === "dag_id") return originalDagId;
+      if (meta === "grid_url") return `/dags/${originalDagId}/grid`;
+      return "";
+    });
 
     const { getByText } = render(
       <TaskInstanceLink
@@ -84,11 +83,14 @@ describe('Test TaskInstanceLink', () => {
           },
         }}
       />,
-      { wrapper: ChakraWrapper },
+      { wrapper: ChakraWrapper }
     );
 
     const link = getByText(`${sourceDagId}.${taskId}`);
     expect(link).toBeInTheDocument();
-    expect(link).toHaveAttribute('href', `/dags/${sourceDagId}/grid?dag_run_id=${sourceRunId}&task_id=${taskId}`);
+    expect(link).toHaveAttribute(
+      "href",
+      `/dags/${sourceDagId}/grid?dag_run_id=${sourceRunId}&task_id=${taskId}`
+    );
   });
 });
diff --git a/airflow/www/static/js/components/Table/Cells.tsx b/airflow/www/static/js/components/Table/Cells.tsx
index 6c34f5d9b1..8084eb917c 100644
--- a/airflow/www/static/js/components/Table/Cells.tsx
+++ b/airflow/www/static/js/components/Table/Cells.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import React, { useMemo } from 'react';
+import React, { useMemo } from "react";
 import {
   Flex,
   Code,
@@ -31,27 +31,29 @@ import {
   ModalOverlay,
   ModalBody,
   ModalHeader,
-} from '@chakra-ui/react';
+} from "@chakra-ui/react";
 
-import { Table } from 'src/components/Table';
-import Time from 'src/components/Time';
-import { getMetaValue } from 'src/utils';
-import { useContainerRef } from 'src/context/containerRef';
-import { SimpleStatus } from 'src/dag/StatusBox';
+import { Table } from "src/components/Table";
+import Time from "src/components/Time";
+import { getMetaValue } from "src/utils";
+import { useContainerRef } from "src/context/containerRef";
+import { SimpleStatus } from "src/dag/StatusBox";
 
 interface CellProps {
   cell: {
     value: any;
     row: {
       original: Record<string, any>;
-    }
-  }
+    };
+  };
 }
 
-export const TimeCell = ({ cell: { value } }: CellProps) => <Time dateTime={value} />;
+export const TimeCell = ({ cell: { value } }: CellProps) => (
+  <Time dateTime={value} />
+);
 
 export const DatasetLink = ({ cell: { value } }: CellProps) => {
-  const datasetsUrl = getMetaValue('datasets_url');
+  const datasetsUrl = getMetaValue("datasets_url");
   return (
     <Link
       color="blue.600"
@@ -63,17 +65,17 @@ export const DatasetLink = ({ cell: { value } }: CellProps) => {
 };
 
 export const DagRunLink = ({ cell: { value, row } }: CellProps) => {
-  const dagId = getMetaValue('dag_id');
-  const gridUrl = getMetaValue('grid_url');
-  const stringToReplace = dagId || '__DAG_ID__';
-  const url = `${gridUrl?.replace(stringToReplace, value)}?dag_run_id=${encodeURIComponent(row.original.dagRunId)}`;
+  const dagId = getMetaValue("dag_id");
+  const gridUrl = getMetaValue("grid_url");
+  const stringToReplace = dagId || "__DAG_ID__";
+  const url = `${gridUrl?.replace(
+    stringToReplace,
+    value
+  )}?dag_run_id=${encodeURIComponent(row.original.dagRunId)}`;
   return (
     <Flex alignItems="center">
       <SimpleStatus state={row.original.state} mr={2} />
-      <Link
-        color="blue.600"
-        href={url}
-      >
+      <Link color="blue.600" href={url}>
         {value}
       </Link>
     </Flex>
@@ -87,29 +89,28 @@ export const TriggeredRuns = ({ cell: { value, row } }: CellProps) => {
   const columns = useMemo(
     () => [
       {
-        Header: 'DAG Id',
-        accessor: 'dagId',
+        Header: "DAG Id",
+        accessor: "dagId",
         Cell: DagRunLink,
       },
       {
-        Header: 'Logical Date',
-        accessor: 'logicalDate',
+        Header: "Logical Date",
+        accessor: "logicalDate",
         Cell: TimeCell,
       },
     ],
-    [],
+    []
   );
 
-  const data = useMemo(
-    () => value,
-    [value],
-  );
+  const data = useMemo(() => value, [value]);
 
   if (!value || !value.length) return null;
 
   return (
     <Box>
-      <Text color="blue.600" cursor="pointer" onClick={onToggle}>{value.length}</Text>
+      <Text color="blue.600" cursor="pointer" onClick={onToggle}>
+        {value.length}
+      </Text>
       <Modal
         size="3xl"
         isOpen={isOpen}
@@ -121,21 +122,21 @@ export const TriggeredRuns = ({ cell: { value, row } }: CellProps) => {
         <ModalOverlay />
         <ModalContent>
           <ModalHeader>
-            <Text as="span" color="gray.400">Dag Runs triggered by</Text>
+            <Text as="span" color="gray.400">
+              Dag Runs triggered by
+            </Text>
             <br />
             {row.original.datasetUri}
             <br />
-            <Text as="span" color="gray.400">at</Text>
+            <Text as="span" color="gray.400">
+              at
+            </Text>
             <br />
             <Time dateTime={row.original.timestamp} />
           </ModalHeader>
           <ModalCloseButton />
           <ModalBody>
-            <Table
-              data={data}
-              columns={columns}
-              pageSize={data.length}
-            />
+            <Table data={data} columns={columns} pageSize={data.length} />
           </ModalBody>
         </ModalContent>
       </Modal>
@@ -145,19 +146,26 @@ export const TriggeredRuns = ({ cell: { value, row } }: CellProps) => {
 
 export const TaskInstanceLink = ({ cell: { value, row } }: CellProps) => {
   const { sourceRunId, sourceDagId, sourceMapIndex } = row.original;
-  const gridUrl = getMetaValue('grid_url');
-  const dagId = getMetaValue('dag_id');
-  const stringToReplace = dagId || '__DAG_ID__';
-  const url = `${gridUrl?.replace(stringToReplace, sourceDagId)}?dag_run_id=${encodeURIComponent(sourceRunId)}&task_id=${encodeURIComponent(value)}`;
-  const mapIndex = sourceMapIndex > -1 ? `[${sourceMapIndex}]` : '';
+  const gridUrl = getMetaValue("grid_url");
+  const dagId = getMetaValue("dag_id");
+  const stringToReplace = dagId || "__DAG_ID__";
+  const url = `${gridUrl?.replace(
+    stringToReplace,
+    sourceDagId
+  )}?dag_run_id=${encodeURIComponent(sourceRunId)}&task_id=${encodeURIComponent(
+    value
+  )}`;
+  const mapIndex = sourceMapIndex > -1 ? `[${sourceMapIndex}]` : "";
   return (
     <Box>
-      <Link color="blue.600" href={url}>{`${sourceDagId}.${value}${mapIndex}`}</Link>
+      <Link
+        color="blue.600"
+        href={url}
+      >{`${sourceDagId}.${value}${mapIndex}`}</Link>
       <Text>{sourceRunId}</Text>
     </Box>
   );
 };
 
-export const CodeCell = ({ cell: { value } }: CellProps) => (
-  value ? <Code>{JSON.stringify(value)}</Code> : null
-);
+export const CodeCell = ({ cell: { value } }: CellProps) =>
+  value ? <Code>{JSON.stringify(value)}</Code> : null;
diff --git a/airflow/www/static/js/components/Table/Table.test.tsx b/airflow/www/static/js/components/Table/Table.test.tsx
index 79f713ba9f..a3664d49a2 100644
--- a/airflow/www/static/js/components/Table/Table.test.tsx
+++ b/airflow/www/static/js/components/Table/Table.test.tsx
@@ -19,53 +19,53 @@
 
 /* global describe, test, expect */
 
-import React, { useState } from 'react';
-import '@testing-library/jest-dom';
-import { render, fireEvent, within } from '@testing-library/react';
-import { sortBy } from 'lodash';
-import type { SortingRule } from 'react-table';
+import React, { useState } from "react";
+import "@testing-library/jest-dom";
+import { render, fireEvent, within } from "@testing-library/react";
+import { sortBy } from "lodash";
+import type { SortingRule } from "react-table";
 
-import { ChakraWrapper } from 'src/utils/testUtils';
-import { Table } from '.';
+import { ChakraWrapper } from "src/utils/testUtils";
+import { Table } from ".";
 
 const data: Record<string, any>[] = [
-  { firstName: 'Lamont', lastName: 'Grimes', country: 'United States' },
-  { firstName: 'Alysa', lastName: 'Armstrong', country: 'Spain' },
-  { firstName: 'Petra', lastName: 'Blick', country: 'France' },
-  { firstName: 'Jeromy', lastName: 'Herman', country: 'Mexico' },
-  { firstName: 'Eleonore', lastName: 'Rohan', country: 'Nigeria' },
+  { firstName: "Lamont", lastName: "Grimes", country: "United States" },
+  { firstName: "Alysa", lastName: "Armstrong", country: "Spain" },
+  { firstName: "Petra", lastName: "Blick", country: "France" },
+  { firstName: "Jeromy", lastName: "Herman", country: "Mexico" },
+  { firstName: "Eleonore", lastName: "Rohan", country: "Nigeria" },
 ];
 
 const columns = [
   {
-    Header: 'First Name',
-    accessor: 'firstName',
+    Header: "First Name",
+    accessor: "firstName",
   },
   {
-    Header: 'Last Name',
-    accessor: 'lastName',
+    Header: "Last Name",
+    accessor: "lastName",
   },
   {
-    Header: 'Country',
-    accessor: 'country',
+    Header: "Country",
+    accessor: "country",
   },
 ];
 
-describe('Test Table', () => {
-  test('Displays correct data', async () => {
+describe("Test Table", () => {
+  test("Displays correct data", async () => {
     const { getAllByRole, getByText, queryByTitle } = render(
       <Table data={data} columns={columns} />,
-      { wrapper: ChakraWrapper },
+      { wrapper: ChakraWrapper }
     );
 
-    const rows = getAllByRole('row');
+    const rows = getAllByRole("row");
     const name1 = getByText(data[0].firstName);
     const name2 = getByText(data[1].firstName);
     const name3 = getByText(data[2].firstName);
     const name4 = getByText(data[3].firstName);
     const name5 = getByText(data[4].firstName);
-    const previous = queryByTitle('Previous Page');
-    const next = queryByTitle('Next Page');
+    const previous = queryByTitle("Previous Page");
+    const next = queryByTitle("Next Page");
 
     // table header is a row so add 1 to expected amount
     expect(rows).toHaveLength(6);
@@ -81,17 +81,17 @@ describe('Test Table', () => {
     expect(next).toBeNull();
   });
 
-  test('Shows empty state', async () => {
+  test("Shows empty state", async () => {
     const { getAllByRole, getByText } = render(
       <Table data={[]} columns={columns} />,
-      { wrapper: ChakraWrapper },
+      { wrapper: ChakraWrapper }
     );
 
-    const rows = getAllByRole('row');
+    const rows = getAllByRole("row");
 
     // table header is a row so add 1 to expected amount
     expect(rows).toHaveLength(2);
-    expect(getByText('No Data found.')).toBeInTheDocument();
+    expect(getByText("No Data found.")).toBeInTheDocument();
   });
 
   // Simulated pagination that would be done serverside
@@ -113,10 +113,10 @@ describe('Test Table', () => {
     );
   };
 
-  test('With manual pagination', async () => {
+  test("With manual pagination", async () => {
     const { getAllByRole, queryByText, getByTitle } = render(
       <PaginatedTable />,
-      { wrapper: ChakraWrapper },
+      { wrapper: ChakraWrapper }
     );
 
     const name1 = data[0].firstName;
@@ -124,12 +124,12 @@ describe('Test Table', () => {
     const name3 = data[2].firstName;
     const name4 = data[3].firstName;
     const name5 = data[4].firstName;
-    const previous = getByTitle('Previous Page');
-    const next = getByTitle('Next Page');
+    const previous = getByTitle("Previous Page");
+    const next = getByTitle("Next Page");
 
     /// // PAGE ONE // ///
     // table header is a row so add 1 to expected amount
-    expect(getAllByRole('row')).toHaveLength(3);
+    expect(getAllByRole("row")).toHaveLength(3);
 
     expect(queryByText(name1)).toBeInTheDocument();
     expect(queryByText(name2)).toBeInTheDocument();
@@ -144,7 +144,7 @@ describe('Test Table', () => {
     fireEvent.click(next);
 
     /// // PAGE TWO // ///
-    expect(getAllByRole('row')).toHaveLength(3);
+    expect(getAllByRole("row")).toHaveLength(3);
 
     expect(queryByText(name1)).toBeNull();
     expect(queryByText(name2)).toBeNull();
@@ -159,7 +159,7 @@ describe('Test Table', () => {
     fireEvent.click(next);
 
     /// // PAGE THREE // ///
-    expect(getAllByRole('row')).toHaveLength(2);
+    expect(getAllByRole("row")).toHaveLength(2);
 
     expect(queryByText(name1)).toBeNull();
     expect(queryByText(name2)).toBeNull();
@@ -194,15 +194,14 @@ describe('Test Table', () => {
     );
   };
 
-  test('With manual sorting', async () => {
-    const { getAllByRole } = render(
-      <SortedTable />,
-      { wrapper: ChakraWrapper },
-    );
+  test("With manual sorting", async () => {
+    const { getAllByRole } = render(<SortedTable />, {
+      wrapper: ChakraWrapper,
+    });
 
     // Default order matches original data order //
-    const firstNameHeader = getAllByRole('columnheader')[0];
-    const rows = getAllByRole('row');
+    const firstNameHeader = getAllByRole("columnheader")[0];
+    const rows = getAllByRole("row");
 
     const firstRowName = within(rows[1]).queryByText(data[0].firstName);
     const lastRowName = within(rows[5]).queryByText(data[4].firstName);
@@ -212,43 +211,47 @@ describe('Test Table', () => {
     fireEvent.click(firstNameHeader);
 
     /// // ASCENDING SORT // ///
-    const ascendingRows = getAllByRole('row');
+    const ascendingRows = getAllByRole("row");
     const ascendingData = sortBy(data, [(o) => o.firstName]);
 
-    const ascendingFirstRowName = within(ascendingRows[1]).queryByText(ascendingData[0].firstName);
-    const ascendingLastRowName = within(ascendingRows[5]).queryByText(ascendingData[4].firstName);
+    const ascendingFirstRowName = within(ascendingRows[1]).queryByText(
+      ascendingData[0].firstName
+    );
+    const ascendingLastRowName = within(ascendingRows[5]).queryByText(
+      ascendingData[4].firstName
+    );
     expect(ascendingFirstRowName).toBeInTheDocument();
     expect(ascendingLastRowName).toBeInTheDocument();
 
     fireEvent.click(firstNameHeader);
 
     /// // DESCENDING SORT // ///
-    const descendingRows = getAllByRole('row');
+    const descendingRows = getAllByRole("row");
     const descendingData = sortBy(data, [(o) => o.firstName]).reverse();
 
     const descendingFirstRowName = within(descendingRows[1]).queryByText(
-      descendingData[0].firstName,
+      descendingData[0].firstName
     );
     const descendingLastRowName = within(descendingRows[5]).queryByText(
-      descendingData[4].firstName,
+      descendingData[4].firstName
     );
     expect(descendingFirstRowName).toBeInTheDocument();
     expect(descendingLastRowName).toBeInTheDocument();
   });
 
-  test('Shows checkboxes', async () => {
+  test("Shows checkboxes", async () => {
     const { getAllByTitle } = render(
       <Table data={data} columns={columns} selectRows={() => {}} />,
-      { wrapper: ChakraWrapper },
+      { wrapper: ChakraWrapper }
     );
 
-    const checkboxes = getAllByTitle('Toggle Row Selected');
+    const checkboxes = getAllByTitle("Toggle Row Selected");
     expect(checkboxes).toHaveLength(data.length);
 
     const checkbox1 = checkboxes[1];
 
     fireEvent.click(checkbox1);
 
-    expect(checkbox1).toHaveAttribute('data-checked');
+    expect(checkbox1).toHaveAttribute("data-checked");
   });
 });
diff --git a/airflow/www/static/js/components/Table/index.tsx b/airflow/www/static/js/components/Table/index.tsx
index 11087bdb26..a2450707b3 100644
--- a/airflow/www/static/js/components/Table/index.tsx
+++ b/airflow/www/static/js/components/Table/index.tsx
@@ -19,11 +19,9 @@
 
 /*
  * Custom wrapper of react-table using Chakra UI components
-*/
+ */
 
-import React, {
-  useEffect, useRef, forwardRef, RefObject,
-} from 'react';
+import React, { useEffect, useRef, forwardRef, RefObject } from "react";
 import {
   Flex,
   Table as ChakraTable,
@@ -37,7 +35,7 @@ import {
   useColorModeValue,
   Checkbox,
   CheckboxProps,
-} from '@chakra-ui/react';
+} from "@chakra-ui/react";
 import {
   useTable,
   useSortBy,
@@ -47,34 +45,33 @@ import {
   Hooks,
   SortingRule,
   Row,
-} from 'react-table';
-import {
-  MdKeyboardArrowLeft, MdKeyboardArrowRight,
-} from 'react-icons/md';
+} from "react-table";
+import { MdKeyboardArrowLeft, MdKeyboardArrowRight } from "react-icons/md";
 import {
-  TiArrowUnsorted, TiArrowSortedDown, TiArrowSortedUp,
-} from 'react-icons/ti';
+  TiArrowUnsorted,
+  TiArrowSortedDown,
+  TiArrowSortedUp,
+} from "react-icons/ti";
 
 interface IndeterminateCheckboxProps extends CheckboxProps {
   indeterminate?: boolean;
 }
 
-const IndeterminateCheckbox = forwardRef<HTMLInputElement, IndeterminateCheckboxProps>(
-  ({ indeterminate, checked, ...rest }, ref) => {
-    const defaultRef = useRef<HTMLInputElement>(null);
-    const resolvedRef = ref as RefObject<HTMLInputElement> || defaultRef;
+const IndeterminateCheckbox = forwardRef<
+  HTMLInputElement,
+  IndeterminateCheckboxProps
+>(({ indeterminate, checked, ...rest }, ref) => {
+  const defaultRef = useRef<HTMLInputElement>(null);
+  const resolvedRef = (ref as RefObject<HTMLInputElement>) || defaultRef;
 
-    useEffect(() => {
-      if (resolvedRef.current) {
-        resolvedRef.current.indeterminate = !!indeterminate;
-      }
-    }, [resolvedRef, indeterminate]);
+  useEffect(() => {
+    if (resolvedRef.current) {
+      resolvedRef.current.indeterminate = !!indeterminate;
+    }
+  }, [resolvedRef, indeterminate]);
 
-    return (
-      <Checkbox ref={resolvedRef} isChecked={checked} {...rest} />
-    );
-  },
-);
+  return <Checkbox ref={resolvedRef} isChecked={checked} {...rest} />;
+});
 
 interface TableProps {
   data: object[];
@@ -83,12 +80,12 @@ interface TableProps {
     totalEntries: number;
     offset: number;
     setOffset: (offset: number) => void;
-  },
+  };
   manualSort?: {
     sortBy: SortingRule<object>[];
     setSortBy: (sortBy: SortingRule<object>[]) => void;
     initialSortBy?: SortingRule<object>[];
-  },
+  };
   pageSize?: number;
   isLoading?: boolean;
   selectRows?: (selectedRows: number[]) => void;
@@ -106,31 +103,35 @@ export const Table = ({
   onRowClicked,
 }: TableProps) => {
   const { totalEntries, offset, setOffset } = manualPagination || {};
-  const oddColor = useColorModeValue('gray.50', 'gray.900');
-  const hoverColor = useColorModeValue('gray.100', 'gray.700');
+  const oddColor = useColorModeValue("gray.50", "gray.900");
+  const hoverColor = useColorModeValue("gray.100", "gray.700");
 
-  const pageCount = totalEntries ? (Math.ceil(totalEntries / pageSize) || 1) : data.length;
+  const pageCount = totalEntries
+    ? Math.ceil(totalEntries / pageSize) || 1
+    : data.length;
 
   const lowerCount = (offset || 0) + 1;
   const upperCount = lowerCount + data.length - 1;
 
   // Don't show row selection if selectRows doesn't exist
   const selectProps = selectRows
-    ? [useRowSelect,
-      (hooks: Hooks) => {
-        hooks.visibleColumns.push((cols) => [
-          {
-            id: 'selection',
-            // eslint-disable-next-line react/no-unstable-nested-components
-            Cell: ({ row }) => (
-              <div>
-                <IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
-              </div>
-            ),
-          },
-          ...cols,
-        ]);
-      }]
+    ? [
+        useRowSelect,
+        (hooks: Hooks) => {
+          hooks.visibleColumns.push((cols) => [
+            {
+              id: "selection",
+              // eslint-disable-next-line react/no-unstable-nested-components
+              Cell: ({ row }) => (
+                <div>
+                  <IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
+                </div>
+              ),
+            },
+            ...cols,
+          ]);
+        },
+      ]
     : [];
 
   const {
@@ -161,7 +162,7 @@ export const Table = ({
     },
     useSortBy,
     usePagination,
-    ...selectProps,
+    ...selectProps
   );
 
   const handleNext = () => {
@@ -186,7 +187,7 @@ export const Table = ({
       // @ts-ignore
       selectRows(selectedFlatRows.map((row) => row.original.mapIndex));
     }
-  // eslint-disable-next-line react-hooks/exhaustive-deps
+    // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [selectedRowIds, selectRows]);
 
   return (
@@ -195,19 +196,30 @@ export const Table = ({
         <Thead>
           <Tr>
             {allColumns.map((column) => (
-              <Th
-                {...column.getHeaderProps(column.getSortByToggleProps())}
-              >
+              <Th {...column.getHeaderProps(column.getSortByToggleProps())}>
                 <Flex>
-                  {column.render('Header')}
-                  {column.isSorted && (
-                    column.isSortedDesc ? (
-                      <TiArrowSortedDown aria-label="sorted descending" style={{ display: 'inline' }} size="1em" />
+                  {column.render("Header")}
+                  {column.isSorted &&
+                    (column.isSortedDesc ? (
+                      <TiArrowSortedDown
+                        aria-label="sorted descending"
+                        style={{ display: "inline" }}
+                        size="1em"
+                      />
                     ) : (
-                      <TiArrowSortedUp aria-label="sorted ascending" style={{ display: 'inline' }} size="1em" />
-                    )
+                      <TiArrowSortedUp
+                        aria-label="sorted ascending"
+                        style={{ display: "inline" }}
+                        size="1em"
+                      />
+                    ))}
+                  {!column.isSorted && column.canSort && (
+                    <TiArrowUnsorted
+                      aria-label="unsorted"
+                      style={{ display: "inline" }}
+                      size="1em"
+                    />
                   )}
-                  {(!column.isSorted && column.canSort) && (<TiArrowUnsorted aria-label="unsorted" style={{ display: 'inline' }} size="1em" />)}
                 </Flex>
               </Th>
             ))}
@@ -215,9 +227,9 @@ export const Table = ({
         </Thead>
         <Tbody {...getTableBodyProps()}>
           {!data.length && !isLoading && (
-          <Tr>
-            <Td colSpan={2}>No Data found.</Td>
-          </Tr>
+            <Tr>
+              <Td colSpan={2}>No Data found.</Td>
+            </Tr>
           )}
           {page.map((row) => {
             prepareRow(row);
@@ -225,15 +237,19 @@ export const Table = ({
               <Tr
                 {...row.getRowProps()}
                 _odd={{ backgroundColor: oddColor }}
-                _hover={onRowClicked && { backgroundColor: hoverColor, cursor: 'pointer' }}
-                onClick={onRowClicked ? (e: any) => onRowClicked(row, e) : undefined}
+                _hover={
+                  onRowClicked && {
+                    backgroundColor: hoverColor,
+                    cursor: "pointer",
+                  }
+                }
+                onClick={
+                  onRowClicked ? (e: any) => onRowClicked(row, e) : undefined
+                }
               >
                 {row.cells.map((cell) => (
-                  <Td
-                    {...cell.getCellProps()}
-                    py={3}
-                  >
-                    {cell.render('Cell')}
+                  <Td {...cell.getCellProps()} py={3}>
+                    {cell.render("Cell")}
                   </Td>
                 ))}
               </Tr>
@@ -242,34 +258,32 @@ export const Table = ({
         </Tbody>
       </ChakraTable>
       {(canPreviousPage || canNextPage) && (
-      <Flex alignItems="center" justifyContent="flex-start" my={4}>
-        <IconButton
-          variant="ghost"
-          onClick={handlePrevious}
-          disabled={!canPreviousPage}
-          aria-label="Previous Page"
-          title="Previous Page"
-          icon={<MdKeyboardArrowLeft />}
-        />
-        <IconButton
-          variant="ghost"
-          onClick={handleNext}
-          disabled={!canNextPage}
-          aria-label="Next Page"
-          title="Next Page"
-          icon={<MdKeyboardArrowRight />}
-        />
-        <Text>
-          {lowerCount}
-          -
-          {upperCount}
-          {' of '}
-          {totalEntries || data.length}
-        </Text>
-      </Flex>
+        <Flex alignItems="center" justifyContent="flex-start" my={4}>
+          <IconButton
+            variant="ghost"
+            onClick={handlePrevious}
+            disabled={!canPreviousPage}
+            aria-label="Previous Page"
+            title="Previous Page"
+            icon={<MdKeyboardArrowLeft />}
+          />
+          <IconButton
+            variant="ghost"
+            onClick={handleNext}
+            disabled={!canNextPage}
+            aria-label="Next Page"
+            title="Next Page"
+            icon={<MdKeyboardArrowRight />}
+          />
+          <Text>
+            {lowerCount}-{upperCount}
+            {" of "}
+            {totalEntries || data.length}
+          </Text>
+        </Flex>
       )}
     </>
   );
 };
 
-export * from './Cells';
+export * from "./Cells";
diff --git a/airflow/www/static/js/components/Time.test.tsx b/airflow/www/static/js/components/Time.test.tsx
index 5e9f86ce13..92ab8b39af 100644
--- a/airflow/www/static/js/components/Time.test.tsx
+++ b/airflow/www/static/js/components/Time.test.tsx
@@ -19,50 +19,48 @@
 
 /* global describe, test, expect, document, CustomEvent */
 
-import React from 'react';
-import {
-  render, fireEvent, act,
-} from '@testing-library/react';
-import moment from 'moment-timezone';
+import React from "react";
+import { render, fireEvent, act } from "@testing-library/react";
+import moment from "moment-timezone";
 
-import { defaultFormatWithTZ, TimezoneEvent } from 'src/datetime_utils';
-import { Wrapper } from 'src/utils/testUtils';
+import { defaultFormatWithTZ, TimezoneEvent } from "src/datetime_utils";
+import { Wrapper } from "src/utils/testUtils";
 
-import Time from './Time';
+import Time from "./Time";
 
-describe('Test Time and TimezoneProvider', () => {
-  test('Displays a UTC time correctly', () => {
+describe("Test Time and TimezoneProvider", () => {
+  test("Displays a UTC time correctly", () => {
     const now = new Date();
-    const { getByText } = render(
-      <Time dateTime={now.toISOString()} />,
-      { wrapper: Wrapper },
-    );
+    const { getByText } = render(<Time dateTime={now.toISOString()} />, {
+      wrapper: Wrapper,
+    });
 
     const utcTime = getByText(moment.utc(now).format(defaultFormatWithTZ));
     expect(utcTime).toBeDefined();
     expect(utcTime.title).toBeFalsy();
   });
 
-  test('Displays moment default tz, includes UTC date in title', () => {
+  test("Displays moment default tz, includes UTC date in title", () => {
     const now = new Date();
-    const tz = 'US/Samoa';
+    const tz = "US/Samoa";
     moment.tz.setDefault(tz);
 
-    const { getByText } = render(
-      <Time dateTime={now.toISOString()} />,
-      { wrapper: Wrapper },
-    );
+    const { getByText } = render(<Time dateTime={now.toISOString()} />, {
+      wrapper: Wrapper,
+    });
 
     const samoaTime = getByText(moment(now).tz(tz).format(defaultFormatWithTZ));
     expect(samoaTime).toBeDefined();
-    expect(samoaTime.title).toEqual(moment.utc(now).format(defaultFormatWithTZ));
+    expect(samoaTime.title).toEqual(
+      moment.utc(now).format(defaultFormatWithTZ)
+    );
   });
 
-  test('Updates based on timezone change', async () => {
+  test("Updates based on timezone change", async () => {
     const now = new Date();
     const { getByText, queryByText } = render(
       <Time dateTime={now.toISOString()} />,
-      { wrapper: Wrapper },
+      { wrapper: Wrapper }
     );
 
     const utcTime = queryByText(moment.utc(now).format(defaultFormatWithTZ));
@@ -70,14 +68,16 @@ describe('Test Time and TimezoneProvider', () => {
 
     // Fire a custom timezone change event
     const event = new CustomEvent(TimezoneEvent, {
-      detail: 'EST',
+      detail: "EST",
     });
     await act(async () => {
       fireEvent(document, event);
     });
 
     expect(utcTime).toBeNull();
-    const estTime = getByText(moment(now).tz('EST').format(defaultFormatWithTZ));
+    const estTime = getByText(
+      moment(now).tz("EST").format(defaultFormatWithTZ)
+    );
     expect(estTime).toBeDefined();
     expect(estTime.title).toEqual(moment.utc(now).format(defaultFormatWithTZ));
   });
diff --git a/airflow/www/static/js/components/Time.tsx b/airflow/www/static/js/components/Time.tsx
index 42893e3508..c9765d7f05 100644
--- a/airflow/www/static/js/components/Time.tsx
+++ b/airflow/www/static/js/components/Time.tsx
@@ -17,11 +17,11 @@
  * under the License.
  */
 
-import React from 'react';
-import moment from 'moment-timezone';
+import React from "react";
+import moment from "moment-timezone";
 
-import { useTimezone } from 'src/context/timezone';
-import { defaultFormatWithTZ } from 'src/datetime_utils';
+import { useTimezone } from "src/context/timezone";
+import { defaultFormatWithTZ } from "src/datetime_utils";
 
 interface Props {
   dateTime?: string | null;
@@ -35,13 +35,13 @@ const Time = ({ dateTime, format = defaultFormatWithTZ }: Props) => {
   if (!dateTime || !time.isValid()) return null;
 
   const formattedTime = time.tz(timezone).format(format);
-  const utcTime = time.tz('UTC').format(defaultFormatWithTZ);
+  const utcTime = time.tz("UTC").format(defaultFormatWithTZ);
 
   return (
     <time
       dateTime={dateTime}
       // show title if date is not UTC
-      title={timezone.toUpperCase() !== 'UTC' ? utcTime : undefined}
+      title={timezone.toUpperCase() !== "UTC" ? utcTime : undefined}
     >
       {formattedTime}
     </time>
diff --git a/airflow/www/static/js/components/Tooltip.tsx b/airflow/www/static/js/components/Tooltip.tsx
index d01aec657c..fc09cefc1f 100644
--- a/airflow/www/static/js/components/Tooltip.tsx
+++ b/airflow/www/static/js/components/Tooltip.tsx
@@ -19,7 +19,7 @@
 
 /* Simplified version of chakra's Tooltip component for faster rendering but less customization */
 
-import React from 'react';
+import React from "react";
 import {
   chakra,
   forwardRef,
@@ -31,22 +31,22 @@ import {
   UseTooltipProps,
   Portal,
   PortalProps,
-} from '@chakra-ui/react';
-import { motion, AnimatePresence } from 'framer-motion';
+} from "@chakra-ui/react";
+import { motion, AnimatePresence } from "framer-motion";
 
 export interface TooltipProps
-  extends HTMLChakraProps<'div'>,
-  ThemingProps<'Tooltip'>,
-  UseTooltipProps {
+  extends HTMLChakraProps<"div">,
+    ThemingProps<"Tooltip">,
+    UseTooltipProps {
   /**
    * The React component to use as the
    * trigger for the tooltip
    */
-  children: React.ReactElement
+  children: React.ReactElement;
   /**
    * The label of the tooltip
    */
-  label?: React.ReactNode
+  label?: React.ReactNode;
   /**
    * The accessible, human friendly label to use for
    * screen readers.
@@ -54,20 +54,20 @@ export interface TooltipProps
    * If passed, tooltip will show the content `label`
    * but expose only `aria-label` to assistive technologies
    */
-  'aria-label'?: string
+  "aria-label"?: string;
   /**
    * If `true`, the tooltip will wrap its children
    * in a `<span/>` with `tabIndex=0`
    */
-  shouldWrapChildren?: boolean
+  shouldWrapChildren?: boolean;
   /**
    * If `true`, the tooltip will show an arrow tip
    */
-  hasArrow?: boolean
+  hasArrow?: boolean;
   /**
    * Props to be forwarded to the portal component
    */
-  portalProps?: Pick<PortalProps, 'appendToParentPortal' | 'containerRef'>
+  portalProps?: Pick<PortalProps, "appendToParentPortal" | "containerRef">;
 }
 
 const scale = {
@@ -75,15 +75,15 @@ const scale = {
     scale: 0.85,
     opacity: 0,
     transition: {
-      opacity: { duration: 0.15, easings: 'easeInOut' },
-      scale: { duration: 0.2, easings: 'easeInOut' },
+      opacity: { duration: 0.15, easings: "easeInOut" },
+      scale: { duration: 0.2, easings: "easeInOut" },
     },
   },
   enter: {
     scale: 1,
     opacity: 1,
     transition: {
-      opacity: { easings: 'easeOut', duration: 0.2 },
+      opacity: { easings: "easeOut", duration: 0.2 },
       scale: { duration: 0.2, ease: [0.175, 0.885, 0.4, 1.1] },
     },
   },
@@ -92,18 +92,18 @@ const scale = {
 const StyledTooltip = chakra(motion.div);
 
 const styles = {
-  '--popper-arrow-bg': ['var(--tooltip-bg)'],
-  '--tooltip-bg': 'colors.gray.700',
-  bg: ['var(--tooltip-bg)'],
-  borderRadius: 'sm',
-  boxShadow: 'md',
-  color: 'whiteAlpha.900',
-  fontSize: 'md',
-  fontWeight: 'medium',
-  maxW: '320px',
-  px: '8px',
-  py: '2px',
-  zIndex: 'tooltip',
+  "--popper-arrow-bg": ["var(--tooltip-bg)"],
+  "--tooltip-bg": "colors.gray.700",
+  bg: ["var(--tooltip-bg)"],
+  borderRadius: "sm",
+  boxShadow: "md",
+  color: "whiteAlpha.900",
+  fontSize: "md",
+  fontWeight: "medium",
+  maxW: "320px",
+  px: "8px",
+  py: "2px",
+  zIndex: "tooltip",
 };
 
 /**
@@ -112,7 +112,7 @@ const styles = {
  * @see Docs     https://chakra-ui.com/docs/overlay/tooltip
  * @see WAI-ARIA https://www.w3.org/TR/wai-aria-practices/#tooltip
  */
-const Tooltip = forwardRef<TooltipProps, 'div'>((props, ref) => {
+const Tooltip = forwardRef<TooltipProps, "div">((props, ref) => {
   const ownProps = omitThemingProps(props);
   const theme = useTheme();
 
@@ -120,7 +120,7 @@ const Tooltip = forwardRef<TooltipProps, 'div'>((props, ref) => {
     children,
     label,
     shouldWrapChildren,
-    'aria-label': ariaLabel,
+    "aria-label": ariaLabel,
     hasArrow,
     bg,
     portalProps,
@@ -133,14 +133,14 @@ const Tooltip = forwardRef<TooltipProps, 'div'>((props, ref) => {
   const tooltip = useTooltip({ ...rest, direction: theme.direction });
 
   /*
-     * Ensure tooltip has only one child node
-     */
+   * Ensure tooltip has only one child node
+   */
   const child = React.Children.only(children) as React.ReactElement & {
-    ref?: React.Ref<any>
+    ref?: React.Ref<any>;
   };
   const trigger: React.ReactElement = React.cloneElement(
     child,
-    tooltip.getTriggerProps(child.props, child.ref),
+    tooltip.getTriggerProps(child.props, child.ref)
   );
 
   const tooltipProps = tooltip.getTooltipProps({}, ref);
@@ -158,38 +158,38 @@ const Tooltip = forwardRef<TooltipProps, 'div'>((props, ref) => {
       {trigger}
       <AnimatePresence>
         {tooltip.isOpen && (
-        <Portal {...portalProps}>
-          <chakra.div
-            {...tooltip.getTooltipPositionerProps()}
-            __css={{
-              zIndex: styles.zIndex,
-              pointerEvents: 'none',
-            }}
-          >
-            <StyledTooltip
-              variants={scale}
-              {...(tooltipProps as any)}
-              initial="exit"
-              animate="enter"
-              exit="exit"
-              __css={styles}
+          <Portal {...portalProps}>
+            <chakra.div
+              {...tooltip.getTooltipPositionerProps()}
+              __css={{
+                zIndex: styles.zIndex,
+                pointerEvents: "none",
+              }}
             >
-              {label}
-              {hasArrow && (
-              <chakra.div
-                data-popper-arrow
-                className="chakra-tooltip__arrow-wrapper"
+              <StyledTooltip
+                variants={scale}
+                {...(tooltipProps as any)}
+                initial="exit"
+                animate="enter"
+                exit="exit"
+                __css={styles}
               >
-                <chakra.div
-                  data-popper-arrow-inner
-                  className="chakra-tooltip__arrow"
-                  __css={{ bg: styles.bg }}
-                />
-              </chakra.div>
-              )}
-            </StyledTooltip>
-          </chakra.div>
-        </Portal>
+                {label}
+                {hasArrow && (
+                  <chakra.div
+                    data-popper-arrow
+                    className="chakra-tooltip__arrow-wrapper"
+                  >
+                    <chakra.div
+                      data-popper-arrow-inner
+                      className="chakra-tooltip__arrow"
+                      __css={{ bg: styles.bg }}
+                    />
+                  </chakra.div>
+                )}
+              </StyledTooltip>
+            </chakra.div>
+          </Portal>
         )}
       </AnimatePresence>
     </>
diff --git a/airflow/www/static/js/connection_form.js b/airflow/www/static/js/connection_form.js
index 755bfe7614..26791e28ec 100644
--- a/airflow/www/static/js/connection_form.js
+++ b/airflow/www/static/js/connection_form.js
@@ -21,13 +21,14 @@
  */
 
 /* global document, DOMParser, $, CodeMirror */
-import { getMetaValue } from './utils';
+import { getMetaValue } from "./utils";
 
-const restApiEnabled = getMetaValue('rest_api_enabled') === 'True';
-const connectionTestUrl = getMetaValue('test_url');
+const restApiEnabled = getMetaValue("rest_api_enabled") === "True";
+const connectionTestUrl = getMetaValue("test_url");
 
 function decode(str) {
-  return new DOMParser().parseFromString(str, 'text/html').documentElement.textContent;
+  return new DOMParser().parseFromString(str, "text/html").documentElement
+    .textContent;
 }
 
 /**
@@ -36,10 +37,12 @@ function decode(str) {
 function getConnTypesToControlsMap() {
   const connTypesToControlsMap = new Map();
 
-  const extraFormControls = Array.from(document.querySelectorAll("[id^='extra__'"));
+  const extraFormControls = Array.from(
+    document.querySelectorAll("[id^='extra__'")
+  );
   extraFormControls.forEach((control) => {
-    const connTypeEnd = control.id.indexOf('__', 'extra__'.length);
-    const connType = control.id.substring('extra__'.length, connTypeEnd);
+    const connTypeEnd = control.id.indexOf("__", "extra__".length);
+    const connType = control.id.substring("extra__".length, connTypeEnd);
 
     const controls = connTypesToControlsMap.has(connType)
       ? connTypesToControlsMap.get(connType)
@@ -56,43 +59,41 @@ function getConnTypesToControlsMap() {
  * Returns the DOM element that contains the different controls.
  */
 function getControlsContainer() {
-  return document.getElementById('conn_id')
-    .parentElement
-    .parentElement
+  return document.getElementById("conn_id").parentElement.parentElement
     .parentElement;
 }
 
 /**
-   * Restores the behaviour for all fields. Used to restore fields to a
-   * well-known state during the change of connection types.
-   */
+ * Restores the behaviour for all fields. Used to restore fields to a
+ * well-known state during the change of connection types.
+ */
 function restoreFieldBehaviours() {
-  Array.from(document.querySelectorAll('label[data-orig-text]')).forEach((elem) => {
-    elem.innerText = elem.dataset.origText;
-    delete elem.dataset.origText;
-  });
+  Array.from(document.querySelectorAll("label[data-orig-text]")).forEach(
+    (elem) => {
+      elem.innerText = elem.dataset.origText;
+      delete elem.dataset.origText;
+    }
+  );
 
-  Array.from(document.querySelectorAll('.form-control')).forEach((elem) => {
-    elem.placeholder = '';
-    elem.parentElement.parentElement.classList.remove('hide');
+  Array.from(document.querySelectorAll(".form-control")).forEach((elem) => {
+    elem.placeholder = "";
+    elem.parentElement.parentElement.classList.remove("hide");
   });
 }
 
 /**
-   * Applies special behaviour for fields. The behaviour is defined through
-   * config, passed by the server.
-   *
-   * @param {string} connection The connection object to apply to.
-   */
+ * Applies special behaviour for fields. The behaviour is defined through
+ * config, passed by the server.
+ *
+ * @param {string} connection The connection object to apply to.
+ */
 function applyFieldBehaviours(connection) {
   if (connection) {
     if (Array.isArray(connection.hidden_fields)) {
       connection.hidden_fields.forEach((field) => {
-        document.getElementById(field)
-          .parentElement
-          .parentElement
-          .classList
-          .add('hide');
+        document
+          .getElementById(field)
+          .parentElement.parentElement.classList.add("hide");
       });
     }
 
@@ -121,47 +122,61 @@ function applyFieldBehaviours(connection) {
   Airflow REST API.
  */
 function handleTestConnection(connectionType, testableConnections) {
-  const testButton = document.getElementById('test-connection');
+  const testButton = document.getElementById("test-connection");
   const testConnEnabled = testableConnections.includes(connectionType);
 
   if (testConnEnabled) {
     // If connection type can be tested in via REST API, enable button and clear toolip.
-    $(testButton).prop('disabled', false).removeAttr('title');
+    $(testButton).prop("disabled", false).removeAttr("title");
   } else {
     // If connection type can NOT be tested via REST API, disable button and display toolip
     // alerting the user.
-    $(testButton).prop('disabled', true)
-      .attr('title', 'This connection type does not currently support testing via '
-        + 'Airflow REST API.');
+    $(testButton)
+      .prop("disabled", true)
+      .attr(
+        "title",
+        "This connection type does not currently support testing via " +
+          "Airflow REST API."
+      );
   }
 }
 
 $(document).ready(() => {
-  const fieldBehavioursElem = document.getElementById('field_behaviours');
+  const fieldBehavioursElem = document.getElementById("field_behaviours");
   const config = JSON.parse(decode(fieldBehavioursElem.textContent));
-  const testableConnsElem = document.getElementById('testable_connection_types');
+  const testableConnsElem = document.getElementById(
+    "testable_connection_types"
+  );
   const testableConns = decode(testableConnsElem.textContent);
 
   // Prevent login/password fields from triggering browser auth extensions
-  const form = document.getElementById('model_form');
-  if (form) form.setAttribute('autocomplete', 'off');
+  const form = document.getElementById("model_form");
+  if (form) form.setAttribute("autocomplete", "off");
 
   // Save all DOM elements into a map on load.
   const controlsContainer = getControlsContainer();
   const connTypesToControlsMap = getConnTypesToControlsMap();
 
   // Create a test connection button & insert it right next to the save (submit) button
-  const testConnBtn = $('<button id="test-connection" type="button" class="btn btn-sm btn-primary" '
-    + 'style="margin-left: 3px; pointer-events: all">Test\n <i class="fa fa-rocket"></i></button>');
+  const testConnBtn = $(
+    '<button id="test-connection" type="button" class="btn btn-sm btn-primary" ' +
+      'style="margin-left: 3px; pointer-events: all">Test\n <i class="fa fa-rocket"></i></button>'
+  );
 
   // Disable the Test Connection button if Airflow REST APIs are not enabled.
   if (!restApiEnabled) {
-    $(testConnBtn).prop('disabled', true)
-      .attr('title', 'Airflow REST APIs have been disabled. '
-        + 'See api->auth_backends section of the Airflow configuration.');
+    $(testConnBtn)
+      .prop("disabled", true)
+      .attr(
+        "title",
+        "Airflow REST APIs have been disabled. " +
+          "See api->auth_backends section of the Airflow configuration."
+      );
   }
 
-  $(testConnBtn).insertAfter($('form#model_form div.well.well-sm button:submit'));
+  $(testConnBtn).insertAfter(
+    $("form#model_form div.well.well-sm button:submit")
+  );
 
   /**
    * Changes the connection type.
@@ -196,18 +211,20 @@ $(document).ready(() => {
    * @param {string} message - The text message to show in alert box
    */
   function displayAlert(status, message) {
-    const alertClass = status ? 'alert-success' : 'alert-error';
-    let alertBox = $('.container .row .alert');
+    const alertClass = status ? "alert-success" : "alert-error";
+    let alertBox = $(".container .row .alert");
     if (alertBox.length) {
-      alertBox.removeClass('alert-success').removeClass('alert-error');
+      alertBox.removeClass("alert-success").removeClass("alert-error");
       alertBox.addClass(alertClass);
       alertBox.text(message);
       alertBox.show();
     } else {
-      alertBox = $(`<div class="alert ${alertClass}">\n`
-        + `<button type="button" class="close" data-dismiss="alert">×</button>\n${message}</div>`);
+      alertBox = $(
+        `<div class="alert ${alertClass}">\n` +
+          `<button type="button" class="close" data-dismiss="alert">×</button>\n${message}</div>`
+      );
 
-      $('.container .row').prepend(alertBox).show();
+      $(".container .row").prepend(alertBox).show();
     }
   }
 
@@ -238,22 +255,22 @@ $(document).ready(() => {
           alphabetical order
     */
     $.each(inArray, function () {
-      if (this.name === 'conn_id') {
+      if (this.name === "conn_id") {
         outObj.connection_id = this.value;
-      } else if (this.value !== '' && this.name === 'port') {
+      } else if (this.value !== "" && this.name === "port") {
         outObj[this.name] = Number(this.value);
-      } else if (this.value !== '' && this.name !== 'csrf_token') {
+      } else if (this.value !== "" && this.name !== "csrf_token") {
         // Check if there are values in the classic Extra form field. These values come in
         // stringified and need to be converted to a JSON object in case there are custom form
         // field values that also need to be included in the ``extra`` object for the output
         // payload.
-        if (this.name === 'extra') {
+        if (this.name === "extra") {
           let extra;
           try {
             extra = JSON.parse(this.value);
           } catch (e) {
             if (e instanceof SyntaxError) {
-              displayAlert(false, 'Extra field value is not valid JSON.');
+              displayAlert(false, "Extra field value is not valid JSON.");
             }
             throw e;
           }
@@ -262,12 +279,12 @@ $(document).ready(() => {
             extrasObj[key] = val;
           });
           // Check if field is a custom form field.
-        } else if (this.name.startsWith('extra__')) {
+        } else if (this.name.startsWith("extra__")) {
           // prior to Airflow 2.3 custom fields were stored in the extra dict with prefix
           // post-2.3 we allow to use with no prefix
           // here we don't know which we are configured to use, so we populate both
           extrasObj[this.name] = this.value;
-          extrasObj[this.name.replace(/extra__.+?__/, '')] = this.value;
+          extrasObj[this.name.replace(/extra__.+?__/, "")] = this.value;
         } else {
           outObj[this.name] = this.value;
         }
@@ -280,14 +297,14 @@ $(document).ready(() => {
   }
 
   // Bind click event to Test Connection button & perform an AJAX call via REST API
-  $('#test-connection').on('click', (e) => {
+  $("#test-connection").on("click", (e) => {
     e.preventDefault();
     $.ajax({
       url: connectionTestUrl,
-      type: 'post',
-      contentType: 'application/json',
-      dataType: 'json',
-      data: getSerializedFormData('form#model_form'),
+      type: "post",
+      contentType: "application/json",
+      dataType: "json",
+      data: getSerializedFormData("form#model_form"),
       success(data) {
         displayAlert(data.status, data.message);
       },
@@ -297,8 +314,8 @@ $(document).ready(() => {
     });
   });
 
-  const connTypeElem = document.getElementById('conn_type');
-  $(connTypeElem).on('change', (e) => {
+  const connTypeElem = document.getElementById("conn_type");
+  $(connTypeElem).on("change", (e) => {
     const connType = e.target.value;
     changeConnType(connType);
   });
@@ -307,10 +324,10 @@ $(document).ready(() => {
   changeConnType(connTypeElem.value);
 
   // Change conn.extra TextArea widget to CodeMirror
-  const textArea = document.getElementById('extra');
+  const textArea = document.getElementById("extra");
   const editor = CodeMirror.fromTextArea(textArea, {
-    mode: { name: 'javascript', json: true },
-    gutters: ['CodeMirror-lint-markers'],
+    mode: { name: "javascript", json: true },
+    gutters: ["CodeMirror-lint-markers"],
     lineWrapping: true,
     lint: true,
   });
diff --git a/airflow/www/static/js/context/autorefresh.tsx b/airflow/www/static/js/context/autorefresh.tsx
index d2582bef31..b1ad380cb9 100644
--- a/airflow/www/static/js/context/autorefresh.tsx
+++ b/airflow/www/static/js/context/autorefresh.tsx
@@ -20,15 +20,22 @@
 /* global localStorage, document */
 
 import React, {
-  useMemo, useContext, useState, useEffect, useCallback, PropsWithChildren,
-} from 'react';
+  useMemo,
+  useContext,
+  useState,
+  useEffect,
+  useCallback,
+  PropsWithChildren,
+} from "react";
 
-import { getMetaValue } from 'src/utils';
+import { getMetaValue } from "src/utils";
 
-const autoRefreshKey = 'disabledAutoRefresh';
+const autoRefreshKey = "disabledAutoRefresh";
 
-const initialIsPaused = getMetaValue('is_paused') === 'True';
-const isRefreshDisabled = JSON.parse(localStorage.getItem(autoRefreshKey) || 'false');
+const initialIsPaused = getMetaValue("is_paused") === "True";
+const isRefreshDisabled = JSON.parse(
+  localStorage.getItem(autoRefreshKey) || "false"
+);
 
 const AutoRefreshContext = React.createContext({
   isRefreshOn: false,
@@ -45,22 +52,19 @@ export const AutoRefreshProvider = ({ children }: PropsWithChildren) => {
 
   const [isRefreshOn, setRefresh] = useState(initialState);
 
-  const onToggle = useCallback(
-    () => setRefresh(!isRefreshOn),
-    [isRefreshOn],
-  );
+  const onToggle = useCallback(() => setRefresh(!isRefreshOn), [isRefreshOn]);
   const stopRefresh = () => setRefresh(false);
 
   const startRefresh = useCallback(
     () => isRefreshAllowed && setRefresh(true),
-    [isRefreshAllowed, setRefresh],
+    [isRefreshAllowed, setRefresh]
   );
 
   const toggleRefresh = useCallback(
     (updateStorage = false) => {
       if (updateStorage) {
         if (isRefreshOn) {
-          localStorage.setItem(autoRefreshKey, 'true');
+          localStorage.setItem(autoRefreshKey, "true");
         } else {
           localStorage.removeItem(autoRefreshKey);
         }
@@ -69,12 +73,12 @@ export const AutoRefreshProvider = ({ children }: PropsWithChildren) => {
         onToggle();
       }
     },
-    [isRefreshAllowed, isRefreshOn, onToggle],
+    [isRefreshAllowed, isRefreshOn, onToggle]
   );
 
   useEffect(() => {
     function isCustomEvent(event: Event): event is CustomEvent {
-      return 'detail' in event;
+      return "detail" in event;
     }
 
     const handleChange = (e: Event) => {
@@ -86,15 +90,22 @@ export const AutoRefreshProvider = ({ children }: PropsWithChildren) => {
       }
     };
 
-    document.addEventListener('paused', handleChange);
+    document.addEventListener("paused", handleChange);
     return () => {
-      document.removeEventListener('paused', handleChange);
+      document.removeEventListener("paused", handleChange);
     };
   });
 
-  const value = useMemo(() => ({
-    isRefreshOn, toggleRefresh, stopRefresh, startRefresh, isPaused,
-  }), [isPaused, isRefreshOn, startRefresh, toggleRefresh]);
+  const value = useMemo(
+    () => ({
+      isRefreshOn,
+      toggleRefresh,
+      stopRefresh,
+      startRefresh,
+      isPaused,
+    }),
+    [isPaused, isRefreshOn, startRefresh, toggleRefresh]
+  );
 
   return (
     <AutoRefreshContext.Provider value={value}>
diff --git a/airflow/www/static/js/context/containerRef.tsx b/airflow/www/static/js/context/containerRef.tsx
index 8689947210..563c0ec557 100644
--- a/airflow/www/static/js/context/containerRef.tsx
+++ b/airflow/www/static/js/context/containerRef.tsx
@@ -17,10 +17,12 @@
  * under the License.
  */
 
-import React, { PropsWithChildren, useContext, useRef } from 'react';
+import React, { PropsWithChildren, useContext, useRef } from "react";
 
 // eslint-disable-next-line max-len
-const ContainerRefContext = React.createContext<React.RefObject<HTMLDivElement> | undefined>(undefined);
+const ContainerRefContext = React.createContext<
+  React.RefObject<HTMLDivElement> | undefined
+>(undefined);
 
 // containerRef is necessary to render for tooltips, modals, and dialogs
 // This provider allows the containerRef to be accessed by any react component
@@ -29,9 +31,7 @@ export const ContainerRefProvider = ({ children }: PropsWithChildren) => {
 
   return (
     <ContainerRefContext.Provider value={containerRef}>
-      <div ref={containerRef}>
-        {children}
-      </div>
+      <div ref={containerRef}>{children}</div>
     </ContainerRefContext.Provider>
   );
 };
diff --git a/airflow/www/static/js/context/timezone.tsx b/airflow/www/static/js/context/timezone.tsx
index c4e329507a..85c23f497a 100644
--- a/airflow/www/static/js/context/timezone.tsx
+++ b/airflow/www/static/js/context/timezone.tsx
@@ -20,16 +20,22 @@
 /* global moment, document */
 
 import React, {
-  useContext, useEffect, useState, useMemo, PropsWithChildren,
-} from 'react';
+  useContext,
+  useEffect,
+  useState,
+  useMemo,
+  PropsWithChildren,
+} from "react";
 
-import { TimezoneEvent } from '../datetime_utils';
+import { TimezoneEvent } from "../datetime_utils";
 
-const TimezoneContext = React.createContext({ timezone: 'UTC' });
+const TimezoneContext = React.createContext({ timezone: "UTC" });
 
 export const TimezoneProvider = ({ children }: PropsWithChildren) => {
-  // @ts-ignores: defaultZone not recognize in moment.
-  const [timezone, setTimezone] = useState((moment.defaultZone && moment.defaultZone.name) || 'UTC');
+  const [timezone, setTimezone] = useState(
+    // @ts-ignores: defaultZone not recognize in moment.
+    (moment.defaultZone && moment.defaultZone.name) || "UTC"
+  );
 
   const handleChange = (e: CustomEvent<string>) => {
     if (e.detail && e.detail !== timezone) setTimezone(e.detail);
@@ -38,7 +44,10 @@ export const TimezoneProvider = ({ children }: PropsWithChildren) => {
   useEffect(() => {
     document.addEventListener(TimezoneEvent, handleChange as EventListener);
     return () => {
-      document.removeEventListener(TimezoneEvent, handleChange as EventListener);
+      document.removeEventListener(
+        TimezoneEvent,
+        handleChange as EventListener
+      );
     };
   });
 
diff --git a/airflow/www/static/js/dag.js b/airflow/www/static/js/dag.js
index e205c1d8c5..787c483719 100644
--- a/airflow/www/static/js/dag.js
+++ b/airflow/www/static/js/dag.js
@@ -19,19 +19,19 @@
 
 /* global document, window, CustomEvent, $ */
 
-import { getMetaValue } from './utils';
-import { approxTimeFromNow, formatDateTime } from './datetime_utils';
-import { openDatasetModal, getDatasetTooltipInfo } from './datasetUtils';
+import { getMetaValue } from "./utils";
+import { approxTimeFromNow, formatDateTime } from "./datetime_utils";
+import { openDatasetModal, getDatasetTooltipInfo } from "./datasetUtils";
 
-const dagId = getMetaValue('dag_id');
-const pausedUrl = getMetaValue('paused_url');
+const dagId = getMetaValue("dag_id");
+const pausedUrl = getMetaValue("paused_url");
 // eslint-disable-next-line import/prefer-default-export
-export const dagTZ = getMetaValue('dag_timezone');
-const datasetsUrl = getMetaValue('datasets_url');
+export const dagTZ = getMetaValue("dag_timezone");
+const datasetsUrl = getMetaValue("datasets_url");
 const nextRun = {
-  createAfter: getMetaValue('next_dagrun_create_after'),
-  intervalStart: getMetaValue('next_dagrun_data_interval_start'),
-  intervalEnd: getMetaValue('next_dagrun_data_interval_end'),
+  createAfter: getMetaValue("next_dagrun_create_after"),
+  intervalStart: getMetaValue("next_dagrun_data_interval_start"),
+  intervalEnd: getMetaValue("next_dagrun_data_interval_end"),
 };
 let nextDatasets = [];
 let nextDatasetsError;
@@ -42,48 +42,52 @@ const setNextDatasets = (datasets, error) => {
 };
 
 // Pills highlighting
-$(window).on('load', function onLoad() {
-  $(`a[href*="${this.location.pathname}"]`).parent().addClass('active');
-  $('.never_active').removeClass('active');
-  const run = $('#next-dataset-tooltip');
-  const singleDatasetUri = $(run).data('uri');
+$(window).on("load", function onLoad() {
+  $(`a[href*="${this.location.pathname}"]`).parent().addClass("active");
+  $(".never_active").removeClass("active");
+  const run = $("#next-dataset-tooltip");
+  const singleDatasetUri = $(run).data("uri");
   if (!singleDatasetUri) {
     getDatasetTooltipInfo(dagId, run, setNextDatasets);
   }
 });
 
-$('#pause_resume').on('change', function onChange() {
+$("#pause_resume").on("change", function onChange() {
   const $input = $(this);
-  const id = $input.data('dag-id');
-  const isPaused = $input.is(':checked');
-  const url = `${pausedUrl}?is_paused=${isPaused}&dag_id=${encodeURIComponent(id)}`;
+  const id = $input.data("dag-id");
+  const isPaused = $input.is(":checked");
+  const url = `${pausedUrl}?is_paused=${isPaused}&dag_id=${encodeURIComponent(
+    id
+  )}`;
   // Remove focus on element so the tooltip will go away
-  $input.trigger('blur');
-  $input.removeClass('switch-input--error');
+  $input.trigger("blur");
+  $input.removeClass("switch-input--error");
 
   // dispatch an event that React can listen for
-  const event = new CustomEvent('paused', { detail: isPaused });
+  const event = new CustomEvent("paused", { detail: isPaused });
   document.dispatchEvent(event);
 
   $.post(url).fail(() => {
     setTimeout(() => {
-      $input.prop('checked', !isPaused);
-      $input.addClass('switch-input--error');
+      $input.prop("checked", !isPaused);
+      $input.addClass("switch-input--error");
       event.value = !isPaused;
       document.dispatchEvent(event);
     }, 500);
   });
 });
 
-$('#next-run').on('mouseover', () => {
-  $('#next-run').attr('data-original-title', () => {
-    let newTitle = '';
+$("#next-run").on("mouseover", () => {
+  $("#next-run").attr("data-original-title", () => {
+    let newTitle = "";
     if (nextRun.createAfter) {
-      newTitle += `<strong>Run After:</strong> ${formatDateTime(nextRun.createAfter)}<br>`;
+      newTitle += `<strong>Run After:</strong> ${formatDateTime(
+        nextRun.createAfter
+      )}<br>`;
       newTitle += `Next Run: ${approxTimeFromNow(nextRun.createAfter)}<br><br>`;
     }
     if (nextRun.intervalStart && nextRun.intervalEnd) {
-      newTitle += '<strong>Data Interval</strong><br>';
+      newTitle += "<strong>Data Interval</strong><br>";
       newTitle += `Start: ${formatDateTime(nextRun.intervalStart)}<br>`;
       newTitle += `End: ${formatDateTime(nextRun.intervalEnd)}`;
     }
@@ -91,13 +95,15 @@ $('#next-run').on('mouseover', () => {
   });
 });
 
-$('.next-dataset-triggered').on('click', (e) => {
-  const run = $('#next-dataset-tooltip');
-  const summary = $(e.target).data('summary');
-  const singleDatasetUri = $(run).data('uri');
+$(".next-dataset-triggered").on("click", (e) => {
+  const run = $("#next-dataset-tooltip");
+  const summary = $(e.target).data("summary");
+  const singleDatasetUri = $(run).data("uri");
   if (!singleDatasetUri) {
     openDatasetModal(dagId, summary, nextDatasets, nextDatasetsError);
   } else {
-    window.location.href = `${datasetsUrl}?uri=${encodeURIComponent(singleDatasetUri)}`;
+    window.location.href = `${datasetsUrl}?uri=${encodeURIComponent(
+      singleDatasetUri
+    )}`;
   }
 });
diff --git a/airflow/www/static/js/dag/InstanceTooltip.test.tsx b/airflow/www/static/js/dag/InstanceTooltip.test.tsx
index 4a8f2b9192..d2a4b92d38 100644
--- a/airflow/www/static/js/dag/InstanceTooltip.test.tsx
+++ b/airflow/www/static/js/dag/InstanceTooltip.test.tsx
@@ -19,73 +19,76 @@
 
 /* global describe, test, expect */
 
-import React from 'react';
-import { render } from '@testing-library/react';
+import React from "react";
+import { render } from "@testing-library/react";
 
-import { Wrapper } from 'src/utils/testUtils';
-import type { TaskState } from 'src/types';
+import { Wrapper } from "src/utils/testUtils";
+import type { TaskState } from "src/types";
 
-import InstanceTooltip from './InstanceTooltip';
+import InstanceTooltip from "./InstanceTooltip";
 
 const instance = {
   startDate: new Date().toISOString(),
   endDate: new Date().toISOString(),
-  state: 'success' as TaskState,
-  runId: 'run',
-  taskId: 'task',
-  note: '',
+  state: "success" as TaskState,
+  runId: "run",
+  taskId: "task",
+  note: "",
 };
 
-describe('Test Task InstanceTooltip', () => {
-  test('Displays a normal task', () => {
+describe("Test Task InstanceTooltip", () => {
+  test("Displays a normal task", () => {
     const { getByText, queryByText } = render(
       <InstanceTooltip
-        group={{ id: 'task', label: 'task', instances: [] }}
+        group={{ id: "task", label: "task", instances: [] }}
         instance={instance}
       />,
-      { wrapper: Wrapper },
+      { wrapper: Wrapper }
     );
 
-    expect(getByText('Status: success')).toBeDefined();
-    expect(queryByText('Contains a note')).toBeNull();
-    expect(getByText('Duration: 00:00:00')).toBeDefined();
+    expect(getByText("Status: success")).toBeDefined();
+    expect(queryByText("Contains a note")).toBeNull();
+    expect(getByText("Duration: 00:00:00")).toBeDefined();
   });
 
-  test('Displays a mapped task with overall status', () => {
+  test("Displays a mapped task with overall status", () => {
     const { getByText } = render(
       <InstanceTooltip
         group={{
-          id: 'task', label: 'task', instances: [], isMapped: true,
+          id: "task",
+          label: "task",
+          instances: [],
+          isMapped: true,
         }}
         instance={{ ...instance, mappedStates: { success: 2 } }}
       />,
-      { wrapper: Wrapper },
+      { wrapper: Wrapper }
     );
 
-    expect(getByText('Overall Status: success')).toBeDefined();
-    expect(getByText('2 mapped tasks')).toBeDefined();
-    expect(getByText('success: 2')).toBeDefined();
+    expect(getByText("Overall Status: success")).toBeDefined();
+    expect(getByText("2 mapped tasks")).toBeDefined();
+    expect(getByText("success: 2")).toBeDefined();
   });
 
-  test('Displays a task group with overall status', () => {
+  test("Displays a task group with overall status", () => {
     const { getByText, queryByText } = render(
       <InstanceTooltip
         group={{
-          id: 'task',
-          label: 'task',
+          id: "task",
+          label: "task",
           instances: [],
           children: [
             {
-              id: 'child_task',
-              label: 'child_task',
+              id: "child_task",
+              label: "child_task",
               instances: [
                 {
-                  taskId: 'child_task',
-                  runId: 'run',
-                  state: 'success',
-                  startDate: '',
-                  endDate: '',
-                  note: '',
+                  taskId: "child_task",
+                  runId: "run",
+                  state: "success",
+                  startDate: "",
+                  endDate: "",
+                  note: "",
                 },
               ],
             },
@@ -93,36 +96,36 @@ describe('Test Task InstanceTooltip', () => {
         }}
         instance={instance}
       />,
-      { wrapper: Wrapper },
+      { wrapper: Wrapper }
     );
 
-    expect(getByText('Overall Status: success')).toBeDefined();
-    expect(queryByText('mapped task')).toBeNull();
-    expect(getByText('success: 1')).toBeDefined();
+    expect(getByText("Overall Status: success")).toBeDefined();
+    expect(queryByText("mapped task")).toBeNull();
+    expect(getByText("success: 1")).toBeDefined();
   });
 
-  test('Mentions a task with a note', () => {
+  test("Mentions a task with a note", () => {
     const { getByText } = render(
       <InstanceTooltip
-        group={{ id: 'task', label: 'task', instances: [] }}
-        instance={{ ...instance, note: 'note' }}
+        group={{ id: "task", label: "task", instances: [] }}
+        instance={{ ...instance, note: "note" }}
       />,
-      { wrapper: Wrapper },
+      { wrapper: Wrapper }
     );
 
-    expect(getByText('Contains a note')).toBeInTheDocument();
+    expect(getByText("Contains a note")).toBeInTheDocument();
   });
 
-  test('Hides duration if there is no start date', () => {
+  test("Hides duration if there is no start date", () => {
     const { queryByText, getByText } = render(
       <InstanceTooltip
-        group={{ id: 'task', label: 'task', instances: [] }}
+        group={{ id: "task", label: "task", instances: [] }}
         instance={{ ...instance, startDate: null }}
       />,
-      { wrapper: Wrapper },
+      { wrapper: Wrapper }
     );
 
-    expect(getByText('Status: success')).toBeDefined();
-    expect(queryByText('Duration: 00:00:00')).toBeNull();
+    expect(getByText("Status: success")).toBeDefined();
+    expect(queryByText("Duration: 00:00:00")).toBeNull();
   });
 });
diff --git a/airflow/www/static/js/dag/InstanceTooltip.tsx b/airflow/www/static/js/dag/InstanceTooltip.tsx
index a4a2550973..28dc9aedb7 100644
--- a/airflow/www/static/js/dag/InstanceTooltip.tsx
+++ b/airflow/www/static/js/dag/InstanceTooltip.tsx
@@ -17,13 +17,13 @@
  * under the License.
  */
 
-import React from 'react';
-import { Box, Text } from '@chakra-ui/react';
+import React from "react";
+import { Box, Text } from "@chakra-ui/react";
 
-import { finalStatesMap } from 'src/utils';
-import { formatDuration, getDuration } from 'src/datetime_utils';
-import type { TaskInstance, Task } from 'src/types';
-import Time from 'src/components/Time';
+import { finalStatesMap } from "src/utils";
+import { formatDuration, getDuration } from "src/datetime_utils";
+import type { TaskInstance, Task } from "src/types";
+import Time from "src/components/Time";
 
 interface Props {
   group: Task;
@@ -32,9 +32,7 @@ interface Props {
 
 const InstanceTooltip = ({
   group,
-  instance: {
-    startDate, endDate, state, runId, mappedStates, note,
-  },
+  instance: { startDate, endDate, state, runId, mappedStates, note },
 }: Props) => {
   if (!group) return null;
   const isGroup = !!group.children;
@@ -48,8 +46,10 @@ const InstanceTooltip = ({
     group.children.forEach((child) => {
       const taskInstance = child.instances.find((ti) => ti.runId === runId);
       if (taskInstance) {
-        const stateKey = taskInstance.state == null ? 'no_status' : taskInstance.state;
-        if (numMap.has(stateKey)) numMap.set(stateKey, (numMap.get(stateKey) || 0) + 1);
+        const stateKey =
+          taskInstance.state == null ? "no_status" : taskInstance.state;
+        if (numMap.has(stateKey))
+          numMap.set(stateKey, (numMap.get(stateKey) || 0) + 1);
       }
     });
   }
@@ -58,7 +58,7 @@ const InstanceTooltip = ({
     Object.keys(mappedStates).forEach((stateKey) => {
       const num = mappedStates[stateKey];
       numMapped += num;
-      numMap.set(stateKey || 'no_status', num);
+      numMap.set(stateKey || "no_status", num);
     });
   }
 
@@ -68,51 +68,39 @@ const InstanceTooltip = ({
         // eslint-disable-next-line react/no-array-index-key
         <Text key={val} ml="10px">
           {val}
-          {': '}
+          {": "}
           {key}
-        </Text>,
+        </Text>
       );
     }
   });
 
   return (
     <Box py="2px">
-      {group.tooltip && (
-        <Text>{group.tooltip}</Text>
-      )}
+      {group.tooltip && <Text>{group.tooltip}</Text>}
       {isMapped && numMapped > 0 && (
         <Text>
-          {numMapped}
-          {' '}
-          mapped task
-          {isGroup && ' group'}
-          {numMapped > 1 && 's'}
+          {numMapped} mapped task
+          {isGroup && " group"}
+          {numMapped > 1 && "s"}
         </Text>
       )}
       <Text>
-        {(isGroup || isMapped) ? 'Overall ' : ''}
-        Status:
-        {' '}
-        {state || 'no status'}
+        {isGroup || isMapped ? "Overall " : ""}
+        Status: {state || "no status"}
       </Text>
       {(isGroup || isMapped) && summary}
       {startDate && (
         <>
           <Text>
-            Started:
-            {' '}
-            <Time dateTime={startDate} />
+            Started: <Time dateTime={startDate} />
           </Text>
           <Text>
-            Duration:
-            {' '}
-            {formatDuration(getDuration(startDate, endDate))}
+            Duration: {formatDuration(getDuration(startDate, endDate))}
           </Text>
         </>
       )}
-      {note && (
-        <Text>Contains a note</Text>
-      )}
+      {note && <Text>Contains a note</Text>}
     </Box>
   );
 };
diff --git a/airflow/www/static/js/dag/Main.tsx b/airflow/www/static/js/dag/Main.tsx
index 6ecfe1b766..aaa20e6a0e 100644
--- a/airflow/www/static/js/dag/Main.tsx
+++ b/airflow/www/static/js/dag/Main.tsx
@@ -19,47 +19,62 @@
 
 /* global localStorage */
 
-import React, {
-  useState, useRef, useEffect, useCallback,
-} from 'react';
-import {
-  Box,
-  Flex,
-  useDisclosure,
-  Divider,
-  Spinner,
-} from '@chakra-ui/react';
-import { isEmpty, debounce } from 'lodash';
-
-import useSelection from 'src/dag/useSelection';
-import { useGridData } from 'src/api';
-import { hoverDelay } from 'src/utils';
-
-import Details from './details';
-import Grid from './grid';
-import FilterBar from './nav/FilterBar';
-import LegendRow from './nav/LegendRow';
-
-const detailsPanelKey = 'hideDetailsPanel';
-const minPanelWidth = 300;
+import React, { useState, useRef, useEffect, useCallback } from "react";
+import { Box, Flex, useDisclosure, Divider, Spinner } from "@chakra-ui/react";
+import { isEmpty, debounce } from "lodash";
+
+import useSelection from "src/dag/useSelection";
+import { useGridData } from "src/api";
+import { hoverDelay } from "src/utils";
 
-const gridWidthKey = 'grid-width';
-const saveWidth = debounce((w) => localStorage.setItem(gridWidthKey, w), hoverDelay);
+import Details from "./details";
+import Grid from "./grid";
+import FilterBar from "./nav/FilterBar";
+import LegendRow from "./nav/LegendRow";
+
+const detailsPanelKey = "hideDetailsPanel";
+const minPanelWidth = 300;
 
-const footerHeight = parseInt(getComputedStyle(document.getElementsByTagName('body')[0]).paddingBottom.replace('px', ''), 10) || 0;
-const headerHeight = parseInt(getComputedStyle(document.getElementsByTagName('body')[0]).paddingTop.replace('px', ''), 10) || 0;
+const gridWidthKey = "grid-width";
+const saveWidth = debounce(
+  (w) => localStorage.setItem(gridWidthKey, w),
+  hoverDelay
+);
+
+const footerHeight =
+  parseInt(
+    getComputedStyle(
+      document.getElementsByTagName("body")[0]
+    ).paddingBottom.replace("px", ""),
+    10
+  ) || 0;
+const headerHeight =
+  parseInt(
+    getComputedStyle(
+      document.getElementsByTagName("body")[0]
+    ).paddingTop.replace("px", ""),
+    10
+  ) || 0;
 
 const Main = () => {
-  const { data: { groups }, isLoading } = useGridData();
+  const {
+    data: { groups },
+    isLoading,
+  } = useGridData();
   const resizeRef = useRef<HTMLDivElement>(null);
   const gridRef = useRef<HTMLDivElement>(null);
-  const isPanelOpen = localStorage.getItem(detailsPanelKey) !== 'true';
+  const isPanelOpen = localStorage.getItem(detailsPanelKey) !== "true";
   const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: isPanelOpen });
   const { clearSelection } = useSelection();
-  const [hoveredTaskState, setHoveredTaskState] = useState<string | null | undefined>();
+  const [hoveredTaskState, setHoveredTaskState] = useState<
+    string | null | undefined
+  >();
 
   // Add a debounced delay to not constantly trigger highlighting certain task states
-  const onStatusHover = debounce((state) => setHoveredTaskState(state), hoverDelay);
+  const onStatusHover = debounce(
+    (state) => setHoveredTaskState(state),
+    hoverDelay
+  );
 
   const onStatusLeave = () => {
     setHoveredTaskState(undefined);
@@ -70,38 +85,45 @@ const Main = () => {
 
   const onPanelToggle = () => {
     if (!isOpen) {
-      localStorage.setItem(detailsPanelKey, 'false');
+      localStorage.setItem(detailsPanelKey, "false");
     } else {
       clearSelection();
-      localStorage.setItem(detailsPanelKey, 'true');
+      localStorage.setItem(detailsPanelKey, "true");
     }
     onToggle();
   };
 
-  const resize = useCallback((e: MouseEvent) => {
-    const gridEl = gridRef.current;
-    if (gridEl && e.x > minPanelWidth && e.x < window.innerWidth - minPanelWidth) {
-      const width = `${e.x}px`;
-      gridEl.style.width = width;
-      saveWidth(width);
-    }
-  }, [gridRef]);
+  const resize = useCallback(
+    (e: MouseEvent) => {
+      const gridEl = gridRef.current;
+      if (
+        gridEl &&
+        e.x > minPanelWidth &&
+        e.x < window.innerWidth - minPanelWidth
+      ) {
+        const width = `${e.x}px`;
+        gridEl.style.width = width;
+        saveWidth(width);
+      }
+    },
+    [gridRef]
+  );
 
   useEffect(() => {
     const resizeEl = resizeRef.current;
     if (resizeEl) {
-      resizeEl.addEventListener('mousedown', (e) => {
+      resizeEl.addEventListener("mousedown", (e) => {
         e.preventDefault();
-        document.addEventListener('mousemove', resize);
+        document.addEventListener("mousemove", resize);
       });
 
-      document.addEventListener('mouseup', () => {
-        document.removeEventListener('mousemove', resize);
+      document.addEventListener("mouseup", () => {
+        document.removeEventListener("mousemove", resize);
       });
 
       return () => {
-        resizeEl?.removeEventListener('mousedown', resize);
-        document.removeEventListener('mouseup', resize);
+        resizeEl?.removeEventListener("mousedown", resize);
+        document.removeEventListener("mouseup", resize);
       };
     }
     return () => {};
@@ -120,45 +142,45 @@ const Main = () => {
       <LegendRow onStatusHover={onStatusHover} onStatusLeave={onStatusLeave} />
       <Divider mb={5} borderBottomWidth={2} />
       <Flex height="100%">
-        {isLoading || isEmpty(groups)
-          ? (<Spinner />)
-          : (
-            <>
-              <Box
-                minWidth={minPanelWidth}
-                flex={isOpen ? undefined : 1}
-                ref={gridRef}
-                height="100%"
-                width={gridWidth}
-              >
-                <Grid
-                  isPanelOpen={isOpen}
-                  onPanelToggle={onPanelToggle}
-                  hoveredTaskState={hoveredTaskState}
+        {isLoading || isEmpty(groups) ? (
+          <Spinner />
+        ) : (
+          <>
+            <Box
+              minWidth={minPanelWidth}
+              flex={isOpen ? undefined : 1}
+              ref={gridRef}
+              height="100%"
+              width={gridWidth}
+            >
+              <Grid
+                isPanelOpen={isOpen}
+                onPanelToggle={onPanelToggle}
+                hoveredTaskState={hoveredTaskState}
+              />
+            </Box>
+            {isOpen && (
+              <>
+                <Box
+                  width={2}
+                  cursor="ew-resize"
+                  bg="gray.200"
+                  ref={resizeRef}
+                  zIndex={1}
                 />
-              </Box>
-              {isOpen && (
-                <>
-                  <Box
-                    width={2}
-                    cursor="ew-resize"
-                    bg="gray.200"
-                    ref={resizeRef}
-                    zIndex={1}
-                  />
-                  <Box
-                    flex={1}
-                    minWidth={minPanelWidth}
-                    zIndex={1}
-                    bg="white"
-                    height="100%"
-                  >
-                    <Details />
-                  </Box>
-                </>
-              )}
-            </>
-          )}
+                <Box
+                  flex={1}
+                  minWidth={minPanelWidth}
+                  zIndex={1}
+                  bg="white"
+                  height="100%"
+                >
+                  <Details />
+                </Box>
+              </>
+            )}
+          </>
+        )}
       </Flex>
     </Box>
   );
diff --git a/airflow/www/static/js/dag/StatusBox.tsx b/airflow/www/static/js/dag/StatusBox.tsx
index 3c1fcca089..80bce4b739 100644
--- a/airflow/www/static/js/dag/StatusBox.tsx
+++ b/airflow/www/static/js/dag/StatusBox.tsx
@@ -17,21 +17,17 @@
  * under the License.
  */
 
-import React from 'react';
-import { isEqual } from 'lodash';
-import {
-  Box,
-  useTheme,
-  BoxProps,
-} from '@chakra-ui/react';
+import React from "react";
+import { isEqual } from "lodash";
+import { Box, useTheme, BoxProps } from "@chakra-ui/react";
 
-import { useContainerRef } from 'src/context/containerRef';
-import type { Task, TaskInstance, TaskState } from 'src/types';
-import type { SelectionProps } from 'src/dag/useSelection';
-import { getStatusBackgroundColor, hoverDelay } from 'src/utils';
-import Tooltip from 'src/components/Tooltip';
+import { useContainerRef } from "src/context/containerRef";
+import type { Task, TaskInstance, TaskState } from "src/types";
+import type { SelectionProps } from "src/dag/useSelection";
+import { getStatusBackgroundColor, hoverDelay } from "src/utils";
+import Tooltip from "src/components/Tooltip";
 
-import InstanceTooltip from './InstanceTooltip';
+import InstanceTooltip from "./InstanceTooltip";
 
 export const boxSize = 10;
 export const boxSizePx = `${boxSize}px`;
@@ -41,8 +37,12 @@ interface StatusWithNotesProps extends BoxProps {
   containsNotes?: boolean;
 }
 
-export const StatusWithNotes = ({ state, containsNotes, ...rest }: StatusWithNotesProps) => {
-  const color = state && stateColors[state] ? stateColors[state] : 'white';
+export const StatusWithNotes = ({
+  state,
+  containsNotes,
+  ...rest
+}: StatusWithNotesProps) => {
+  const color = state && stateColors[state] ? stateColors[state] : "white";
   return (
     <Box
       width={boxSizePx}
@@ -62,7 +62,7 @@ export const SimpleStatus = ({ state, ...rest }: SimpleStatusProps) => (
   <Box
     width={boxSizePx}
     height={boxSizePx}
-    background={state && stateColors[state] ? stateColors[state] : 'white'}
+    background={state && stateColors[state] ? stateColors[state] : "white"}
     borderRadius="2px"
     borderWidth={state ? 0 : 1}
     {...rest}
@@ -78,7 +78,11 @@ interface Props {
 }
 
 const StatusBox = ({
-  group, instance, onSelect, isActive, containsNotes = false,
+  group,
+  instance,
+  onSelect,
+  isActive,
+  containsNotes = false,
 }: Props) => {
   const containerRef = useContainerRef();
   const { runId, taskId } = instance;
@@ -88,17 +92,26 @@ const StatusBox = ({
   // Fetch the corresponding column element and set its background color when hovering
   const onMouseEnter = () => {
     if (containerRef && containerRef.current) {
-      ([...containerRef.current.getElementsByClassName(`js-${runId}`)] as HTMLElement[])
-        .forEach((e) => {
-          // Don't apply hover if it is already selected
-          if (e.getAttribute('data-selected') === 'false') e.style.backgroundColor = hoverBlue;
-        });
+      (
+        [
+          ...containerRef.current.getElementsByClassName(`js-${runId}`),
+        ] as HTMLElement[]
+      ).forEach((e) => {
+        // Don't apply hover if it is already selected
+        if (e.getAttribute("data-selected") === "false")
+          e.style.backgroundColor = hoverBlue;
+      });
     }
   };
   const onMouseLeave = () => {
     if (containerRef && containerRef.current) {
-      ([...containerRef.current.getElementsByClassName(`js-${runId}`)] as HTMLElement[])
-        .forEach((e) => { e.style.backgroundColor = ''; });
+      (
+        [
+          ...containerRef.current.getElementsByClassName(`js-${runId}`),
+        ] as HTMLElement[]
+      ).forEach((e) => {
+        e.style.backgroundColor = "";
+      });
     }
   };
 
@@ -134,13 +147,9 @@ const StatusBox = ({
 
 // The default equality function is a shallow comparison and json objects will return false
 // This custom compare function allows us to do a deeper comparison
-const compareProps = (
-  prevProps: Props,
-  nextProps: Props,
-) => (
-  isEqual(prevProps.group, nextProps.group)
-  && isEqual(prevProps.instance, nextProps.instance)
-  && isEqual(prevProps.isActive, nextProps.isActive)
-);
+const compareProps = (prevProps: Props, nextProps: Props) =>
+  isEqual(prevProps.group, nextProps.group) &&
+  isEqual(prevProps.instance, nextProps.instance) &&
+  isEqual(prevProps.isActive, nextProps.isActive);
 
 export default React.memo(StatusBox, compareProps);
diff --git a/airflow/www/static/js/dag/details/BreadcrumbText.tsx b/airflow/www/static/js/dag/details/BreadcrumbText.tsx
index b0cf19161a..b9ecdbeb20 100644
--- a/airflow/www/static/js/dag/details/BreadcrumbText.tsx
+++ b/airflow/www/static/js/dag/details/BreadcrumbText.tsx
@@ -17,11 +17,8 @@
  * under the License.
  */
 
-import React from 'react';
-import {
-  Box,
-  Heading,
-} from '@chakra-ui/react';
+import React from "react";
+import { Box, Heading } from "@chakra-ui/react";
 
 interface Props {
   label: string;
@@ -40,7 +37,9 @@ const BreadcrumbText = ({ label, value }: Props) => (
     >
       {label}
     </Heading>
-    <Heading as="h3" size="md">{value}</Heading>
+    <Heading as="h3" size="md">
+      {value}
+    </Heading>
   </Box>
 );
 
diff --git a/airflow/www/static/js/dag/details/Dag.tsx b/airflow/www/static/js/dag/details/Dag.tsx
index f5b57042bf..45fad8231d 100644
--- a/airflow/www/static/js/dag/details/Dag.tsx
+++ b/airflow/www/static/js/dag/details/Dag.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import React, { ReactNode, useRef } from 'react';
+import React, { ReactNode, useRef } from "react";
 import {
   Table,
   Tbody,
@@ -29,23 +29,28 @@ import {
   Heading,
   Text,
   Box,
-} from '@chakra-ui/react';
-import { mean } from 'lodash';
+} from "@chakra-ui/react";
+import { mean } from "lodash";
 
-import { getDuration, formatDuration } from 'src/datetime_utils';
+import { getDuration, formatDuration } from "src/datetime_utils";
 import {
-  finalStatesMap, getMetaValue, getTaskSummary, useOffsetTop,
-} from 'src/utils';
-import { useGridData } from 'src/api';
-import Time from 'src/components/Time';
-import type { TaskState } from 'src/types';
+  finalStatesMap,
+  getMetaValue,
+  getTaskSummary,
+  useOffsetTop,
+} from "src/utils";
+import { useGridData } from "src/api";
+import Time from "src/components/Time";
+import type { TaskState } from "src/types";
 
-import { SimpleStatus } from '../StatusBox';
+import { SimpleStatus } from "../StatusBox";
 
-const dagDetailsUrl = getMetaValue('dag_details_url');
+const dagDetailsUrl = getMetaValue("dag_details_url");
 
 const Dag = () => {
-  const { data: { dagRuns, groups } } = useGridData();
+  const {
+    data: { dagRuns, groups },
+  } = useGridData();
   const detailsRef = useRef<HTMLDivElement>(null);
   const offsetTop = useOffsetTop(detailsRef);
 
@@ -54,8 +59,9 @@ const Dag = () => {
   const durations: number[] = [];
   dagRuns.forEach((dagRun) => {
     durations.push(getDuration(dagRun.startDate, dagRun.endDate));
-    const stateKey = dagRun.state == null ? 'no_status' : dagRun.state;
-    if (numMap.has(stateKey)) numMap.set(stateKey, (numMap.get(stateKey) || 0) + 1);
+    const stateKey = dagRun.state == null ? "no_status" : dagRun.state;
+    if (numMap.has(stateKey))
+      numMap.set(stateKey, (numMap.get(stateKey) || 0) + 1);
   });
 
   const stateSummary: ReactNode[] = [];
@@ -67,17 +73,11 @@ const Dag = () => {
           <Td>
             <Flex alignItems="center">
               <SimpleStatus state={val as TaskState} mr={2} />
-              <Text>
-                Total
-                {' '}
-                {val}
-              </Text>
+              <Text>Total {val}</Text>
             </Flex>
           </Td>
-          <Td>
-            {key}
-          </Td>
-        </Tr>,
+          <Td>{key}</Td>
+        </Tr>
       );
     }
   });
@@ -103,53 +103,47 @@ const Dag = () => {
         <Table variant="striped">
           <Tbody>
             {durations.length > 0 && (
-            <>
-              <Tr borderBottomWidth={2} borderBottomColor="gray.300">
-                <Td><Heading size="sm">DAG Runs Summary</Heading></Td>
-                <Td />
-              </Tr>
-              <Tr>
-                <Td>Total Runs Displayed</Td>
-                <Td>
-                  {durations.length}
-                </Td>
-              </Tr>
-              {stateSummary}
-              {firstStart && (
-                <Tr>
-                  <Td>First Run Start</Td>
+              <>
+                <Tr borderBottomWidth={2} borderBottomColor="gray.300">
                   <Td>
-                    <Time dateTime={firstStart} />
+                    <Heading size="sm">DAG Runs Summary</Heading>
                   </Td>
+                  <Td />
                 </Tr>
-              )}
-              {lastStart && (
                 <Tr>
-                  <Td>Last Run Start</Td>
-                  <Td>
-                    <Time dateTime={lastStart} />
-                  </Td>
+                  <Td>Total Runs Displayed</Td>
+                  <Td>{durations.length}</Td>
                 </Tr>
-              )}
-              <Tr>
-                <Td>Max Run Duration</Td>
-                <Td>
-                  {formatDuration(max)}
-                </Td>
-              </Tr>
-              <Tr>
-                <Td>Mean Run Duration</Td>
-                <Td>
-                  {formatDuration(avg)}
-                </Td>
-              </Tr>
-              <Tr>
-                <Td>Min Run Duration</Td>
-                <Td>
-                  {formatDuration(min)}
-                </Td>
-              </Tr>
-            </>
+                {stateSummary}
+                {firstStart && (
+                  <Tr>
+                    <Td>First Run Start</Td>
+                    <Td>
+                      <Time dateTime={firstStart} />
+                    </Td>
+                  </Tr>
+                )}
+                {lastStart && (
+                  <Tr>
+                    <Td>Last Run Start</Td>
+                    <Td>
+                      <Time dateTime={lastStart} />
+                    </Td>
+                  </Tr>
+                )}
+                <Tr>
+                  <Td>Max Run Duration</Td>
+                  <Td>{formatDuration(max)}</Td>
+                </Tr>
+                <Tr>
+                  <Td>Mean Run Duration</Td>
+                  <Td>{formatDuration(avg)}</Td>
+                </Tr>
+                <Tr>
+                  <Td>Min Run Duration</Td>
+                  <Td>{formatDuration(min)}</Td>
+                </Tr>
+              </>
             )}
             <Tr borderBottomWidth={2} borderBottomColor="gray.300">
               <Td>
@@ -162,16 +156,16 @@ const Dag = () => {
               <Td>{taskSummary.taskCount}</Td>
             </Tr>
             {!!taskSummary.groupCount && (
-            <Tr>
-              <Td>Total Task Groups</Td>
-              <Td>{taskSummary.groupCount}</Td>
-            </Tr>
+              <Tr>
+                <Td>Total Task Groups</Td>
+                <Td>{taskSummary.groupCount}</Td>
+              </Tr>
             )}
             {Object.entries(taskSummary.operators).map(([key, value]) => (
               <Tr key={key}>
                 <Td>
                   {key}
-                  {value > 1 && 's'}
+                  {value > 1 && "s"}
                 </Td>
                 <Td>{value}</Td>
               </Tr>
diff --git a/airflow/www/static/js/dag/details/Header.tsx b/airflow/www/static/js/dag/details/Header.tsx
index d7ddb98564..a0593c21ca 100644
--- a/airflow/www/static/js/dag/details/Header.tsx
+++ b/airflow/www/static/js/dag/details/Header.tsx
@@ -17,28 +17,34 @@
  * under the License.
  */
 
-import React, { useEffect } from 'react';
+import React, { useEffect } from "react";
 import {
   Breadcrumb,
   BreadcrumbItem,
   BreadcrumbLink,
   Text,
-} from '@chakra-ui/react';
+} from "@chakra-ui/react";
 
-import { getDagRunLabel, getMetaValue, getTask } from 'src/utils';
-import useSelection from 'src/dag/useSelection';
-import Time from 'src/components/Time';
-import { useGridData } from 'src/api';
-import RunTypeIcon from 'src/components/RunTypeIcon';
+import { getDagRunLabel, getMetaValue, getTask } from "src/utils";
+import useSelection from "src/dag/useSelection";
+import Time from "src/components/Time";
+import { useGridData } from "src/api";
+import RunTypeIcon from "src/components/RunTypeIcon";
 
-import BreadcrumbText from './BreadcrumbText';
+import BreadcrumbText from "./BreadcrumbText";
 
-const dagId = getMetaValue('dag_id');
+const dagId = getMetaValue("dag_id");
 
 const Header = () => {
-  const { data: { dagRuns, groups, ordering } } = useGridData();
+  const {
+    data: { dagRuns, groups, ordering },
+  } = useGridData();
 
-  const { selected: { taskId, runId, mapIndex }, onSelect, clearSelection } = useSelection();
+  const {
+    selected: { taskId, runId, mapIndex },
+    onSelect,
+    clearSelection,
+  } = useSelection();
   const dagRun = dagRuns.find((r) => r.runId === runId);
 
   // clearSelection if the current selected dagRun is
@@ -52,14 +58,15 @@ const Header = () => {
   let runLabel;
   if (dagRun && runId) {
     // If a runId includes the runtype then parse the time, otherwise use the custom run id
-    const runName = (
-      runId.includes('manual__')
-      || runId.includes('scheduled__')
-      || runId.includes('backfill__')
-      || runId.includes('dataset_triggered__')
-    )
-      ? <Time dateTime={getDagRunLabel({ dagRun, ordering })} />
-      : runId;
+    const runName =
+      runId.includes("manual__") ||
+      runId.includes("scheduled__") ||
+      runId.includes("backfill__") ||
+      runId.includes("dataset_triggered__") ? (
+        <Time dateTime={getDagRunLabel({ dagRun, ordering })} />
+      ) : (
+        runId
+      );
     runLabel = (
       <>
         <RunTypeIcon runType={dagRun.runType} />
@@ -70,8 +77,9 @@ const Header = () => {
 
   const group = getTask({ taskId, task: groups });
 
-  const lastIndex = taskId ? taskId.lastIndexOf('.') : null;
-  const taskName = taskId && lastIndex ? taskId.substring(lastIndex + 1) : taskId;
+  const lastIndex = taskId ? taskId.lastIndexOf(".") : null;
+  const taskName =
+    taskId && lastIndex ? taskId.substring(lastIndex + 1) : taskId;
 
   const isDagDetails = !runId && !taskId;
   const isRunDetails = !!(runId && !taskId);
@@ -81,27 +89,41 @@ const Header = () => {
   return (
     <Breadcrumb separator={<Text color="gray.300">/</Text>}>
       <BreadcrumbItem isCurrentPage={isDagDetails} mt={4}>
-        <BreadcrumbLink onClick={clearSelection} _hover={isDagDetails ? { cursor: 'default' } : undefined}>
+        <BreadcrumbLink
+          onClick={clearSelection}
+          _hover={isDagDetails ? { cursor: "default" } : undefined}
+        >
           <BreadcrumbText label="DAG" value={dagId} />
         </BreadcrumbLink>
       </BreadcrumbItem>
       {runId && (
         <BreadcrumbItem isCurrentPage={isRunDetails} mt={4}>
-          <BreadcrumbLink onClick={() => onSelect({ runId })} _hover={isRunDetails ? { cursor: 'default' } : undefined}>
+          <BreadcrumbLink
+            onClick={() => onSelect({ runId })}
+            _hover={isRunDetails ? { cursor: "default" } : undefined}
+          >
             <BreadcrumbText label="Run" value={runLabel} />
           </BreadcrumbLink>
         </BreadcrumbItem>
       )}
       {taskId && (
         <BreadcrumbItem isCurrentPage mt={4}>
-          <BreadcrumbLink onClick={() => onSelect({ runId, taskId })} _hover={isTaskDetails ? { cursor: 'default' } : undefined}>
-            <BreadcrumbText label="Task" value={`${taskName}${group?.isMapped ? ' []' : ''}`} />
+          <BreadcrumbLink
+            onClick={() => onSelect({ runId, taskId })}
+            _hover={isTaskDetails ? { cursor: "default" } : undefined}
+          >
+            <BreadcrumbText
+              label="Task"
+              value={`${taskName}${group?.isMapped ? " []" : ""}`}
+            />
           </BreadcrumbLink>
         </BreadcrumbItem>
       )}
       {mapIndex !== null && (
         <BreadcrumbItem isCurrentPage mt={4}>
-          <BreadcrumbLink _hover={isMappedTaskDetails ? { cursor: 'default' } : undefined}>
+          <BreadcrumbLink
+            _hover={isMappedTaskDetails ? { cursor: "default" } : undefined}
+          >
             <BreadcrumbText label="Map Index" value={mapIndex} />
           </BreadcrumbLink>
         </BreadcrumbItem>
diff --git a/airflow/www/static/js/dag/details/NotesAccordion.test.tsx b/airflow/www/static/js/dag/details/NotesAccordion.test.tsx
index fb8f104513..a70b73446b 100644
--- a/airflow/www/static/js/dag/details/NotesAccordion.test.tsx
+++ b/airflow/www/static/js/dag/details/NotesAccordion.test.tsx
@@ -19,15 +19,15 @@
 
 /* global describe, test, expect */
 
-import React from 'react';
-import { render, fireEvent, waitFor } from '@testing-library/react';
+import React from "react";
+import { render, fireEvent, waitFor } from "@testing-library/react";
 
-import * as utils from 'src/utils';
-import { Wrapper } from 'src/utils/testUtils';
+import * as utils from "src/utils";
+import { Wrapper } from "src/utils/testUtils";
 
-import NotesAccordion from './NotesAccordion';
+import NotesAccordion from "./NotesAccordion";
 
-describe('Test DagRun / Task Instance Notes', () => {
+describe("Test DagRun / Task Instance Notes", () => {
   window.scrollTo = jest.fn();
 
   afterEach(() => {
@@ -38,91 +38,83 @@ describe('Test DagRun / Task Instance Notes', () => {
     jest.clearAllMocks();
   });
 
-  test('No initial value, accordion is also open', async () => {
-    jest.spyOn(utils, 'getMetaValue').mockImplementation(
-      (meta) => {
-        if (meta === 'can_edit') return 'True';
-        return '';
-      },
-    );
+  test("No initial value, accordion is also open", async () => {
+    jest.spyOn(utils, "getMetaValue").mockImplementation((meta) => {
+      if (meta === "can_edit") return "True";
+      return "";
+    });
 
     const { getByText } = render(
       <NotesAccordion dagId="dagId" runId="runId" />,
-      { wrapper: Wrapper },
+      { wrapper: Wrapper }
     );
 
-    await waitFor(() => expect(getByText('Add Note')).toBeVisible());
+    await waitFor(() => expect(getByText("Add Note")).toBeVisible());
   });
 
-  test('With initial value, accordion is open. And update button changed', () => {
-    jest.spyOn(utils, 'getMetaValue').mockImplementation(
-      (meta) => {
-        if (meta === 'can_edit') return 'True';
-        return '';
-      },
-    );
+  test("With initial value, accordion is open. And update button changed", () => {
+    jest.spyOn(utils, "getMetaValue").mockImplementation((meta) => {
+      if (meta === "can_edit") return "True";
+      return "";
+    });
 
     const { queryByText, getByText } = render(
       <NotesAccordion dagId="dagId" runId="runId" initialValue="I am a note" />,
-      { wrapper: Wrapper },
+      { wrapper: Wrapper }
     );
 
-    const changeButton = getByText('Edit Note');
+    const changeButton = getByText("Edit Note");
 
     expect(changeButton).toBeInTheDocument();
-    expect(queryByText('Add Note')).toBe(null);
+    expect(queryByText("Add Note")).toBe(null);
 
     fireEvent.click(changeButton);
 
-    expect(getByText('Save Note')).toBeInTheDocument();
-    expect(getByText('Cancel')).toBeInTheDocument();
+    expect(getByText("Save Note")).toBeInTheDocument();
+    expect(getByText("Cancel")).toBeInTheDocument();
   });
 
-  test('Cannot Edit Note without edit permissions', () => {
-    jest.spyOn(utils, 'getMetaValue').mockImplementation(
-      (meta) => {
-        if (meta === 'can_edit') return 'False';
-        return '';
-      },
-    );
+  test("Cannot Edit Note without edit permissions", () => {
+    jest.spyOn(utils, "getMetaValue").mockImplementation((meta) => {
+      if (meta === "can_edit") return "False";
+      return "";
+    });
 
     const { getByText } = render(
       <NotesAccordion dagId="dagId" runId="runId" initialValue="I am a note" />,
-      { wrapper: Wrapper },
+      { wrapper: Wrapper }
     );
 
-    const changeButton = getByText('Edit Note');
+    const changeButton = getByText("Edit Note");
 
     expect(changeButton).toBeInTheDocument();
     expect(changeButton).toBeDisabled();
   });
 
-  test('Making changes and then discarding will go reset to the original notes.', () => {
-    jest.spyOn(utils, 'getMetaValue').mockImplementation(
-      (meta) => {
-        if (meta === 'can_edit') return 'True';
-        return '';
-      },
-    );
+  test("Making changes and then discarding will go reset to the original notes.", () => {
+    jest.spyOn(utils, "getMetaValue").mockImplementation((meta) => {
+      if (meta === "can_edit") return "True";
+      return "";
+    });
 
     const { getByTestId, getByText, queryByText } = render(
       <NotesAccordion dagId="dagId" runId="runId" initialValue="I am a note" />,
-      { wrapper: Wrapper },
+      { wrapper: Wrapper }
     );
 
-    const changeButton = getByText('Edit Note');
+    const changeButton = getByText("Edit Note");
 
     fireEvent.click(changeButton);
 
-    expect(getByText('Save Note')).toBeInTheDocument();
-    const textarea = getByTestId('notes-input');
+    expect(getByText("Save Note")).toBeInTheDocument();
+    const textarea = getByTestId("notes-input");
 
-    fireEvent.change(textarea, { target: { value: 'A different note.' } });
+    fireEvent.change(textarea, { target: { value: "A different note." } });
 
-    expect(queryByText('I am a note')).toBe(null);
+    expect(queryByText("I am a note")).toBe(null);
 
-    fireEvent.click(getByText('Cancel'));
+    fireEvent.click(getByText("Cancel"));
 
-    expect(getByText('I am a note')).toBeInTheDocument();
+    expect(getByText("I am a note")).toBeInTheDocument();
   });
 });
diff --git a/airflow/www/static/js/dag/details/NotesAccordion.tsx b/airflow/www/static/js/dag/details/NotesAccordion.tsx
index bb5edd6f09..2426663f95 100644
--- a/airflow/www/static/js/dag/details/NotesAccordion.tsx
+++ b/airflow/www/static/js/dag/details/NotesAccordion.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import React, { useState } from 'react';
+import React, { useState } from "react";
 import {
   Accordion,
   AccordionItem,
@@ -30,13 +30,13 @@ import {
   Text,
   Textarea,
   Divider,
-} from '@chakra-ui/react';
-import ResizeTextarea from 'react-textarea-autosize';
+} from "@chakra-ui/react";
+import ResizeTextarea from "react-textarea-autosize";
 
-import { getMetaValue } from 'src/utils';
-import { useSetDagRunNote, useSetTaskInstanceNote } from 'src/api';
-import { MdEdit } from 'react-icons/md';
-import ReactMarkdown from 'src/components/ReactMarkdown';
+import { getMetaValue } from "src/utils";
+import { useSetDagRunNote, useSetTaskInstanceNote } from "src/api";
+import { MdEdit } from "react-icons/md";
+import ReactMarkdown from "src/components/ReactMarkdown";
 
 interface Props {
   dagId: string;
@@ -47,26 +47,28 @@ interface Props {
 }
 
 const NotesAccordion = ({
-  dagId, runId, taskId, mapIndex, initialValue,
+  dagId,
+  runId,
+  taskId,
+  mapIndex,
+  initialValue,
 }: Props) => {
-  const canEdit = getMetaValue('can_edit') === 'True';
-  const [note, setNote] = useState(initialValue ?? '');
+  const canEdit = getMetaValue("can_edit") === "True";
+  const [note, setNote] = useState(initialValue ?? "");
   const [editMode, setEditMode] = useState(false);
 
-  const {
-    mutateAsync: apiCallToSetDagRunNote, isLoading: dagRunIsLoading,
-  } = useSetDagRunNote({ dagId, runId });
-  const {
-    mutateAsync: apiCallToSetTINote, isLoading: tiIsLoading,
-  } = useSetTaskInstanceNote({
-    dagId,
-    runId,
-    taskId: taskId ?? '',
-    mapIndex,
-  });
+  const { mutateAsync: apiCallToSetDagRunNote, isLoading: dagRunIsLoading } =
+    useSetDagRunNote({ dagId, runId });
+  const { mutateAsync: apiCallToSetTINote, isLoading: tiIsLoading } =
+    useSetTaskInstanceNote({
+      dagId,
+      runId,
+      taskId: taskId ?? "",
+      mapIndex,
+    });
   const isLoading = dagRunIsLoading || tiIsLoading;
 
-  const objectIdentifier = (taskId == null) ? 'DAG Run' : 'Task Instance';
+  const objectIdentifier = taskId == null ? "DAG Run" : "Task Instance";
 
   const handleSubmit = async (e: React.FormEvent) => {
     e.preventDefault();
@@ -85,9 +87,7 @@ const NotesAccordion = ({
           <AccordionButton p={0} pb={2} fontSize="inherit">
             <Box flex="1" textAlign="left">
               <Text as="strong" size="lg">
-                {objectIdentifier}
-                {' '}
-                Notes:
+                {objectIdentifier} Notes:
               </Text>
             </Box>
             <AccordionIcon />
@@ -96,7 +96,6 @@ const NotesAccordion = ({
             {editMode ? (
               <form onSubmit={handleSubmit}>
                 <Box>
-
                   <Textarea
                     autoFocus
                     minH="unset"
@@ -112,11 +111,18 @@ const NotesAccordion = ({
                   />
                 </Box>
                 <Flex mt={3} justify="right">
-                  <Button type="submit" isLoading={isLoading} colorScheme="blue">
+                  <Button
+                    type="submit"
+                    isLoading={isLoading}
+                    colorScheme="blue"
+                  >
                     Save Note
                   </Button>
                   <Button
-                    onClick={() => { setNote(initialValue ?? ''); setEditMode(false); }}
+                    onClick={() => {
+                      setNote(initialValue ?? "");
+                      setEditMode(false);
+                    }}
                     isLoading={isLoading}
                     ml={3}
                   >
@@ -126,7 +132,7 @@ const NotesAccordion = ({
               </form>
             ) : (
               <Flex direction="column">
-                <Flex direction="column" style={{ fontSize: '12px' }}>
+                <Flex direction="column" style={{ fontSize: "12px" }}>
                   <ReactMarkdown>{note}</ReactMarkdown>
                 </Flex>
                 <Flex justify="right">
@@ -134,12 +140,16 @@ const NotesAccordion = ({
                     onClick={() => setEditMode(true)}
                     isDisabled={!canEdit}
                     isLoading={isLoading}
-                    title={`${!note ? 'Add' : 'Edit'} a note to this ${objectIdentifier}`}
-                    aria-label={`${!note ? 'Add' : 'Edit'} a note to this ${objectIdentifier}`}
+                    title={`${
+                      !note ? "Add" : "Edit"
+                    } a note to this ${objectIdentifier}`}
+                    aria-label={`${
+                      !note ? "Add" : "Edit"
+                    } a note to this ${objectIdentifier}`}
                     mt={2}
                     leftIcon={<MdEdit />}
                   >
-                    {!note ? 'Add Note' : 'Edit Note'}
+                    {!note ? "Add Note" : "Edit Note"}
                   </Button>
                 </Flex>
               </Flex>
diff --git a/airflow/www/static/js/dag/details/dagRun/ClearRun.tsx b/airflow/www/static/js/dag/details/dagRun/ClearRun.tsx
index bd0a7d9234..d943bbeb13 100644
--- a/airflow/www/static/js/dag/details/dagRun/ClearRun.tsx
+++ b/airflow/www/static/js/dag/details/dagRun/ClearRun.tsx
@@ -17,14 +17,14 @@
  * under the License.
  */
 
-import React, { useState } from 'react';
-import { Button, useDisclosure } from '@chakra-ui/react';
+import React, { useState } from "react";
+import { Button, useDisclosure } from "@chakra-ui/react";
 
-import { useClearRun } from 'src/api';
-import { getMetaValue } from 'src/utils';
-import ConfirmDialog from 'src/components/ConfirmDialog';
+import { useClearRun } from "src/api";
+import { getMetaValue } from "src/utils";
+import ConfirmDialog from "src/components/ConfirmDialog";
 
-const canEdit = getMetaValue('can_edit') === 'True';
+const canEdit = getMetaValue("can_edit") === "True";
 
 interface Props {
   dagId: string;
@@ -50,11 +50,7 @@ const ClearRun = ({ dagId, runId }: Props) => {
 
   return (
     <>
-      <Button
-        onClick={onClick}
-        isLoading={isLoading}
-        isDisabled={!canEdit}
-      >
+      <Button onClick={onClick} isLoading={isLoading} isDisabled={!canEdit}>
         Clear existing tasks
       </Button>
       <ConfirmDialog
diff --git a/airflow/www/static/js/dag/details/dagRun/DatasetTriggerEvents.tsx b/airflow/www/static/js/dag/details/dagRun/DatasetTriggerEvents.tsx
index c18bddcfb6..07649d54d9 100644
--- a/airflow/www/static/js/dag/details/dagRun/DatasetTriggerEvents.tsx
+++ b/airflow/www/static/js/dag/details/dagRun/DatasetTriggerEvents.tsx
@@ -16,59 +16,56 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React, { useMemo } from 'react';
-import {
-  Box, Heading, Text,
-} from '@chakra-ui/react';
+import React, { useMemo } from "react";
+import { Box, Heading, Text } from "@chakra-ui/react";
 
 import {
-  DatasetLink, Table, TaskInstanceLink, TimeCell,
-} from 'src/components/Table';
-import { useUpstreamDatasetEvents } from 'src/api';
-import type { DagRun as DagRunType } from 'src/types';
+  DatasetLink,
+  Table,
+  TaskInstanceLink,
+  TimeCell,
+} from "src/components/Table";
+import { useUpstreamDatasetEvents } from "src/api";
+import type { DagRun as DagRunType } from "src/types";
 
 interface Props {
-  runId: DagRunType['runId'];
+  runId: DagRunType["runId"];
 }
 
 const DatasetTriggerEvents = ({ runId }: Props) => {
-  const { data: { datasetEvents = [] }, isLoading } = useUpstreamDatasetEvents({ runId });
+  const {
+    data: { datasetEvents = [] },
+    isLoading,
+  } = useUpstreamDatasetEvents({ runId });
 
   const columns = useMemo(
     () => [
       {
-        Header: 'Dataset URI',
-        accessor: 'datasetUri',
+        Header: "Dataset URI",
+        accessor: "datasetUri",
         Cell: DatasetLink,
       },
       {
-        Header: 'Source Task Instance',
-        accessor: 'sourceTaskId',
+        Header: "Source Task Instance",
+        accessor: "sourceTaskId",
         Cell: TaskInstanceLink,
       },
       {
-        Header: 'When',
-        accessor: 'timestamp',
+        Header: "When",
+        accessor: "timestamp",
         Cell: TimeCell,
       },
     ],
-    [],
+    []
   );
 
-  const data = useMemo(
-    () => datasetEvents,
-    [datasetEvents],
-  );
+  const data = useMemo(() => datasetEvents, [datasetEvents]);
 
   return (
     <Box mt={3} flexGrow={1}>
       <Heading size="md">Dataset Events</Heading>
       <Text>Dataset updates that triggered this DAG run.</Text>
-      <Table
-        data={data}
-        columns={columns}
-        isLoading={isLoading}
-      />
+      <Table data={data} columns={columns} isLoading={isLoading} />
     </Box>
   );
 };
diff --git a/airflow/www/static/js/dag/details/dagRun/MarkFailedRun.tsx b/airflow/www/static/js/dag/details/dagRun/MarkFailedRun.tsx
index 7096c10790..cd403de69f 100644
--- a/airflow/www/static/js/dag/details/dagRun/MarkFailedRun.tsx
+++ b/airflow/www/static/js/dag/details/dagRun/MarkFailedRun.tsx
@@ -17,14 +17,14 @@
  * under the License.
  */
 
-import React, { useState } from 'react';
-import { Button, useDisclosure } from '@chakra-ui/react';
+import React, { useState } from "react";
+import { Button, useDisclosure } from "@chakra-ui/react";
 
-import { useMarkFailedRun } from 'src/api';
-import { getMetaValue } from 'src/utils';
-import ConfirmDialog from 'src/components/ConfirmDialog';
+import { useMarkFailedRun } from "src/api";
+import { getMetaValue } from "src/utils";
+import ConfirmDialog from "src/components/ConfirmDialog";
 
-const canEdit = getMetaValue('can_edit') === 'True';
+const canEdit = getMetaValue("can_edit") === "True";
 
 interface Props {
   dagId: string;
@@ -50,7 +50,14 @@ const MarkFailedRun = ({ dagId, runId }: Props) => {
 
   return (
     <>
-      <Button onClick={onClick} colorScheme="red" isLoading={isLoading} isDisabled={!canEdit}>Mark Failed</Button>
+      <Button
+        onClick={onClick}
+        colorScheme="red"
+        isLoading={isLoading}
+        isDisabled={!canEdit}
+      >
+        Mark Failed
+      </Button>
       <ConfirmDialog
         isOpen={isOpen}
         onClose={onClose}
diff --git a/airflow/www/static/js/dag/details/dagRun/MarkSuccessRun.tsx b/airflow/www/static/js/dag/details/dagRun/MarkSuccessRun.tsx
index 38392e76fd..bbe755addf 100644
--- a/airflow/www/static/js/dag/details/dagRun/MarkSuccessRun.tsx
+++ b/airflow/www/static/js/dag/details/dagRun/MarkSuccessRun.tsx
@@ -17,14 +17,14 @@
  * under the License.
  */
 
-import React, { useState } from 'react';
-import { Button, useDisclosure } from '@chakra-ui/react';
+import React, { useState } from "react";
+import { Button, useDisclosure } from "@chakra-ui/react";
 
-import { useMarkSuccessRun } from 'src/api';
-import ConfirmDialog from 'src/components/ConfirmDialog';
-import { getMetaValue } from 'src/utils';
+import { useMarkSuccessRun } from "src/api";
+import ConfirmDialog from "src/components/ConfirmDialog";
+import { getMetaValue } from "src/utils";
 
-const canEdit = getMetaValue('can_edit') === 'True';
+const canEdit = getMetaValue("can_edit") === "True";
 
 interface Props {
   dagId: string;
@@ -34,7 +34,10 @@ interface Props {
 const MarkSuccessRun = ({ dagId, runId }: Props) => {
   const [affectedTasks, setAffectedTasks] = useState<string[]>([]);
   const { isOpen, onOpen, onClose } = useDisclosure();
-  const { mutateAsync: markSuccess, isLoading } = useMarkSuccessRun(dagId, runId);
+  const { mutateAsync: markSuccess, isLoading } = useMarkSuccessRun(
+    dagId,
+    runId
+  );
 
   const onClick = async () => {
     const data = await markSuccess({ confirmed: false });
@@ -50,7 +53,14 @@ const MarkSuccessRun = ({ dagId, runId }: Props) => {
 
   return (
     <>
-      <Button onClick={onClick} colorScheme="green" isLoading={isLoading} isDisabled={!canEdit}>Mark Success</Button>
+      <Button
+        onClick={onClick}
+        colorScheme="green"
+        isLoading={isLoading}
+        isDisabled={!canEdit}
+      >
+        Mark Success
+      </Button>
       <ConfirmDialog
         isOpen={isOpen}
         onClose={onClose}
diff --git a/airflow/www/static/js/dag/details/dagRun/QueueRun.tsx b/airflow/www/static/js/dag/details/dagRun/QueueRun.tsx
index e48187761a..3f684b7899 100644
--- a/airflow/www/static/js/dag/details/dagRun/QueueRun.tsx
+++ b/airflow/www/static/js/dag/details/dagRun/QueueRun.tsx
@@ -17,14 +17,14 @@
  * under the License.
  */
 
-import React, { useState } from 'react';
-import { Button, useDisclosure } from '@chakra-ui/react';
+import React, { useState } from "react";
+import { Button, useDisclosure } from "@chakra-ui/react";
 
-import { useQueueRun } from 'src/api';
-import ConfirmDialog from 'src/components/ConfirmDialog';
-import { getMetaValue } from 'src/utils';
+import { useQueueRun } from "src/api";
+import ConfirmDialog from "src/components/ConfirmDialog";
+import { getMetaValue } from "src/utils";
 
-const canEdit = getMetaValue('can_edit') === 'True';
+const canEdit = getMetaValue("can_edit") === "True";
 
 interface Props {
   dagId: string;
diff --git a/airflow/www/static/js/dag/details/dagRun/index.tsx b/airflow/www/static/js/dag/details/dagRun/index.tsx
index 31f610c12c..546c541299 100644
--- a/airflow/www/static/js/dag/details/dagRun/index.tsx
+++ b/airflow/www/static/js/dag/details/dagRun/index.tsx
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React, { useRef } from 'react';
+import React, { useRef } from "react";
 import {
   Flex,
   Text,
@@ -30,42 +30,44 @@ import {
   Tr,
   Td,
   useClipboard,
-} from '@chakra-ui/react';
+} from "@chakra-ui/react";
 
-import { MdOutlineAccountTree } from 'react-icons/md';
-import ReactJson from 'react-json-view';
+import { MdOutlineAccountTree } from "react-icons/md";
+import ReactJson from "react-json-view";
 
-import { useGridData } from 'src/api';
-import { appendSearchParams, getMetaValue, useOffsetTop } from 'src/utils';
-import type { DagRun as DagRunType } from 'src/types';
-import { SimpleStatus } from 'src/dag/StatusBox';
-import { ClipboardText } from 'src/components/Clipboard';
-import { formatDuration, getDuration } from 'src/datetime_utils';
-import Time from 'src/components/Time';
-import RunTypeIcon from 'src/components/RunTypeIcon';
-import URLSearchParamsWrapper from 'src/utils/URLSearchParamWrapper';
-import NotesAccordion from 'src/dag/details/NotesAccordion';
+import { useGridData } from "src/api";
+import { appendSearchParams, getMetaValue, useOffsetTop } from "src/utils";
+import type { DagRun as DagRunType } from "src/types";
+import { SimpleStatus } from "src/dag/StatusBox";
+import { ClipboardText } from "src/components/Clipboard";
+import { formatDuration, getDuration } from "src/datetime_utils";
+import Time from "src/components/Time";
+import RunTypeIcon from "src/components/RunTypeIcon";
+import URLSearchParamsWrapper from "src/utils/URLSearchParamWrapper";
+import NotesAccordion from "src/dag/details/NotesAccordion";
 
-import MarkFailedRun from './MarkFailedRun';
-import MarkSuccessRun from './MarkSuccessRun';
-import QueueRun from './QueueRun';
-import ClearRun from './ClearRun';
-import DatasetTriggerEvents from './DatasetTriggerEvents';
+import MarkFailedRun from "./MarkFailedRun";
+import MarkSuccessRun from "./MarkSuccessRun";
+import QueueRun from "./QueueRun";
+import ClearRun from "./ClearRun";
+import DatasetTriggerEvents from "./DatasetTriggerEvents";
 
-const dagId = getMetaValue('dag_id');
-const graphUrl = getMetaValue('graph_url');
+const dagId = getMetaValue("dag_id");
+const graphUrl = getMetaValue("graph_url");
 
 interface Props {
-  runId: DagRunType['runId'];
+  runId: DagRunType["runId"];
 }
 
 const DagRun = ({ runId }: Props) => {
-  const { data: { dagRuns } } = useGridData();
+  const {
+    data: { dagRuns },
+  } = useGridData();
   const detailsRef = useRef<HTMLDivElement>(null);
   const offsetTop = useOffsetTop(detailsRef);
 
   const run = dagRuns.find((dr) => dr.runId === runId);
-  const { onCopy, hasCopied } = useClipboard(run?.conf || '');
+  const { onCopy, hasCopied } = useClipboard(run?.conf || "");
   if (!run) return null;
   const {
     executionDate,
@@ -90,7 +92,13 @@ const DagRun = ({ runId }: Props) => {
   return (
     <>
       <Flex justifyContent="space-between" alignItems="center">
-        <Button as={Link} variant="ghost" colorScheme="blue" href={graphLink} leftIcon={<MdOutlineAccountTree />}>
+        <Button
+          as={Link}
+          variant="ghost"
+          colorScheme="blue"
+          href={graphLink}
+          leftIcon={<MdOutlineAccountTree />}
+        >
           Graph
         </Button>
         <MarkFailedRun dagId={dagId} runId={runId} />
@@ -99,7 +107,9 @@ const DagRun = ({ runId }: Props) => {
       <Box py="4px">
         <Divider my={3} />
         <Flex justifyContent="flex-end" alignItems="center">
-          <Text fontWeight="bold" mr={2}>Re-run:</Text>
+          <Text fontWeight="bold" mr={2}>
+            Re-run:
+          </Text>
           <ClearRun dagId={dagId} runId={runId} />
           <QueueRun dagId={dagId} runId={runId} />
         </Flex>
@@ -127,13 +137,15 @@ const DagRun = ({ runId }: Props) => {
               <Td>
                 <Flex>
                   <SimpleStatus state={state} mx={2} />
-                  {state || 'no status'}
+                  {state || "no status"}
                 </Flex>
               </Td>
             </Tr>
             <Tr>
               <Td>Run ID</Td>
-              <Td><ClipboardText value={runId} /></Td>
+              <Td>
+                <ClipboardText value={runId} />
+              </Td>
             </Tr>
             <Tr>
               <Td>Run type</Td>
@@ -145,9 +157,7 @@ const DagRun = ({ runId }: Props) => {
             {startDate && (
               <Tr>
                 <Td>Run duration</Td>
-                <Td>
-                  {formatDuration(getDuration(startDate, endDate))}
-                </Td>
+                <Td>{formatDuration(getDuration(startDate, endDate))}</Td>
               </Tr>
             )}
             {lastSchedulingDecision && (
@@ -200,38 +210,36 @@ const DagRun = ({ runId }: Props) => {
             )}
             <Tr>
               <Td>Externally triggered</Td>
-              <Td>
-                {externalTrigger ? 'True' : 'False'}
-              </Td>
+              <Td>{externalTrigger ? "True" : "False"}</Td>
             </Tr>
             <Tr>
               <Td>Run config</Td>
-              {
-                confIsJson
-                  ? (
-                    <Td>
-                      <Flex>
-                        <ReactJson
-                          src={JSON.parse(conf ?? '')}
-                          name={false}
-                          theme="rjv-default"
-                          iconStyle="triangle"
-                          indentWidth={2}
-                          displayDataTypes={false}
-                          enableClipboard={false}
-                          style={{ backgroundColor: 'inherit' }}
-                        />
-                        <Spacer />
-                        <Button aria-label="Copy" onClick={onCopy}>{hasCopied ? 'Copied!' : 'Copy'}</Button>
-                      </Flex>
-                    </Td>
-                  )
-                  : <Td>{conf ?? 'None'}</Td>
-              }
+              {confIsJson ? (
+                <Td>
+                  <Flex>
+                    <ReactJson
+                      src={JSON.parse(conf ?? "")}
+                      name={false}
+                      theme="rjv-default"
+                      iconStyle="triangle"
+                      indentWidth={2}
+                      displayDataTypes={false}
+                      enableClipboard={false}
+                      style={{ backgroundColor: "inherit" }}
+                    />
+                    <Spacer />
+                    <Button aria-label="Copy" onClick={onCopy}>
+                      {hasCopied ? "Copied!" : "Copy"}
+                    </Button>
+                  </Flex>
+                </Td>
+              ) : (
+                <Td>{conf ?? "None"}</Td>
+              )}
             </Tr>
           </Tbody>
         </Table>
-        {runType === 'dataset_triggered' && (
+        {runType === "dataset_triggered" && (
           <DatasetTriggerEvents runId={runId} />
         )}
       </Box>
diff --git a/airflow/www/static/js/dag/details/index.tsx b/airflow/www/static/js/dag/details/index.tsx
index ee73378acd..182e3acaf0 100644
--- a/airflow/www/static/js/dag/details/index.tsx
+++ b/airflow/www/static/js/dag/details/index.tsx
@@ -17,44 +17,36 @@
  * under the License.
  */
 
-import React from 'react';
-import {
-  Flex,
-  Box,
-  Divider,
-} from '@chakra-ui/react';
+import React from "react";
+import { Flex, Box, Divider } from "@chakra-ui/react";
 
-import useSelection from 'src/dag/useSelection';
+import useSelection from "src/dag/useSelection";
 
-import Header from './Header';
-import TaskInstanceContent from './taskInstance';
-import DagRunContent from './dagRun';
-import DagContent from './Dag';
+import Header from "./Header";
+import TaskInstanceContent from "./taskInstance";
+import DagRunContent from "./dagRun";
+import DagContent from "./Dag";
 
 const Details = () => {
-  const { selected: { runId, taskId, mapIndex }, onSelect } = useSelection();
+  const {
+    selected: { runId, taskId, mapIndex },
+    onSelect,
+  } = useSelection();
 
   return (
-    <Flex
-      flexDirection="column"
-      pl={3}
-      mr={3}
-      height="100%"
-    >
+    <Flex flexDirection="column" pl={3} mr={3} height="100%">
       <Header />
       <Divider my={2} />
       <Box height="100%">
         {!runId && !taskId && <DagContent />}
-        {runId && !taskId && (
-          <DagRunContent runId={runId} />
-        )}
+        {runId && !taskId && <DagRunContent runId={runId} />}
         {taskId && runId && (
-        <TaskInstanceContent
-          runId={runId}
-          taskId={taskId}
-          mapIndex={mapIndex === null ? undefined : mapIndex}
-          onSelect={onSelect}
-        />
+          <TaskInstanceContent
+            runId={runId}
+            taskId={taskId}
+            mapIndex={mapIndex === null ? undefined : mapIndex}
+            onSelect={onSelect}
+          />
         )}
       </Box>
     </Flex>
diff --git a/airflow/www/static/js/dag/details/taskInstance/BackToTaskSummary.tsx b/airflow/www/static/js/dag/details/taskInstance/BackToTaskSummary.tsx
index 26918d5cb5..df65cefe9d 100644
--- a/airflow/www/static/js/dag/details/taskInstance/BackToTaskSummary.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/BackToTaskSummary.tsx
@@ -17,8 +17,8 @@
  * under the License.
  */
 
-import React from 'react';
-import { Button, Flex } from '@chakra-ui/react';
+import React from "react";
+import { Button, Flex } from "@chakra-ui/react";
 
 interface Props {
   isMapIndexDefined: boolean;
@@ -30,12 +30,7 @@ const BackToTaskSummary = ({ isMapIndexDefined, onClick }: Props) => {
 
   return (
     <Flex justifyContent="right">
-      <Button
-        variant="ghost"
-        colorScheme="blue"
-        onClick={onClick}
-        size="lg"
-      >
+      <Button variant="ghost" colorScheme="blue" onClick={onClick} size="lg">
         Back to Dynamic Task Summary
       </Button>
     </Flex>
diff --git a/airflow/www/static/js/dag/details/taskInstance/DatasetUpdateEvents.tsx b/airflow/www/static/js/dag/details/taskInstance/DatasetUpdateEvents.tsx
index 2ed85ed0ec..919f4bca8a 100644
--- a/airflow/www/static/js/dag/details/taskInstance/DatasetUpdateEvents.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/DatasetUpdateEvents.tsx
@@ -16,69 +16,64 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React, { useMemo } from 'react';
-import {
-  Box, Heading, Text,
-} from '@chakra-ui/react';
+import React, { useMemo } from "react";
+import { Box, Heading, Text } from "@chakra-ui/react";
 
 import {
-  DatasetLink, Table, TimeCell, TriggeredRuns,
-} from 'src/components/Table';
-import { useDatasetEvents } from 'src/api';
-import type { DagRun as DagRunType } from 'src/types';
-import { getMetaValue } from 'src/utils';
+  DatasetLink,
+  Table,
+  TimeCell,
+  TriggeredRuns,
+} from "src/components/Table";
+import { useDatasetEvents } from "src/api";
+import type { DagRun as DagRunType } from "src/types";
+import { getMetaValue } from "src/utils";
 
 interface Props {
-  runId: DagRunType['runId'];
+  runId: DagRunType["runId"];
   taskId: string;
 }
 
-const dagId = getMetaValue('dag_id') || undefined;
+const dagId = getMetaValue("dag_id") || undefined;
 
 const DatasetUpdateEvents = ({ runId, taskId }: Props) => {
-  const { data: { datasetEvents = [] }, isLoading } = useDatasetEvents(
-    {
-      sourceDagId: dagId,
-      sourceRunId: runId,
-      sourceTaskId: taskId,
-    },
-  );
+  const {
+    data: { datasetEvents = [] },
+    isLoading,
+  } = useDatasetEvents({
+    sourceDagId: dagId,
+    sourceRunId: runId,
+    sourceTaskId: taskId,
+  });
 
   const columns = useMemo(
     () => [
       {
-        Header: 'Dataset URI',
-        accessor: 'datasetUri',
+        Header: "Dataset URI",
+        accessor: "datasetUri",
         Cell: DatasetLink,
       },
       {
-        Header: 'When',
-        accessor: 'timestamp',
+        Header: "When",
+        accessor: "timestamp",
         Cell: TimeCell,
       },
       {
-        Header: 'Triggered Runs',
-        accessor: 'createdDagruns',
+        Header: "Triggered Runs",
+        accessor: "createdDagruns",
         Cell: TriggeredRuns,
       },
     ],
-    [],
+    []
   );
 
-  const data = useMemo(
-    () => datasetEvents,
-    [datasetEvents],
-  );
+  const data = useMemo(() => datasetEvents, [datasetEvents]);
 
   return (
     <Box mt={3} flexGrow={1}>
       <Heading size="md">Dataset Events</Heading>
       <Text>Dataset updates caused by this task instance</Text>
-      <Table
-        data={data}
-        columns={columns}
-        isLoading={isLoading}
-      />
+      <Table data={data} columns={columns} isLoading={isLoading} />
     </Box>
   );
 };
diff --git a/airflow/www/static/js/dag/details/taskInstance/Details.tsx b/airflow/www/static/js/dag/details/taskInstance/Details.tsx
index 2baefa5d69..de92561d05 100644
--- a/airflow/www/static/js/dag/details/taskInstance/Details.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/Details.tsx
@@ -17,25 +17,17 @@
  * under the License.
  */
 
-import React from 'react';
-import {
-  Text,
-  Flex,
-  Table,
-  Tbody,
-  Tr,
-  Td,
-  Divider,
-} from '@chakra-ui/react';
+import React from "react";
+import { Text, Flex, Table, Tbody, Tr, Td, Divider } from "@chakra-ui/react";
 
-import { finalStatesMap } from 'src/utils';
-import { getDuration, formatDuration } from 'src/datetime_utils';
-import { SimpleStatus } from 'src/dag/StatusBox';
-import Time from 'src/components/Time';
-import { ClipboardText } from 'src/components/Clipboard';
-import type { Task, TaskInstance, TaskState } from 'src/types';
-import useTaskInstance from 'src/api/useTaskInstance';
-import DatasetUpdateEvents from './DatasetUpdateEvents';
+import { finalStatesMap } from "src/utils";
+import { getDuration, formatDuration } from "src/datetime_utils";
+import { SimpleStatus } from "src/dag/StatusBox";
+import Time from "src/components/Time";
+import { ClipboardText } from "src/components/Clipboard";
+import type { Task, TaskInstance, TaskState } from "src/types";
+import useTaskInstance from "src/api/useTaskInstance";
+import DatasetUpdateEvents from "./DatasetUpdateEvents";
 
 interface Props {
   instance: TaskInstance;
@@ -47,22 +39,10 @@ const Details = ({ instance, group, dagId }: Props) => {
   const isGroup = !!group.children;
   const summary: React.ReactNode[] = [];
 
-  const {
-    taskId,
-    runId,
-    startDate,
-    endDate,
-    state,
-    mappedStates,
-    mapIndex,
-  } = instance;
+  const { taskId, runId, startDate, endDate, state, mappedStates, mapIndex } =
+    instance;
 
-  const {
-    isMapped,
-    tooltip,
-    operator,
-    hasOutletDatasets,
-  } = group;
+  const { isMapped, tooltip, operator, hasOutletDatasets } = group;
 
   const { data: apiTI } = useTaskInstance({
     dagId,
@@ -78,15 +58,17 @@ const Details = ({ instance, group, dagId }: Props) => {
     group.children?.forEach((child) => {
       const taskInstance = child.instances.find((ti) => ti.runId === runId);
       if (taskInstance) {
-        const stateKey = taskInstance.state == null ? 'no_status' : taskInstance.state;
-        if (numMap.has(stateKey)) numMap.set(stateKey, (numMap.get(stateKey) || 0) + 1);
+        const stateKey =
+          taskInstance.state == null ? "no_status" : taskInstance.state;
+        if (numMap.has(stateKey))
+          numMap.set(stateKey, (numMap.get(stateKey) || 0) + 1);
       }
     });
   } else if (isMapped && mappedStates) {
     Object.keys(mappedStates).forEach((stateKey) => {
       const num = mappedStates[stateKey];
       numMapped += num;
-      numMap.set(stateKey || 'no_status', num);
+      numMap.set(stateKey || "no_status", num);
     });
   }
 
@@ -100,21 +82,23 @@ const Details = ({ instance, group, dagId }: Props) => {
             <Flex alignItems="center">
               <SimpleStatus state={val as TaskState} mx={2} />
               {val}
-              {': '}
+              {": "}
               {key}
             </Flex>
           </Td>
-        </Tr>,
+        </Tr>
       );
     }
   });
 
-  const taskIdTitle = isGroup ? 'Task Group ID' : 'Task ID';
-  const isStateFinal = state && ['success', 'failed', 'upstream_failed', 'skipped'].includes(state);
-  const isOverall = (isMapped || isGroup) && 'Overall ';
+  const taskIdTitle = isGroup ? "Task Group ID" : "Task ID";
+  const isStateFinal =
+    state &&
+    ["success", "failed", "upstream_failed", "skipped"].includes(state);
+  const isOverall = (isMapped || isGroup) && "Overall ";
   return (
     <Flex flexWrap="wrap" justifyContent="space-between">
-      {state === 'deferred' && (
+      {state === "deferred" && (
         <>
           <Text as="strong">Triggerer info</Text>
           <Divider my={2} />
@@ -158,17 +142,15 @@ const Details = ({ instance, group, dagId }: Props) => {
             <Td>
               <Flex>
                 <SimpleStatus state={state} mx={2} />
-                {state || 'no status'}
+                {state || "no status"}
               </Flex>
             </Td>
           </Tr>
           {mappedStates && numMapped > 0 && (
             <Tr>
               <Td colSpan={2}>
-                {numMapped}
-                {' '}
-                {isGroup ? 'Task Group' : 'Task'}
-                {numMapped === 1 ? ' ' : 's '}
+                {numMapped} {isGroup ? "Task Group" : "Task"}
+                {numMapped === 1 ? " " : "s "}
                 Mapped
               </Td>
             </Tr>
diff --git a/airflow/www/static/js/dag/details/taskInstance/ExtraLinks.tsx b/airflow/www/static/js/dag/details/taskInstance/ExtraLinks.tsx
index 2c9dbfe303..d4d4f04c49 100644
--- a/airflow/www/static/js/dag/details/taskInstance/ExtraLinks.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/ExtraLinks.tsx
@@ -17,15 +17,10 @@
  * under the License.
  */
 
-import React from 'react';
-import {
-  Button,
-  Flex,
-  Link,
-  Divider,
-} from '@chakra-ui/react';
+import React from "react";
+import { Button, Flex, Link, Divider } from "@chakra-ui/react";
 
-import { useExtraLinks } from 'src/api';
+import { useExtraLinks } from "src/api";
 
 interface Props {
   dagId: string;
@@ -41,11 +36,15 @@ const ExtraLinks = ({
   extraLinks = [],
 }: Props) => {
   const { data: links = [] } = useExtraLinks({
-    dagId, taskId, executionDate, extraLinks,
+    dagId,
+    taskId,
+    executionDate,
+    extraLinks,
   });
 
   if (!links.length) return null;
-  const isExternal = (url: string | null) => url && /^(?:[a-z]+:)?\/\//.test(url);
+  const isExternal = (url: string | null) =>
+    url && /^(?:[a-z]+:)?\/\//.test(url);
 
   return (
     <>
@@ -58,7 +57,7 @@ const ExtraLinks = ({
             colorScheme="blue"
             href={url}
             isDisabled={!url}
-            target={isExternal(url) ? '_blank' : undefined}
+            target={isExternal(url) ? "_blank" : undefined}
           >
             {name}
           </Button>
diff --git a/airflow/www/static/js/dag/details/taskInstance/Logs/LogBlock.tsx b/airflow/www/static/js/dag/details/taskInstance/Logs/LogBlock.tsx
index 33f7168177..36698b7057 100644
--- a/airflow/www/static/js/dag/details/taskInstance/Logs/LogBlock.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/Logs/LogBlock.tsx
@@ -17,13 +17,9 @@
  * under the License.
  */
 
-import React, {
-  useRef, useEffect, useState,
-} from 'react';
-import {
-  Code,
-} from '@chakra-ui/react';
-import { useOffsetTop } from 'src/utils';
+import React, { useRef, useEffect, useState } from "react";
+import { Code } from "@chakra-ui/react";
+import { useOffsetTop } from "src/utils";
 
 interface Props {
   parsedLogs: string;
@@ -31,11 +27,7 @@ interface Props {
   tryNumber: number;
 }
 
-const LogBlock = ({
-  parsedLogs,
-  wrap,
-  tryNumber,
-}: Props) => {
+const LogBlock = ({ parsedLogs, wrap, tryNumber }: Props) => {
   const [autoScroll, setAutoScroll] = useState(true);
 
   const logBoxRef = useRef<HTMLPreElement>(null);
@@ -43,7 +35,10 @@ const LogBlock = ({
   const offsetTop = useOffsetTop(logBoxRef);
 
   const scrollToBottom = () => {
-    codeBlockBottomDiv.current?.scrollIntoView({ block: 'nearest', inline: 'nearest' });
+    codeBlockBottomDiv.current?.scrollIntoView({
+      block: "nearest",
+      inline: "nearest",
+    });
   };
 
   useEffect(() => {
@@ -73,7 +68,7 @@ const LogBlock = ({
       p={3}
       pb={0}
       display="block"
-      whiteSpace={wrap ? 'pre-wrap' : 'pre'}
+      whiteSpace={wrap ? "pre-wrap" : "pre"}
       border="1px solid"
       borderRadius={3}
       borderColor="blue.500"
diff --git a/airflow/www/static/js/dag/details/taskInstance/Logs/LogLink.test.tsx b/airflow/www/static/js/dag/details/taskInstance/Logs/LogLink.test.tsx
index e3e235596e..32c1eb3b02 100644
--- a/airflow/www/static/js/dag/details/taskInstance/Logs/LogLink.test.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/Logs/LogLink.test.tsx
@@ -19,12 +19,12 @@
 
 /* global describe, test, expect */
 
-import React from 'react';
-import { render } from '@testing-library/react';
-import LogLink from './LogLink';
+import React from "react";
+import { render } from "@testing-library/react";
+import LogLink from "./LogLink";
 
-describe('Test LogLink Component.', () => {
-  test('Internal Link', () => {
+describe("Test LogLink Component.", () => {
+  test("Internal Link", () => {
     const tryNumber = 1;
     const { getByText, container } = render(
       <LogLink
@@ -33,19 +33,21 @@ describe('Test LogLink Component.', () => {
         taskId="dummyTaskId"
         executionDate="2020:01:01T01:00+00:00"
         isInternal
-      />,
+      />
     );
 
-    expect(getByText('Download')).toBeDefined();
-    const linkElement = container.querySelector('a');
+    expect(getByText("Download")).toBeDefined();
+    const linkElement = container.querySelector("a");
     expect(linkElement).toBeDefined();
-    expect(linkElement).not.toHaveAttribute('target');
-    expect(linkElement?.href.includes(
-      `?dag_id=dummyDagId&task_id=dummyTaskId&execution_date=2020%3A01%3A01T01%3A00%2B00%3A00&map_index=-1&format=file&try_number=${tryNumber}`,
-    )).toBeTruthy();
+    expect(linkElement).not.toHaveAttribute("target");
+    expect(
+      linkElement?.href.includes(
+        `?dag_id=dummyDagId&task_id=dummyTaskId&execution_date=2020%3A01%3A01T01%3A00%2B00%3A00&map_index=-1&format=file&try_number=${tryNumber}`
+      )
+    ).toBeTruthy();
   });
 
-  test('External Link', () => {
+  test("External Link", () => {
     const tryNumber = 1;
     const { getByText, container } = render(
       <LogLink
@@ -53,15 +55,17 @@ describe('Test LogLink Component.', () => {
         dagId="dummyDagId"
         taskId="dummyTaskId"
         executionDate="2020:01:01T01:00+00:00"
-      />,
+      />
     );
 
     expect(getByText(tryNumber)).toBeDefined();
-    const linkElement = container.querySelector('a');
+    const linkElement = container.querySelector("a");
     expect(linkElement).toBeDefined();
-    expect(linkElement).toHaveAttribute('target', '_blank');
-    expect(linkElement?.href.includes(
-      `?dag_id=dummyDagId&task_id=dummyTaskId&execution_date=2020%3A01%3A01T01%3A00%2B00%3A00&map_index=-1&try_number=${tryNumber}`,
-    )).toBeTruthy();
+    expect(linkElement).toHaveAttribute("target", "_blank");
+    expect(
+      linkElement?.href.includes(
+        `?dag_id=dummyDagId&task_id=dummyTaskId&execution_date=2020%3A01%3A01T01%3A00%2B00%3A00&map_index=-1&try_number=${tryNumber}`
+      )
+    ).toBeTruthy();
   });
 });
diff --git a/airflow/www/static/js/dag/details/taskInstance/Logs/LogLink.tsx b/airflow/www/static/js/dag/details/taskInstance/Logs/LogLink.tsx
index 9ae2704ce6..4aa02f10f3 100644
--- a/airflow/www/static/js/dag/details/taskInstance/Logs/LogLink.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/Logs/LogLink.tsx
@@ -17,45 +17,53 @@
  * under the License.
  */
 
-import React from 'react';
+import React from "react";
 
-import { getMetaValue } from 'src/utils';
-import LinkButton from 'src/components/LinkButton';
-import type { Dag, DagRun, TaskInstance } from 'src/types';
+import { getMetaValue } from "src/utils";
+import LinkButton from "src/components/LinkButton";
+import type { Dag, DagRun, TaskInstance } from "src/types";
 
-const logsWithMetadataUrl = getMetaValue('logs_with_metadata_url');
-const externalLogUrl = getMetaValue('external_log_url');
+const logsWithMetadataUrl = getMetaValue("logs_with_metadata_url");
+const externalLogUrl = getMetaValue("external_log_url");
 
 interface Props {
-  dagId: Dag['id'];
-  taskId: TaskInstance['taskId'];
-  executionDate: DagRun['executionDate'];
+  dagId: Dag["id"];
+  taskId: TaskInstance["taskId"];
+  executionDate: DagRun["executionDate"];
   isInternal?: boolean;
-  tryNumber: TaskInstance['tryNumber'];
-  mapIndex?: TaskInstance['mapIndex'];
+  tryNumber: TaskInstance["tryNumber"];
+  mapIndex?: TaskInstance["mapIndex"];
 }
 
 const LogLink = ({
-  dagId, taskId, executionDate, isInternal, tryNumber, mapIndex,
+  dagId,
+  taskId,
+  executionDate,
+  isInternal,
+  tryNumber,
+  mapIndex,
 }: Props) => {
-  let fullMetadataUrl = `${isInternal ? logsWithMetadataUrl : externalLogUrl
-  }?dag_id=${encodeURIComponent(dagId)
-  }&task_id=${encodeURIComponent(taskId)
-  }&execution_date=${encodeURIComponent(executionDate)
-  }&map_index=${encodeURIComponent(mapIndex?.toString() ?? '-1')
-  }`;
+  let fullMetadataUrl = `${
+    isInternal ? logsWithMetadataUrl : externalLogUrl
+  }?dag_id=${encodeURIComponent(dagId)}&task_id=${encodeURIComponent(
+    taskId
+  )}&execution_date=${encodeURIComponent(
+    executionDate
+  )}&map_index=${encodeURIComponent(mapIndex?.toString() ?? "-1")}`;
 
   if (isInternal && tryNumber) {
-    fullMetadataUrl += `&format=file${tryNumber > 0 && `&try_number=${tryNumber}`}`;
+    fullMetadataUrl += `&format=file${
+      tryNumber > 0 && `&try_number=${tryNumber}`
+    }`;
   } else {
     fullMetadataUrl += `&try_number=${tryNumber}`;
   }
   return (
     <LinkButton
       href={fullMetadataUrl}
-      target={isInternal ? undefined : '_blank'}
+      target={isInternal ? undefined : "_blank"}
     >
-      {isInternal ? 'Download' : tryNumber}
+      {isInternal ? "Download" : tryNumber}
     </LinkButton>
   );
 };
diff --git a/airflow/www/static/js/dag/details/taskInstance/Logs/index.test.tsx b/airflow/www/static/js/dag/details/taskInstance/Logs/index.test.tsx
index 19fd3ee953..a10dbe2931 100644
--- a/airflow/www/static/js/dag/details/taskInstance/Logs/index.test.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/Logs/index.test.tsx
@@ -19,14 +19,14 @@
 
 /* global jest, describe, test, expect, beforeEach, window */
 
-import React from 'react';
-import { render, fireEvent } from '@testing-library/react';
-import type { UseQueryResult } from 'react-query';
+import React from "react";
+import { render, fireEvent } from "@testing-library/react";
+import type { UseQueryResult } from "react-query";
 
-import * as utils from 'src/utils';
-import * as useTaskLogModule from 'src/api/useTaskLog';
+import * as utils from "src/utils";
+import * as useTaskLogModule from "src/api/useTaskLog";
 
-import Logs from './index';
+import Logs from "./index";
 
 const mockTaskLog = `
 5d28cfda3219
@@ -49,18 +49,20 @@ const mockTaskLog = `
 
 let useTaskLogMock: jest.SpyInstance;
 
-describe('Test Logs Component.', () => {
+describe("Test Logs Component.", () => {
   const returnValue = {
     data: mockTaskLog,
     isSuccess: true,
   } as UseQueryResult<string, unknown>;
 
   beforeEach(() => {
-    useTaskLogMock = jest.spyOn(useTaskLogModule, 'default').mockImplementation(() => returnValue);
+    useTaskLogMock = jest
+      .spyOn(useTaskLogModule, "default")
+      .mockImplementation(() => returnValue);
     window.HTMLElement.prototype.scrollIntoView = jest.fn();
   });
 
-  test('Test Logs Content', () => {
+  test("Test Logs Content", () => {
     const tryNumber = 2;
     const { getByText } = render(
       <Logs
@@ -69,56 +71,65 @@ describe('Test Logs Component.', () => {
         taskId="dummyTaskId"
         executionDate="2020:01:01T01:00+00:00"
         tryNumber={tryNumber}
-      />,
+      />
     );
-    expect(getByText('[2022-06-04, 00:00:01 UTC] {taskinstance.py:1329} INFO -', { exact: false })).toBeDefined();
-    expect(getByText(
-      '[2022-06-04, 00:00:01 UTC] {standard_task_runner.py:81} INFO - Job 1626: Subtask section_1.get_entry_group',
-      { exact: false },
-    )).toBeDefined();
-    expect(getByText('AIRFLOW_CTX_DAG_ID=test_ui_grid', { exact: false })).toBeDefined();
+    expect(
+      getByText("[2022-06-04, 00:00:01 UTC] {taskinstance.py:1329} INFO -", {
+        exact: false,
+      })
+    ).toBeDefined();
+    expect(
+      getByText(
+        "[2022-06-04, 00:00:01 UTC] {standard_task_runner.py:81} INFO - Job 1626: Subtask section_1.get_entry_group",
+        { exact: false }
+      )
+    ).toBeDefined();
+    expect(
+      getByText("AIRFLOW_CTX_DAG_ID=test_ui_grid", { exact: false })
+    ).toBeDefined();
 
     expect(useTaskLogMock).toHaveBeenLastCalledWith({
-      dagId: 'dummyDagId',
-      dagRunId: 'dummyDagRunId',
-      taskId: 'dummyTaskId',
+      dagId: "dummyDagId",
+      dagRunId: "dummyDagRunId",
+      taskId: "dummyTaskId",
       taskTryNumber: 2,
     });
   });
 
   test.each([
-    { defaultWrap: 'True', shouldBeChecked: true },
-    { defaultWrap: 'False', shouldBeChecked: false },
-    { defaultWrap: '', shouldBeChecked: false },
-  ])('Test wrap checkbox initial value $defaultWrap', ({ defaultWrap, shouldBeChecked }) => {
-    jest.spyOn(utils, 'getMetaValue').mockImplementation(
-      (meta) => {
-        if (meta === 'default_wrap') return defaultWrap;
-        return '';
-      },
-    );
-
-    const tryNumber = 2;
-    const { getByTestId } = render(
-      <Logs
-        dagId="dummyDagId"
-        dagRunId="dummyDagRunId"
-        taskId="dummyTaskId"
-        executionDate="2020:01:01T01:00+00:00"
-        mapIndex={1}
-        tryNumber={tryNumber}
-      />,
-    );
-
-    const wrapCheckbox = getByTestId('wrap-checkbox');
-    if (shouldBeChecked) {
-      expect(wrapCheckbox).toHaveAttribute('data-checked');
-    } else {
-      expect(wrapCheckbox.getAttribute('data-checked')).toBeNull();
+    { defaultWrap: "True", shouldBeChecked: true },
+    { defaultWrap: "False", shouldBeChecked: false },
+    { defaultWrap: "", shouldBeChecked: false },
+  ])(
+    "Test wrap checkbox initial value $defaultWrap",
+    ({ defaultWrap, shouldBeChecked }) => {
+      jest.spyOn(utils, "getMetaValue").mockImplementation((meta) => {
+        if (meta === "default_wrap") return defaultWrap;
+        return "";
+      });
+
+      const tryNumber = 2;
+      const { getByTestId } = render(
+        <Logs
+          dagId="dummyDagId"
+          dagRunId="dummyDagRunId"
+          taskId="dummyTaskId"
+          executionDate="2020:01:01T01:00+00:00"
+          mapIndex={1}
+          tryNumber={tryNumber}
+        />
+      );
+
+      const wrapCheckbox = getByTestId("wrap-checkbox");
+      if (shouldBeChecked) {
+        expect(wrapCheckbox).toHaveAttribute("data-checked");
+      } else {
+        expect(wrapCheckbox.getAttribute("data-checked")).toBeNull();
+      }
     }
-  });
+  );
 
-  test('Test Logs Content Mapped Task', () => {
+  test("Test Logs Content Mapped Task", () => {
     const tryNumber = 2;
     const { getByText } = render(
       <Logs
@@ -128,25 +139,33 @@ describe('Test Logs Component.', () => {
         executionDate="2020:01:01T01:00+00:00"
         mapIndex={1}
         tryNumber={tryNumber}
-      />,
+      />
     );
-    expect(getByText('[2022-06-04, 00:00:01 UTC] {taskinstance.py:1329} INFO -', { exact: false })).toBeDefined();
-    expect(getByText(
-      '[2022-06-04, 00:00:01 UTC] {standard_task_runner.py:81} INFO - Job 1626: Subtask section_1.get_entry_group',
-      { exact: false },
-    )).toBeDefined();
-    expect(getByText('AIRFLOW_CTX_DAG_ID=test_ui_grid', { exact: false })).toBeDefined();
+    expect(
+      getByText("[2022-06-04, 00:00:01 UTC] {taskinstance.py:1329} INFO -", {
+        exact: false,
+      })
+    ).toBeDefined();
+    expect(
+      getByText(
+        "[2022-06-04, 00:00:01 UTC] {standard_task_runner.py:81} INFO - Job 1626: Subtask section_1.get_entry_group",
+        { exact: false }
+      )
+    ).toBeDefined();
+    expect(
+      getByText("AIRFLOW_CTX_DAG_ID=test_ui_grid", { exact: false })
+    ).toBeDefined();
 
     expect(useTaskLogMock).toHaveBeenLastCalledWith({
-      dagId: 'dummyDagId',
-      dagRunId: 'dummyDagRunId',
+      dagId: "dummyDagId",
+      dagRunId: "dummyDagRunId",
       mapIndex: 1,
-      taskId: 'dummyTaskId',
+      taskId: "dummyTaskId",
       taskTryNumber: 2,
     });
   });
 
-  test('Test Logs Attempt Select Button', () => {
+  test("Test Logs Attempt Select Button", () => {
     const tryNumber = 2;
     const { getByText, getByTestId } = render(
       <Logs
@@ -155,28 +174,28 @@ describe('Test Logs Component.', () => {
         taskId="dummyTaskId"
         executionDate="2020:01:01T01:00+00:00"
         tryNumber={tryNumber}
-      />,
+      />
     );
     // Internal Log Attempt buttons.
-    expect(getByText('1')).toBeDefined();
-    expect(getByText('2')).toBeDefined();
+    expect(getByText("1")).toBeDefined();
+    expect(getByText("2")).toBeDefined();
 
-    expect(getByText('Download')).toBeDefined();
+    expect(getByText("Download")).toBeDefined();
 
     expect(useTaskLogMock).toHaveBeenLastCalledWith({
-      dagId: 'dummyDagId',
-      dagRunId: 'dummyDagRunId',
-      taskId: 'dummyTaskId',
+      dagId: "dummyDagId",
+      dagRunId: "dummyDagRunId",
+      taskId: "dummyTaskId",
       taskTryNumber: 2,
     });
-    const attemptButton1 = getByTestId('log-attempt-select-button-1');
+    const attemptButton1 = getByTestId("log-attempt-select-button-1");
 
     fireEvent.click(attemptButton1);
 
     expect(useTaskLogMock).toHaveBeenLastCalledWith({
-      dagId: 'dummyDagId',
-      dagRunId: 'dummyDagRunId',
-      taskId: 'dummyTaskId',
+      dagId: "dummyDagId",
+      dagRunId: "dummyDagRunId",
+      taskId: "dummyTaskId",
       taskTryNumber: 1,
     });
   });
diff --git a/airflow/www/static/js/dag/details/taskInstance/Logs/index.tsx b/airflow/www/static/js/dag/details/taskInstance/Logs/index.tsx
index 5ebf7b2a98..827a69edba 100644
--- a/airflow/www/static/js/dag/details/taskInstance/Logs/index.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/Logs/index.tsx
@@ -17,9 +17,7 @@
  * under the License.
  */
 
-import React, {
-  useState, useEffect, useMemo,
-} from 'react';
+import React, { useState, useEffect, useMemo } from "react";
 import {
   Text,
   Box,
@@ -28,21 +26,21 @@ import {
   Button,
   Checkbox,
   Icon,
-} from '@chakra-ui/react';
-import { MdWarning } from 'react-icons/md';
+} from "@chakra-ui/react";
+import { MdWarning } from "react-icons/md";
 
-import { getMetaValue } from 'src/utils';
-import useTaskLog from 'src/api/useTaskLog';
-import LinkButton from 'src/components/LinkButton';
-import { useTimezone } from 'src/context/timezone';
-import type { Dag, DagRun, TaskInstance } from 'src/types';
-import MultiSelect from 'src/components/MultiSelect';
+import { getMetaValue } from "src/utils";
+import useTaskLog from "src/api/useTaskLog";
+import LinkButton from "src/components/LinkButton";
+import { useTimezone } from "src/context/timezone";
+import type { Dag, DagRun, TaskInstance } from "src/types";
+import MultiSelect from "src/components/MultiSelect";
 
-import URLSearchParamsWrapper from 'src/utils/URLSearchParamWrapper';
+import URLSearchParamsWrapper from "src/utils/URLSearchParamWrapper";
 
-import LogLink from './LogLink';
-import { LogLevel, logLevelColorMapping, parseLogs } from './utils';
-import LogBlock from './LogBlock';
+import LogLink from "./LogLink";
+import { LogLevel, logLevelColorMapping, parseLogs } from "./utils";
+import LogBlock from "./LogBlock";
 
 interface LogLevelOption {
   label: LogLevel;
@@ -55,11 +53,14 @@ interface FileSourceOption {
   value: string;
 }
 
-const showExternalLogRedirect = getMetaValue('show_external_log_redirect') === 'True';
-const externalLogName = getMetaValue('external_log_name');
-const logUrl = getMetaValue('log_url');
+const showExternalLogRedirect =
+  getMetaValue("show_external_log_redirect") === "True";
+const externalLogName = getMetaValue("external_log_name");
+const logUrl = getMetaValue("log_url");
 
-const getLinkIndexes = (tryNumber: number | undefined): Array<Array<number>> => {
+const getLinkIndexes = (
+  tryNumber: number | undefined
+): Array<Array<number>> => {
   const internalIndexes: Array<number> = [];
   const externalIndexes: Array<number> = [];
 
@@ -79,18 +80,20 @@ const getLinkIndexes = (tryNumber: number | undefined): Array<Array<number>> =>
 
 const logLevelOptions: Array<LogLevelOption> = Object.values(LogLevel).map(
   (value): LogLevelOption => ({
-    label: value, value, color: logLevelColorMapping[value],
-  }),
+    label: value,
+    value,
+    color: logLevelColorMapping[value],
+  })
 );
 
 interface Props {
-  dagId: Dag['id'];
-  dagRunId: DagRun['runId'];
-  taskId: TaskInstance['taskId'];
-  mapIndex?: TaskInstance['mapIndex'];
-  executionDate: DagRun['executionDate'];
-  tryNumber: TaskInstance['tryNumber'];
-  state?: TaskInstance['state'];
+  dagId: Dag["id"];
+  dagRunId: DagRun["runId"];
+  taskId: TaskInstance["taskId"];
+  mapIndex?: TaskInstance["mapIndex"];
+  executionDate: DagRun["executionDate"];
+  tryNumber: TaskInstance["tryNumber"];
+  state?: TaskInstance["state"];
 }
 
 const Logs = ({
@@ -103,10 +106,16 @@ const Logs = ({
   state,
 }: Props) => {
   const [internalIndexes, externalIndexes] = getLinkIndexes(tryNumber);
-  const [selectedTryNumber, setSelectedTryNumber] = useState<number | undefined>();
-  const [wrap, setWrap] = useState(getMetaValue('default_wrap') === 'True');
-  const [logLevelFilters, setLogLevelFilters] = useState<Array<LogLevelOption>>([]);
-  const [fileSourceFilters, setFileSourceFilters] = useState<Array<FileSourceOption>>([]);
+  const [selectedTryNumber, setSelectedTryNumber] = useState<
+    number | undefined
+  >();
+  const [wrap, setWrap] = useState(getMetaValue("default_wrap") === "True");
+  const [logLevelFilters, setLogLevelFilters] = useState<Array<LogLevelOption>>(
+    []
+  );
+  const [fileSourceFilters, setFileSourceFilters] = useState<
+    Array<FileSourceOption>
+  >([]);
   const { timezone } = useTimezone();
 
   const taskTryNumber = selectedTryNumber || tryNumber || 1;
@@ -125,7 +134,7 @@ const Logs = ({
   });
 
   if (mapIndex !== undefined) {
-    params.append('map_index', mapIndex.toString());
+    params.append("map_index", mapIndex.toString());
   }
 
   const {
@@ -133,13 +142,14 @@ const Logs = ({
     fileSources = [],
     warning,
   } = useMemo(
-    () => parseLogs(
-      data,
-      timezone,
-      logLevelFilters.map((option) => option.value),
-      fileSourceFilters.map((option) => option.value),
-    ),
-    [data, fileSourceFilters, logLevelFilters, timezone],
+    () =>
+      parseLogs(
+        data,
+        timezone,
+        logLevelFilters.map((option) => option.value),
+        fileSourceFilters.map((option) => option.value)
+      ),
+    [data, fileSourceFilters, logLevelFilters, timezone]
   );
 
   useEffect(() => {
@@ -149,11 +159,15 @@ const Logs = ({
       setSelectedTryNumber(undefined);
     }
 
-    if (data && fileSourceFilters.length > 0
-      && fileSourceFilters.reduce(
-        (isSourceMissing, option) => (isSourceMissing || !fileSources.includes(option.value)),
-        false,
-      )) {
+    if (
+      data &&
+      fileSourceFilters.length > 0 &&
+      fileSourceFilters.reduce(
+        (isSourceMissing, option) =>
+          isSourceMissing || !fileSources.includes(option.value),
+        false
+      )
+    ) {
       setFileSourceFilters([]);
     }
   }, [data, fileSourceFilters, fileSources, taskTryNumber, tryNumber]);
@@ -169,7 +183,7 @@ const Logs = ({
                 {internalIndexes.map((index) => (
                   <Button
                     key={index}
-                    variant={taskTryNumber === index ? 'solid' : 'ghost'}
+                    variant={taskTryNumber === index ? "solid" : "ghost"}
                     colorScheme="blue"
                     onClick={() => setSelectedTryNumber(index)}
                     data-testid={`log-attempt-select-button-${index}`}
@@ -196,7 +210,7 @@ const Logs = ({
                       }),
                       option: (provided, ...rest) => ({
                         ...provided,
-                        borderLeft: 'solid 4px black',
+                        borderLeft: "solid 4px black",
                         borderColor: rest[0].data.color,
                         mt: 2,
                       }),
@@ -234,20 +248,22 @@ const Logs = ({
                   tryNumber={tryNumber}
                   mapIndex={mapIndex}
                 />
-                <LinkButton
-                  href={`${logUrl}&${params.toString()}`}
-                >
+                <LinkButton href={`${logUrl}&${params.toString()}`}>
                   See More
                 </LinkButton>
               </Flex>
             </Flex>
           </Box>
           {!!warning && (
-            <Flex bg="yellow.200" borderRadius={2} borderColor="gray.400" alignItems="center" p={2}>
+            <Flex
+              bg="yellow.200"
+              borderRadius={2}
+              borderColor="gray.400"
+              alignItems="center"
+              p={2}
+            >
               <Icon as={MdWarning} color="yellow.500" mr={2} />
-              <Text fontSize="sm">
-                {warning}
-              </Text>
+              <Text fontSize="sm">{warning}</Text>
             </Flex>
           )}
           {!!parsedLogs && (
@@ -262,27 +278,17 @@ const Logs = ({
       {externalLogName && externalIndexes.length > 0 && (
         <>
           <Box>
-            <Text>
-              View Logs in
-              {' '}
-              {externalLogName}
-              {' '}
-              (by attempts):
-            </Text>
+            <Text>View Logs in {externalLogName} (by attempts):</Text>
             <Flex flexWrap="wrap">
-              {
-                externalIndexes.map(
-                  (index) => (
-                    <LogLink
-                      key={index}
-                      dagId={dagId}
-                      taskId={taskId}
-                      executionDate={executionDate}
-                      tryNumber={index}
-                    />
-                  ),
-                )
-              }
+              {externalIndexes.map((index) => (
+                <LogLink
+                  key={index}
+                  dagId={dagId}
+                  taskId={taskId}
+                  executionDate={executionDate}
+                  tryNumber={index}
+                />
+              ))}
             </Flex>
           </Box>
           <Divider my={2} />
diff --git a/airflow/www/static/js/dag/details/taskInstance/Logs/utils.test.tsx b/airflow/www/static/js/dag/details/taskInstance/Logs/utils.test.tsx
index c822f5ee2e..23082e4282 100644
--- a/airflow/www/static/js/dag/details/taskInstance/Logs/utils.test.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/Logs/utils.test.tsx
@@ -19,7 +19,7 @@
 
 /* global describe, test, expect */
 
-import { LogLevel, parseLogs } from './utils';
+import { LogLevel, parseLogs } from "./utils";
 
 const mockTaskLog = `
 5d28cfda3219
@@ -40,93 +40,88 @@ const mockTaskLog = `
 [2022-06-04 00:00:02,010] {taskinstance.py:1548} WARNING - Exporting env vars: AIRFLOW_CTX_DAG_OWNER=*** AIRFLOW_CTX_DAG_ID=test_ui_grid
 `;
 
-describe('Test Logs Utils.', () => {
-  test('parseLogs function replaces datetimes', () => {
-    const { parsedLogs, fileSources } = parseLogs(
-      mockTaskLog,
-      'UTC',
-      [],
-      [],
-    );
+describe("Test Logs Utils.", () => {
+  test("parseLogs function replaces datetimes", () => {
+    const { parsedLogs, fileSources } = parseLogs(mockTaskLog, "UTC", [], []);
 
-    expect(parsedLogs).toContain('2022-06-04, 00:00:01 UTC');
+    expect(parsedLogs).toContain("2022-06-04, 00:00:01 UTC");
     expect(fileSources).toEqual([
-      'dagbag.py',
-      'standard_task_runner.py',
-      'task_command.py',
-      'taskinstance.py',
+      "dagbag.py",
+      "standard_task_runner.py",
+      "task_command.py",
+      "taskinstance.py",
     ]);
-    const result = parseLogs(
-      mockTaskLog,
-      'America/Los_Angeles',
-      [],
-      [],
-    );
-    expect(result.parsedLogs).toContain('2022-06-03, 17:00:01 PDT');
+    const result = parseLogs(mockTaskLog, "America/Los_Angeles", [], []);
+    expect(result.parsedLogs).toContain("2022-06-03, 17:00:01 PDT");
   });
 
   test.each([
-    { logLevelFilters: [LogLevel.INFO], expectedNumberOfLines: 11, expectedNumberOfFileSources: 4 },
+    {
+      logLevelFilters: [LogLevel.INFO],
+      expectedNumberOfLines: 11,
+      expectedNumberOfFileSources: 4,
+    },
     {
       logLevelFilters: [LogLevel.WARNING],
       expectedNumberOfLines: 1,
       expectedNumberOfFileSources: 1,
     },
   ])(
-    'Filtering logs on $logLevelFilters level should return $expectedNumberOfLines lines and $expectedNumberOfFileSources file sources',
+    "Filtering logs on $logLevelFilters level should return $expectedNumberOfLines lines and $expectedNumberOfFileSources file sources",
     ({
       logLevelFilters,
-      expectedNumberOfLines, expectedNumberOfFileSources,
+      expectedNumberOfLines,
+      expectedNumberOfFileSources,
     }) => {
       const { parsedLogs, fileSources } = parseLogs(
         mockTaskLog,
         null,
         logLevelFilters,
-        [],
+        []
       );
 
       expect(fileSources).toHaveLength(expectedNumberOfFileSources);
       expect(parsedLogs).toBeDefined();
-      const lines = parsedLogs!.split('\n');
+      const lines = parsedLogs!.split("\n");
       expect(lines).toHaveLength(expectedNumberOfLines);
       lines.forEach((line) => expect(line).toContain(logLevelFilters[0]));
-    },
+    }
   );
 
-  test('parseLogs function with file source filter', () => {
+  test("parseLogs function with file source filter", () => {
     const { parsedLogs, fileSources } = parseLogs(
       mockTaskLog,
       null,
       [],
-      ['taskinstance.py'],
+      ["taskinstance.py"]
     );
 
     expect(fileSources).toEqual([
-      'dagbag.py',
-      'standard_task_runner.py',
-      'task_command.py',
-      'taskinstance.py',
+      "dagbag.py",
+      "standard_task_runner.py",
+      "task_command.py",
+      "taskinstance.py",
     ]);
-    const lines = parsedLogs!.split('\n');
+    const lines = parsedLogs!.split("\n");
     expect(lines).toHaveLength(7);
-    lines.forEach((line) => expect(line).toContain('taskinstance.py'));
+    lines.forEach((line) => expect(line).toContain("taskinstance.py"));
   });
 
-  test('parseLogs function with filter on log level and file source', () => {
+  test("parseLogs function with filter on log level and file source", () => {
     const { parsedLogs, fileSources } = parseLogs(
       mockTaskLog,
       null,
       [LogLevel.INFO, LogLevel.WARNING],
-      ['taskinstance.py'],
+      ["taskinstance.py"]
     );
 
     expect(fileSources).toEqual([
-      'dagbag.py',
-      'standard_task_runner.py',
-      'task_command.py',
-      'taskinstance.py',
+      "dagbag.py",
+      "standard_task_runner.py",
+      "task_command.py",
+      "taskinstance.py",
     ]);
-    const lines = parsedLogs!.split('\n');
+    const lines = parsedLogs!.split("\n");
     expect(lines).toHaveLength(7);
     lines.forEach((line) => expect(line).toMatch(/INFO|WARNING/));
   });
diff --git a/airflow/www/static/js/dag/details/taskInstance/Logs/utils.ts b/airflow/www/static/js/dag/details/taskInstance/Logs/utils.ts
index 75e7ca1bed..21e215fe1e 100644
--- a/airflow/www/static/js/dag/details/taskInstance/Logs/utils.ts
+++ b/airflow/www/static/js/dag/details/taskInstance/Logs/utils.ts
@@ -19,29 +19,29 @@
 
 /* global moment */
 
-import { defaultFormatWithTZ } from 'src/datetime_utils';
+import { defaultFormatWithTZ } from "src/datetime_utils";
 
 export enum LogLevel {
-  DEBUG = 'DEBUG',
-  INFO = 'INFO',
-  WARNING = 'WARNING',
-  ERROR = 'ERROR',
-  CRITICAL = 'CRITICAL',
+  DEBUG = "DEBUG",
+  INFO = "INFO",
+  WARNING = "WARNING",
+  ERROR = "ERROR",
+  CRITICAL = "CRITICAL",
 }
 
 export const logLevelColorMapping = {
-  [LogLevel.DEBUG]: 'gray.300',
-  [LogLevel.INFO]: 'green.200',
-  [LogLevel.WARNING]: 'yellow.200',
-  [LogLevel.ERROR]: 'red.200',
-  [LogLevel.CRITICAL]: 'red.400',
+  [LogLevel.DEBUG]: "gray.300",
+  [LogLevel.INFO]: "green.200",
+  [LogLevel.WARNING]: "yellow.200",
+  [LogLevel.ERROR]: "red.200",
+  [LogLevel.CRITICAL]: "red.400",
 };
 
 export const parseLogs = (
   data: string | undefined,
   timezone: string | null,
   logLevelFilters: Array<LogLevel>,
-  fileSourceFilters: Array<string>,
+  fileSourceFilters: Array<string>
 ) => {
   if (!data) {
     return {};
@@ -51,9 +51,9 @@ export const parseLogs = (
   let warning;
 
   try {
-    lines = data.split('\n');
+    lines = data.split("\n");
   } catch (err) {
-    warning = 'Unable to show logs. There was an error parsing logs.';
+    warning = "Unable to show logs. There was an error parsing logs.";
     return { warning };
   }
 
@@ -64,28 +64,38 @@ export const parseLogs = (
     let parsedLine = line;
 
     // Apply log level filter.
... 9578 lines suppressed ...