---
.github/workflows/ci.yml | 2 +-
Dockerfile | 2 +-
package.json | 2 +-
yarn.lock | 20 ++++++++++----------
4 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 817b2938451..a1f58889ce2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -161,7 +161,7 @@ jobs:
name: e2e-web-tests (${{ matrix.shard-index }}/${{ strategy.job-total }})
runs-on: ubuntu-20.04
# We need to update this each time we update playwright
- container: mcr.microsoft.com/playwright:v1.44.0-focal
+ container: mcr.microsoft.com/playwright:v1.44.1-focal
strategy:
fail-fast: false
matrix:
diff --git a/Dockerfile b/Dockerfile
index 6376adeb2f1..9c39b9a9392 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -5,7 +5,7 @@
# reference: https://stackoverflow.com/a/51683309/3711475
# reference: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md#running-puppeteer-in-docker
-FROM mcr.microsoft.com/playwright:v1.44.0-focal AS setup
+FROM mcr.microsoft.com/playwright:v1.44.1-focal AS setup
USER root
diff --git a/package.json b/package.json
index b40e036263c..ca946437c61 100644
--- a/package.json
+++ b/package.json
@@ -129,7 +129,7 @@
"license-check-and-add": "^4.0.5",
"mini-css-extract-plugin": "2.9.0",
"npm-run-all": "^4.1.5",
- "playwright": "^1.44.0",
+ "playwright": "^1.44.1",
"postcss": "^8.4.35",
"postcss-modules": "^6.0.0",
"prettier": "^3.2.5",
diff --git a/yarn.lock b/yarn.lock
index bfb76bd927e..2379603431d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3354,7 +3354,7 @@ __metadata:
luxon: ^3.4.4
mini-css-extract-plugin: 2.9.0
npm-run-all: ^4.1.5
- playwright: ^1.44.0
+ playwright: ^1.44.1
postcss: ^8.4.35
postcss-modules: ^6.0.0
prettier: ^3.2.5
@@ -10673,27 +10673,27 @@ __metadata:
languageName: node
linkType: hard
-"playwright-core@npm:1.44.0":
- version: 1.44.0
- resolution: "playwright-core@npm:1.44.0"
+"playwright-core@npm:1.44.1":
+ version: 1.44.1
+ resolution: "playwright-core@npm:1.44.1"
bin:
playwright-core: cli.js
- checksum: 7bee257c830153578753a6dfb34b8216f8c552d750e24a0be6d3ba10baff013fb1320a1c3d487fbb0df9d1ce5d1f027ccf6e990d4514989da63691f177141ba4
+ checksum: ebc6fa0ff77792fe52648fda06cc4474d4e9746db6dc5750d262b7fe2caf9f9e2327a71f1fb365e862213403a9daf95361c5040a9b0fd462928d7eb4fdc760e1
languageName: node
linkType: hard
-"playwright@npm:^1.44.0":
- version: 1.44.0
- resolution: "playwright@npm:1.44.0"
+"playwright@npm:^1.44.1":
+ version: 1.44.1
+ resolution: "playwright@npm:1.44.1"
dependencies:
fsevents: 2.3.2
- playwright-core: 1.44.0
+ playwright-core: 1.44.1
dependenciesMeta:
fsevents:
optional: true
bin:
playwright: cli.js
- checksum: 22653ded652f436c1a837842009a175e8acb91ab340bb3deee87dbdb7205b439bd174f5f20591eb67f0171728c9f8f4bdfa3668a517da6bc7b45a4a79eabdbd5
+ checksum: c2e8ff0a855e4a9e665ae5ffa0ff90423289546157be55e83f78916d17075c5910a7244773739c2a973da884a4ff0b3cf9b6e6b543de112e43b12169a9208e86
languageName: node
linkType: hard
From 9ee52e9f589f811185e88a669dd4fde4b3266713 Mon Sep 17 00:00:00 2001
From: v-rakeshsh <155614445+v-rakeshsh@users.noreply.github.com>
Date: Sat, 15 Jun 2024 01:28:19 +0530
Subject: [PATCH 2/5] feat(react 18): upgrade to react 18. (#7336)
#### Details
This feature updates below packages.
1. react from v16 to v18.
2. react-dom from v16 to v18.
3. @types-react from v16 to v18.
4. @types-react-dom from v16 to v18.
5. @testing-library/react from v12 to v15.
6. @fluentui/react from v8.x.x to v8.118.1.
7. Removed react-helmet and added react-helmet-async.
**1. Notable changes for react, react-dom:**
**Motivation:** React 18 introduces a new root API which provides better
ergonomics for managing roots. The new root API also enables the new
concurrent renderer, which allows you to opt-into concurrent features.
**In V16, we had below to render the component:**
import { render } from 'react-dom';
const container = document.getElementById('app');
render(, container);
- **In V18, we have below to render the component:**
import { createRoot } from 'react-dom/client';
const container = document.getElementById('app');
const root = createRoot(container); // createRoot(container!) if you use
TypeScript
root.render();
**2. Notable changes for @types-react and @types-react-dom:**
**Motivation:** The new types are safer and catch issues that used to be
ignored by the type checker. The most notable change is that the
children prop now needs to be listed explicitly when defining props
- In old we have below
WrappedComponent: React.ComponentType,
- In new we have below
WrappedComponent: React.ComponentType<**React.PropsWithChildren
**>,
**Approach for type changes:** So this Type changes are added using
automation script https://github.com/eps1lon/types-react-codemod. This
automation script is suggested in react18 migration document.
- Added new package types-react-codemod.
- After adding the package, executed yarn types-react-codemod preset-18
./src in root, and then selected all option from the list of options.
- This will transform all types of component type having child
components to >.
**3. Notable changes for @testing-library/react:**
- Current version of @testing-library/react does not support react18, so
from v13.x.x, react18 support is added. So updated to latest V15. For
reference -
https://github.com/testing-library/react-testing-library/releases/tag/v13.0.0
- Wrapped state updates/async operations under act.
- Updated test cases with createRoot for createRootMock instead of
render and renderMock.
-
**4. Notable changes for @fluentui/react from v8.x.x to v8.118.1**
- Existing fluent ui version does not support react18, test cases were
failing, hence after checking v8.118.1 documentation, it supports react
and react-dom v18. Hence upadated.
**5. Notable changes for react-helmet-async:**
- Current react-helmet package throws error 'objects cannot be child,
expected elements', for react18, Hence as alternative used
react-helmet-async. For reference
https://www.npmjs.com/package/react-helmet-async?activeTab=readme
because react-helmet-async uses react18 as dependency.
- Wrapped Helmet provider for root, as to pass context of
react-helmet-async.
- Created a variable to store data, and then this data was passed as
JSX, instead of passing the data as it is. Because it will throw
**"Objects cannot be used as react elements"**.
**For example:**
`export const GuidanceTitle =
NamedFC('GuidanceTitle', ({ name }) => {
const titleValue = `Guidance for ${name} - ${productName}`;
return (
<>
{titleValue}
{name}
>
);
});`
**6. Along with above**
- Made changes to mock helpers, because after react18 changes, the JSON
structure of component was coming differently, so accordingly corrected
the helpers, to get proper component name for snapshots.
- Updated snapshots, because as we are using latest Fluent UI version,
new props are introduced which can be seen in snapshots.
- Refactored few test cases, which were wrong logically, like for
example:
using of mockReactComponents in global and inside test case using of
useOriginalComponents to get the props using
getMockComponentClassPropsForCall which was wrong logically is fixed to
use any one approach.
- Updated report package with react, react-dom v18 to keep in sync with
AI web.
##### Context
This PR includes all changes required for migration of AI web from
react16 to react18.
It includes test cases fixes.
It includes lint issues fixes.
#### Pull request checklist
- [ ] Addresses an existing issue: #0000
- [x] Ran `yarn fastpass`
- [x] Added/updated relevant unit test(s) (and ran `yarn test`)
- [x] Verified code coverage for the changes made. Check coverage report
at: `/test-results/unit/coverage`
- [x] PR title *AND* final merge commit title both start with a semantic
tag (`fix:`, `chore:`, `feat(feature-name):`, `refactor:`). See
`CONTRIBUTING.md`.
- [ ] (UI changes only) Added screenshots/GIFs to description above
- [x] (UI changes only) Verified usability with NVDA/JAWS
---------
Co-authored-by: Jeevani Chinthala
Co-authored-by: JeevaniChinthala <148259226+JeevaniChinthala@users.noreply.github.com>
Co-authored-by: v-sharmachir
Co-authored-by: Chirag Sharma <150002431+v-sharmachir@users.noreply.github.com>
Co-authored-by: Saanica Ghate <148259220+SaanicaG@users.noreply.github.com>
Co-authored-by: Saanica Ghate
---
package.json | 14 +-
packages/report/package.json | 8 +-
packages/ui/package.json | 6 +-
.../components/assessment-instance-table.tsx | 4 +-
src/DetailsView/details-view-initializer.ts | 10 +-
src/DetailsView/details-view-renderer.tsx | 11 +-
.../no-content-available-view-renderer.tsx | 10 +-
.../common/property-bag-column-renderer.tsx | 3 +-
.../components/with-store-subscription.tsx | 2 +-
.../extensibility/react-extension-point.tsx | 10 +-
src/common/react/named-fc.ts | 6 +-
src/common/types/link-component-type.ts | 2 +-
.../initializer/debug-tools-init.tsx | 6 +-
src/injected/dialog-renderer-impl.tsx | 9 +-
.../visualization/issues-formatter.ts | 4 +-
.../components/diagnostic-view-toggle.tsx | 2 +-
src/popup/incompatible-browser-renderer.tsx | 11 +-
src/popup/main-renderer.tsx | 11 +-
src/popup/popup-initializer.ts | 9 +-
.../types/report-export-service.ts | 2 +-
src/reports/components/head.tsx | 5 +-
.../components/report-instance-list.tsx | 8 +-
.../components/summary-report-head.tsx | 3 +-
.../settings-drop-down.test.ts.snap | 1 +
.../preview-features.test.ts.snap | 1 +
.../__snapshots__/settings-panel.test.ts.snap | 4 +-
.../first-time-dialog.test.ts.snap | 1 +
.../__snapshots__/hamburger-menu.test.ts.snap | 2 +
.../unit/mock-helpers/mock-module-helpers.tsx | 22 +-
...uto-detected-failures-dialog.test.tsx.snap | 117 +-
.../__snapshots__/export-dialog.test.tsx.snap | 12 +-
.../export-dropdown.test.tsx.snap | 20 +-
...valid-load-assessment-dialog.test.tsx.snap | 4 +-
.../issue-filing-dialog.test.tsx.snap | 36 +-
.../next-requirement-button.test.tsx.snap | 21 +-
.../start-over-dropdown.test.tsx.snap | 8 +-
.../__snapshots__/switcher.test.tsx.snap | 2 +-
...nstances-collapsible-content.test.tsx.snap | 12 +-
.../target-change-dialog.test.tsx.snap | 40 +-
.../assessment-instance-table.test.tsx | 28 +-
.../auto-detected-failures-dialog.test.tsx | 26 +-
.../choice-group-pass-fail.test.tsx | 7 +-
.../details-view-command-bar.test.tsx | 29 +-
...ils-view-content-with-local-state.test.tsx | 8 +-
.../components/export-dialog.test.tsx | 5 +-
.../failure-instance-panel-control.test.tsx | 18 +-
.../components/generic-dialog.test.tsx | 8 +-
.../invalid-load-assessment-dialog.test.tsx | 6 +-
.../components/issue-filing-dialog.test.tsx | 28 +-
.../next-requirement-button.test.tsx | 6 +-
...quick-assess-to-assessment-dialog.test.tsx | 5 +-
.../report-export-component.test.tsx | 12 +-
...restart-scan-visual-helper-toggle.test.tsx | 2 +-
.../save-assessment-button.test.tsx | 32 +-
.../components/start-over-dropdown.test.tsx | 62 +-
.../DetailsView/components/switcher.test.tsx | 12 +-
...ent-instances-collapsible-content.test.tsx | 16 +
.../details-view-renderer.test.tsx | 30 +-
...o-content-available-view-renderer.test.tsx | 32 +-
.../telemetry-permission-dialog.test.tsx.snap | 6 +-
.../components/selector-input-list.test.tsx | 11 +-
.../telemetry-permission-dialog.test.tsx | 10 +-
.../tests/common/components/toast.test.tsx | 7 +-
.../with-store-subscription.test.tsx | 10 +-
src/tests/unit/tests/content/index.test.tsx | 5 +
.../__snapshots__/stores-tree.test.tsx.snap | 6 +-
.../telemetry-viewer.test.tsx | 17 +-
.../components/details-dialog.test.tsx | 3 +-
.../injected/dialog-renderer-impl.test.tsx | 46 +-
.../azure-boards-settings-form.test.tsx.snap | 8 +-
.../launch-panel-header.test.tsx.snap | 380 +-
.../diagnostic-view-toggle.test.tsx | 89 +-
.../components/launch-panel-header.test.tsx | 17 +-
.../incompatible-browser-renderer.test.tsx | 24 +-
.../unit/tests/popup/main-renderer.test.tsx | 26 +-
.../reports/components/inline-image.test.tsx | 5 +-
.../guidance-title.test.tsx.snap | 5 +-
.../__snapshots__/markup.test.tsx.snap | 5 +-
.../views/content/guidance-title.test.tsx | 5 +-
.../unit/tests/views/content/markup.test.tsx | 4 +-
.../tests/views/insights/renderer.test.tsx | 13 +-
src/views/content/content-page.tsx | 11 +-
src/views/content/guidance-title.tsx | 23 +-
src/views/content/markup.tsx | 51 +-
src/views/insights/initializer.ts | 4 +-
src/views/insights/insights-router.tsx | 2 +-
src/views/insights/renderer.tsx | 19 +-
yarn.lock | 6203 +++++++----------
88 files changed, 3073 insertions(+), 4773 deletions(-)
diff --git a/package.json b/package.json
index ca946437c61..3c9080a1cdc 100644
--- a/package.json
+++ b/package.json
@@ -79,15 +79,15 @@
"@swc/core": "^1.3.107",
"@swc/jest": "^0.2.36",
"@testing-library/jest-dom": "^6.4.2",
- "@testing-library/react": "12.1.2",
+ "@testing-library/react": "^15.0.5",
"@types/chrome": "0.0.260",
"@types/jest": "^29.5.11",
"@types/jsdom": "^21.1.6",
"@types/lodash": "^4.14.202",
"@types/luxon": "^3.4.2",
"@types/node": "^16.11.7",
- "@types/react": "^16.14.25",
- "@types/react-dom": "^16.9.15",
+ "@types/react": "^18.3.1",
+ "@types/react-dom": "^18.3.0",
"@types/react-helmet": "^6.1.11",
"@types/react-router-dom": "^5.3.3",
"@types/serve-static": "^1.15.5",
@@ -151,7 +151,7 @@
"webpack-node-externals": "^3.0.0"
},
"dependencies": {
- "@fluentui/react": "^8.96.1",
+ "@fluentui/react": "^8.118.1",
"@microsoft/applicationinsights-web": "^2.8.15",
"@testing-library/user-event": "^14.5.2",
"ajv": "^8.12.0",
@@ -160,9 +160,9 @@
"idb-keyval": "^6.2.1",
"lodash": "^4.17.21",
"luxon": "^3.4.4",
- "react": "^16.14.0",
- "react-dom": "^16.14.0",
- "react-helmet": "^6.1.0",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-helmet-async": "^2.0.5",
"react-resize-detector": "^9.1.1",
"react-router-dom": "^6.21.3",
"tabbable": "^6.2.0",
diff --git a/packages/report/package.json b/packages/report/package.json
index 6bfc40d9fb7..56b8497ad2e 100644
--- a/packages/report/package.json
+++ b/packages/report/package.json
@@ -18,14 +18,14 @@
"url": "https://github.com/Microsoft/accessibility-insights-web"
},
"dependencies": {
- "@fluentui/react": "^8.96.1",
+ "@fluentui/react": "^8.118.1",
"axe-core": "4.8.4",
"classnames": "^2.5.1",
"lodash": "^4.17.21",
"luxon": "^3.4.4",
- "react": "^16.14.0",
- "react-dom": "^16.14.0",
- "react-helmet": "^6.1.0",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-helmet-async": "^2.0.5",
"uuid": "^9.0.1"
}
}
diff --git a/packages/ui/package.json b/packages/ui/package.json
index a972de8848a..c44dc002fa1 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -22,9 +22,9 @@
"classnames": "^2.5.1",
"lodash": "^4.17.21",
"luxon": "^3.4.4",
- "react": "^16.14.0",
- "react-dom": "^16.14.0",
- "react-helmet": "^6.1.0",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-helmet-async": "^2.0.5",
"react-resize-detector": "^9.1.1",
"uuid": "^9.0.1"
}
diff --git a/src/DetailsView/components/assessment-instance-table.tsx b/src/DetailsView/components/assessment-instance-table.tsx
index 4221b444ad6..c3fd59b0136 100644
--- a/src/DetailsView/components/assessment-instance-table.tsx
+++ b/src/DetailsView/components/assessment-instance-table.tsx
@@ -14,7 +14,7 @@ import { AssessmentDefaultMessageGenerator } from 'assessments/assessment-defaul
import { InstanceTableHeaderType, InstanceTableRow } from 'assessments/types/instance-table-data';
import { InsightsCommandButton } from 'common/components/controls/insights-command-button';
import { ManualTestStatus } from 'common/types/store-data/manual-test-status';
-import { has } from 'lodash';
+import { hasIn } from 'lodash';
import * as React from 'react';
import {
AssessmentNavState,
@@ -137,7 +137,7 @@ export class AssessmentInstanceTable extends React.Component
- has(item.instance.testStepResults, step) &&
+ hasIn(item.instance.testStepResults, step) &&
item.instance.testStepResults[step].status === ManualTestStatus.UNKNOWN,
);
}
diff --git a/src/DetailsView/details-view-initializer.ts b/src/DetailsView/details-view-initializer.ts
index 3de773bb90b..1659240f78d 100644
--- a/src/DetailsView/details-view-initializer.ts
+++ b/src/DetailsView/details-view-initializer.ts
@@ -81,7 +81,7 @@ import {
TabStopsFailedCounterIncludingNoInstance,
TabStopsFailedCounterInstancesOnly,
} from 'DetailsView/tab-stops-failed-counter';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOMClient from 'react-dom/client';
import { ReportExportServiceProviderImpl } from 'report-export/report-export-service-provider-impl';
import { AssessmentJsonExportGenerator } from 'reports/assessment-json-export-generator';
import { AssessmentReportHtmlGenerator } from 'reports/assessment-report-html-generator';
@@ -735,7 +735,7 @@ if (tabId != null) {
const renderer = new DetailsViewRenderer(
deps,
dom,
- ReactDOM.render,
+ ReactDOMClient.createRoot,
documentElementSetter,
);
@@ -751,7 +751,7 @@ if (tabId != null) {
.catch(() => {
const renderer = createNullifiedRenderer(
document,
- ReactDOM.render,
+ ReactDOMClient.createRoot,
createDefaultLogger(),
);
renderer.render();
@@ -760,7 +760,7 @@ if (tabId != null) {
function createNullifiedRenderer(
doc: Document,
- render: typeof ReactDOM.render,
+ createRoot: typeof ReactDOMClient.createRoot,
logger: Logger,
): NoContentAvailableViewRenderer {
// using an instance of an actual store (instead of a StoreProxy) so we can get the default state.
@@ -773,5 +773,5 @@ function createNullifiedRenderer(
getNarrowModeThresholds: getNarrowModeThresholdsForWeb,
};
- return new NoContentAvailableViewRenderer(deps, doc, render, documentElementSetter);
+ return new NoContentAvailableViewRenderer(deps, doc, createRoot, documentElementSetter);
}
diff --git a/src/DetailsView/details-view-renderer.tsx b/src/DetailsView/details-view-renderer.tsx
index c241ce2ca7d..9873562059f 100644
--- a/src/DetailsView/details-view-renderer.tsx
+++ b/src/DetailsView/details-view-renderer.tsx
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import * as React from 'react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOMClient from 'react-dom/client';
import { Theme, ThemeDeps } from '../common/components/theme';
import { config } from '../common/configuration';
import { DocumentManipulator } from '../common/document-manipulator';
@@ -12,21 +12,20 @@ export class DetailsViewRenderer {
constructor(
private readonly deps: DetailsViewRendererDeps,
private readonly dom: Document,
- private readonly renderer: typeof ReactDOM.render,
+ private readonly createRoot: typeof ReactDOMClient.createRoot,
private readonly documentManipulator: DocumentManipulator,
) {}
public render(): void {
- const detailsViewContainer = this.dom.querySelector('#details-container');
+ const detailsViewContainer = this.dom.querySelector('#details-container') as Element;
const iconPath = '../' + config.getOption('icon128');
this.documentManipulator.setShortcutIcon(iconPath);
-
- this.renderer(
+ const root = this.createRoot(detailsViewContainer);
+ root.render(
<>
>,
- detailsViewContainer,
);
}
}
diff --git a/src/DetailsView/no-content-available-view-renderer.tsx b/src/DetailsView/no-content-available-view-renderer.tsx
index 310af14edd1..5114ffc414d 100644
--- a/src/DetailsView/no-content-available-view-renderer.tsx
+++ b/src/DetailsView/no-content-available-view-renderer.tsx
@@ -6,22 +6,22 @@ import {
NoContentAvailableViewDeps,
} from 'DetailsView/components/no-content-available/no-content-available-view';
import * as React from 'react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOMClient from 'react-dom/client';
import { config } from '../common/configuration';
export class NoContentAvailableViewRenderer {
constructor(
private readonly deps: NoContentAvailableViewDeps,
private readonly dom: Document,
- private readonly renderer: typeof ReactDOM.render,
+ private readonly createRoot: typeof ReactDOMClient.createRoot,
private readonly documentManipulator: DocumentManipulator,
) {}
public render(): void {
- const detailsViewContainer = this.dom.querySelector('#details-container');
+ const detailsViewContainer = this.dom.querySelector('#details-container') as Element;
const iconPath = '../' + config.getOption('icon128');
this.documentManipulator.setShortcutIcon(iconPath);
-
- this.renderer(, detailsViewContainer);
+ const root = this.createRoot(detailsViewContainer);
+ root.render();
}
}
diff --git a/src/assessments/common/property-bag-column-renderer.tsx b/src/assessments/common/property-bag-column-renderer.tsx
index ed521bd1751..2695b4c60aa 100644
--- a/src/assessments/common/property-bag-column-renderer.tsx
+++ b/src/assessments/common/property-bag-column-renderer.tsx
@@ -50,7 +50,8 @@ export function propertyBagColumnRenderer(
propertyMap: DictionaryStringTo,
) => {
if (isEmpty(propertyMap)) {
- return {config.defaultValue};
+ const value: any = config.defaultValue;
+ return {value};
}
return Object.keys(propertyMap).map(key => {
diff --git a/src/common/components/with-store-subscription.tsx b/src/common/components/with-store-subscription.tsx
index 446254fad96..82e9f044577 100644
--- a/src/common/components/with-store-subscription.tsx
+++ b/src/common/components/with-store-subscription.tsx
@@ -14,7 +14,7 @@ export type WithStoreSubscriptionDeps = {
};
export function withStoreSubscription, S>(
- WrappedComponent: React.ComponentType
,
+ WrappedComponent: React.ComponentType>,
): React.ComponentClass>, Partial> & {
displayName: string;
} {
diff --git a/src/common/extensibility/react-extension-point.tsx b/src/common/extensibility/react-extension-point.tsx
index 938865a3207..77f057164db 100644
--- a/src/common/extensibility/react-extension-point.tsx
+++ b/src/common/extensibility/react-extension-point.tsx
@@ -20,14 +20,14 @@ type ExtensionPoint = {
apply: (component: C) => Extension;
};
-type ReactExtension = Extension> & {
+type ReactExtension = Extension>> & {
extensionType: 'reactComponent';
};
-type ReactExtensionPoint = ExtensionPoint> & {
+type ReactExtensionPoint = ExtensionPoint>> & {
extensionType: 'reactComponent';
- create: (component: React.FC) => ReactExtension
;
- component: React.FC
;
+ create: (component: React.FC>) => ReactExtension;
+ component: React.FC>;
};
function isReactExtension(extension: Extension): extension is ReactExtension {
@@ -55,7 +55,7 @@ export function reactExtensionPoint(
return result;
});
- function create(extensionComponent: React.FC
): ReactExtension
{
+ function create(extensionComponent: React.FC>): ReactExtension {
const Wrap = extensionComponent;
const wrapComponent = NamedFC
(extensionPointKey, props => );
wrapComponent.displayName = extensionPointKey;
diff --git a/src/common/react/named-fc.ts b/src/common/react/named-fc.ts
index ae9371169c8..508160c9e0b 100644
--- a/src/common/react/named-fc.ts
+++ b/src/common/react/named-fc.ts
@@ -2,11 +2,13 @@
// Licensed under the MIT License.
import * as React from 'react';
-export type ReactFCWithDisplayName
= React.FC
& { displayName: string };
+export type ReactFCWithDisplayName
= React.FC> & {
+ displayName: string;
+};
export function NamedFC(
displayName: string,
- component: React.FC
,
+ component: React.FC>,
): ReactFCWithDisplayName {
component.displayName = displayName;
diff --git a/src/common/types/link-component-type.ts b/src/common/types/link-component-type.ts
index 2029ba881f4..0f893e9c970 100644
--- a/src/common/types/link-component-type.ts
+++ b/src/common/types/link-component-type.ts
@@ -2,4 +2,4 @@
// Licensed under the MIT License.
import { ILinkProps } from '@fluentui/react';
-export type LinkComponentType = React.FC;
+export type LinkComponentType = React.FC>;
diff --git a/src/debug-tools/initializer/debug-tools-init.tsx b/src/debug-tools/initializer/debug-tools-init.tsx
index e17f0bebd39..35d26607370 100644
--- a/src/debug-tools/initializer/debug-tools-init.tsx
+++ b/src/debug-tools/initializer/debug-tools-init.tsx
@@ -30,7 +30,7 @@ import { defaultDateFormatter } from 'debug-tools/components/telemetry-viewer/te
import { TelemetryListener } from 'debug-tools/controllers/telemetry-listener';
import { DebugToolsNavStore } from 'debug-tools/stores/debug-tools-nav-store';
import * as React from 'react';
-import * as ReactDOM from 'react-dom';
+import { createRoot } from 'react-dom/client';
import UAParser from 'ua-parser-js';
export const initializeDebugTools = () => {
@@ -108,8 +108,8 @@ const createStoreProxies = (storeUpdateMessageHub: StoreUpdateMessageHub) => {
const render = (deps: DebugToolsViewDeps) => {
const container = document.querySelector('#debug-tools-container');
-
- ReactDOM.render(, container);
+ const root = createRoot(container);
+ root.render();
};
initializeDebugTools();
diff --git a/src/injected/dialog-renderer-impl.tsx b/src/injected/dialog-renderer-impl.tsx
index 0a129a9a2de..8a51edfb036 100644
--- a/src/injected/dialog-renderer-impl.tsx
+++ b/src/injected/dialog-renderer-impl.tsx
@@ -26,7 +26,7 @@ import { IssueFilingUrlStringUtils } from 'issue-filing/common/issue-filing-url-
import { PlainTextFormatter } from 'issue-filing/common/markup/plain-text-formatter';
import { AxeResultToIssueFilingDataConverter } from 'issue-filing/rule-result-to-issue-filing-data';
import * as React from 'react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOMClient from 'react-dom/client';
import { Target } from 'scanner/iruleresults';
import { DictionaryStringTo } from 'types/common-types';
import { rootContainerId } from './constants';
@@ -43,7 +43,7 @@ export class DialogRendererImpl implements DialogRenderer {
constructor(
private readonly dom: Document,
- private readonly renderer: typeof ReactDOM.render,
+ private readonly createRoot: typeof ReactDOMClient.createRoot,
private readonly frameMessenger: SingleFrameMessenger,
private readonly htmlElementUtils: HTMLElementUtils,
private readonly windowUtils: WindowUtils,
@@ -101,8 +101,8 @@ export class DialogRendererImpl implements DialogRenderer {
userConfigMessageCreator: mainWindowContext.getUserConfigMessageCreator(),
LinkComponent: NewTabLink,
};
-
- this.renderer(
+ const root = this.createRoot(dialogContainer);
+ root.render(
,
- dialogContainer,
);
return null;
} else {
diff --git a/src/injected/visualization/issues-formatter.ts b/src/injected/visualization/issues-formatter.ts
index c3d1fd07fc5..3f60b35ddd2 100644
--- a/src/injected/visualization/issues-formatter.ts
+++ b/src/injected/visualization/issues-formatter.ts
@@ -5,7 +5,7 @@ import { NavigatorUtils } from 'common/navigator-utils';
import { HtmlElementAxeResults } from 'common/types/store-data/visualization-scan-result-data';
import { DialogRendererImpl } from 'injected/dialog-renderer-impl';
import { SingleFrameMessenger } from 'injected/frameCommunicators/single-frame-messenger';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOMClient from 'react-dom/client';
import { BrowserAdapter } from '../../common/browser-adapters/browser-adapter';
import { HTMLElementUtils } from '../../common/html-element-utils';
@@ -31,7 +31,7 @@ export class IssuesFormatter implements Formatter {
) {
this.dialogRenderer = new DialogRendererImpl(
document,
- ReactDOM.render,
+ ReactDOMClient.createRoot,
frameMessenger,
htmlElementUtils,
windowUtils,
diff --git a/src/popup/components/diagnostic-view-toggle.tsx b/src/popup/components/diagnostic-view-toggle.tsx
index 21d1efc2b0a..4e9687d19d4 100644
--- a/src/popup/components/diagnostic-view-toggle.tsx
+++ b/src/popup/components/diagnostic-view-toggle.tsx
@@ -46,7 +46,7 @@ export class DiagnosticViewToggle extends React.Component<
// Must be consistent with internal react naming for same property to work
// tslint:disable-next-line: variable-name
- private _isMounted: boolean;
+ protected _isMounted: boolean;
constructor(props: DiagnosticViewToggleProps) {
super(props);
diff --git a/src/popup/incompatible-browser-renderer.tsx b/src/popup/incompatible-browser-renderer.tsx
index d13ac115fe2..c3772132657 100644
--- a/src/popup/incompatible-browser-renderer.tsx
+++ b/src/popup/incompatible-browser-renderer.tsx
@@ -2,21 +2,21 @@
// Licensed under the MIT License.
import { title } from 'content/strings/application';
import * as React from 'react';
-import ReactDOM from 'react-dom';
+import * as ReactDOMClient from 'react-dom/client';
import { NewTabLink } from '../common/components/new-tab-link';
import { Header } from './components/header';
export class IncompatibleBrowserRenderer {
constructor(
- private readonly renderer: typeof ReactDOM.render,
+ private readonly createRoot: typeof ReactDOMClient.createRoot,
private readonly dom: Document,
) {}
public render(): void {
- const container = this.dom.querySelector('#popup-container');
-
- this.renderer(
+ const container = this.dom.querySelector('#popup-container') as Element;
+ const root = this.createRoot(container);
+ root.render(
<>
@@ -38,7 +38,6 @@ export class IncompatibleBrowserRenderer {
>,
- container,
);
}
}
diff --git a/src/popup/main-renderer.tsx b/src/popup/main-renderer.tsx
index 053a147bad5..b34f0fec8f6 100644
--- a/src/popup/main-renderer.tsx
+++ b/src/popup/main-renderer.tsx
@@ -2,7 +2,7 @@
// Licensed under the MIT License.
import { title } from 'content/strings/application';
import * as React from 'react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOMClient from 'react-dom/client';
import { Theme, ThemeDeps, ThemeInnerState } from '../common/components/theme';
import { WithStoreSubscriptionDeps } from '../common/components/with-store-subscription';
@@ -20,7 +20,7 @@ export class MainRenderer {
constructor(
private readonly deps: MainRendererDeps,
private readonly popupHandlers: PopupHandlers,
- private readonly renderer: typeof ReactDOM.render,
+ private readonly createRoot: typeof ReactDOMClient.createRoot,
private readonly dom: Document,
private readonly popupWindow: Window,
private readonly targetTabUrl: string,
@@ -31,9 +31,9 @@ export class MainRenderer {
) {}
public render(): void {
- const container = this.dom.querySelector('#popup-container');
-
- this.renderer(
+ const container = this.dom.querySelector('#popup-container') as Element;
+ const root = this.createRoot(container);
+ root.render(
<>
>,
- container,
);
}
}
diff --git a/src/popup/popup-initializer.ts b/src/popup/popup-initializer.ts
index 9eefd313c44..3f1462b96fd 100644
--- a/src/popup/popup-initializer.ts
+++ b/src/popup/popup-initializer.ts
@@ -10,7 +10,7 @@ import { DocumentManipulator } from 'common/document-manipulator';
import { StoreUpdateMessageHub } from 'common/store-update-message-hub';
import { ExceptionTelemetryListener } from 'common/telemetry/exception-telemetry-listener';
import { ExceptionTelemetrySanitizer } from 'common/telemetry/exception-telemetry-sanitizer';
-import * as ReactDOM from 'react-dom';
+import { createRoot } from 'react-dom/client';
import { AxeInfo } from '../common/axe-info';
import { NewTabLink } from '../common/components/new-tab-link';
import { DropdownClickHandler } from '../common/dropdown-click-handler';
@@ -81,10 +81,7 @@ export class PopupInitializer {
}
private useIncompatibleBrowserRenderer = () => {
- const incompatibleBrowserRenderer = new IncompatibleBrowserRenderer(
- ReactDOM.render,
- document,
- );
+ const incompatibleBrowserRenderer = new IncompatibleBrowserRenderer(createRoot, document);
incompatibleBrowserRenderer.render();
};
@@ -239,7 +236,7 @@ export class PopupInitializer {
const renderer = new MainRenderer(
deps,
popupHandlers,
- ReactDOM.render,
+ createRoot,
document,
window,
tab.url,
diff --git a/src/report-export/types/report-export-service.ts b/src/report-export/types/report-export-service.ts
index 9825145b39c..445c27b8e50 100644
--- a/src/report-export/types/report-export-service.ts
+++ b/src/report-export/types/report-export-service.ts
@@ -25,5 +25,5 @@ export type ReportExportService = {
href?: string,
download?: string,
) => IContextualMenuItem;
- exportForm?: React.ComponentType;
+ exportForm?: React.ComponentType>;
};
diff --git a/src/reports/components/head.tsx b/src/reports/components/head.tsx
index 331d42339d5..ffe811ef806 100644
--- a/src/reports/components/head.tsx
+++ b/src/reports/components/head.tsx
@@ -12,13 +12,12 @@ export type bundledStylesProp = {
};
export const Head = NamedFC('Head', props => {
+ const titleValue = `${props.titlePreface} ${props.title}`;
// tslint:disable: react-no-dangerous-html
return (
-
- {props.titlePreface} {props.title}
-
+ {titleValue}
diff --git a/src/reports/components/report-instance-list.tsx b/src/reports/components/report-instance-list.tsx
index d71d1fba4a4..f1d54d9995e 100644
--- a/src/reports/components/report-instance-list.tsx
+++ b/src/reports/components/report-instance-list.tsx
@@ -32,7 +32,7 @@ export const ReportInstanceList = NamedFC('ReportInstan
return props.instances.map((instance, index) => {
return (
- {renderInstanceRows(instance)}
+ {renderInstanceRows(instance) as JSX.Element[]}
);
});
@@ -59,7 +59,7 @@ export const ReportInstanceList = NamedFC('ReportInstan
}
function renderPropertyBagHeaderRow(key: string): Row {
- return renderRow(
+ const rowValue = renderRow(
[
{key + ':'}
@@ -67,6 +67,8 @@ export const ReportInstanceList = NamedFC('ReportInstan
],
key,
);
+
+ return rowValue;
}
function renderProperyBagEntryRows(bag: BagOf, outerIndex: Index): Row[] {
@@ -97,7 +99,7 @@ export const ReportInstanceList = NamedFC('ReportInstan
function renderRow(cells: Cell[], index: Index): Row {
return (
- {cells}
+ {cells as JSX.Element[]}
);
}
diff --git a/src/reports/components/summary-report-head.tsx b/src/reports/components/summary-report-head.tsx
index 282bec78ac4..b1470ee6a60 100644
--- a/src/reports/components/summary-report-head.tsx
+++ b/src/reports/components/summary-report-head.tsx
@@ -7,11 +7,12 @@ import * as reportStyles from '../automated-checks-report.styles';
import * as bundledStyles from '../bundled-reporter-styles';
export const SummaryReportHead = NamedFC('SummaryReportHead', () => {
+ const titleValue = `${brand} automated checks result`;
// tslint:disable: react-no-dangerous-html
return (
- {brand} automated checks result
+ {titleValue}
diff --git a/src/tests/end-to-end/tests/common/__snapshots__/settings-drop-down.test.ts.snap b/src/tests/end-to-end/tests/common/__snapshots__/settings-drop-down.test.ts.snap
index a92f4784d69..24c24d3428e 100644
--- a/src/tests/end-to-end/tests/common/__snapshots__/settings-drop-down.test.ts.snap
+++ b/src/tests/end-to-end/tests/common/__snapshots__/settings-drop-down.test.ts.snap
@@ -11,6 +11,7 @@ exports[`Settings Dropdown content should match snapshot 1`] = `
|