diff --git a/apps/perf-test-react-components/.eslintrc.json b/apps/perf-test-react-components/.eslintrc.json
index 2948bbf09dea57..2b95bc03dc3f57 100644
--- a/apps/perf-test-react-components/.eslintrc.json
+++ b/apps/perf-test-react-components/.eslintrc.json
@@ -3,6 +3,7 @@
"root": true,
"rules": {
"@griffel/styles-file": "off",
- "no-console": "off"
+ "no-console": "off",
+ "no-restricted-globals": "off"
}
}
diff --git a/apps/public-docsite-v9/.eslintrc.json b/apps/public-docsite-v9/.eslintrc.json
index b3f394f7cbaca5..54ac641d7b3bfc 100644
--- a/apps/public-docsite-v9/.eslintrc.json
+++ b/apps/public-docsite-v9/.eslintrc.json
@@ -5,6 +5,7 @@
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/jsx-no-bind": "off",
"deprecation/deprecation": "off",
- "import/no-extraneous-dependencies": ["error", { "packageDir": [".", "../.."] }]
+ "import/no-extraneous-dependencies": ["error", { "packageDir": [".", "../.."] }],
+ "no-restricted-globals": "off"
}
}
diff --git a/apps/react-18-tests-v8/.eslintrc.json b/apps/react-18-tests-v8/.eslintrc.json
index 92f2e74b606c9e..b2f318c6b6a1e4 100644
--- a/apps/react-18-tests-v8/.eslintrc.json
+++ b/apps/react-18-tests-v8/.eslintrc.json
@@ -1,5 +1,7 @@
{
"extends": ["plugin:@fluentui/eslint-plugin/react"],
"root": true,
- "rules": {}
+ "rules": {
+ "no-restricted-globals": "off"
+ }
}
diff --git a/apps/react-18-tests-v9/.eslintrc.json b/apps/react-18-tests-v9/.eslintrc.json
index 92f2e74b606c9e..b2f318c6b6a1e4 100644
--- a/apps/react-18-tests-v9/.eslintrc.json
+++ b/apps/react-18-tests-v9/.eslintrc.json
@@ -1,5 +1,7 @@
{
"extends": ["plugin:@fluentui/eslint-plugin/react"],
"root": true,
- "rules": {}
+ "rules": {
+ "no-restricted-globals": "off"
+ }
}
diff --git a/apps/recipes-react-components/.eslintrc.json b/apps/recipes-react-components/.eslintrc.json
index 453f767c8db077..b9d702c5e755da 100644
--- a/apps/recipes-react-components/.eslintrc.json
+++ b/apps/recipes-react-components/.eslintrc.json
@@ -7,6 +7,7 @@
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/jsx-no-bind": "off",
"deprecation/deprecation": "off",
- "import/no-extraneous-dependencies": ["error", { "packageDir": [".", "../.."] }]
+ "import/no-extraneous-dependencies": ["error", { "packageDir": [".", "../.."] }],
+ "no-restricted-globals": "off"
}
}
diff --git a/apps/ssr-tests-v9/.eslintrc.json b/apps/ssr-tests-v9/.eslintrc.json
index f502b07b22439c..b6719ff5c00cf2 100644
--- a/apps/ssr-tests-v9/.eslintrc.json
+++ b/apps/ssr-tests-v9/.eslintrc.json
@@ -2,6 +2,7 @@
"extends": ["plugin:@fluentui/eslint-plugin/node"],
"root": true,
"rules": {
- "import/no-extraneous-dependencies": ["error", { "packageDir": ["../../", "./"] }]
+ "import/no-extraneous-dependencies": ["error", { "packageDir": ["../../", "./"] }],
+ "no-restricted-globals": "off"
}
}
diff --git a/apps/stress-test/.eslintrc.json b/apps/stress-test/.eslintrc.json
index f502b07b22439c..b6719ff5c00cf2 100644
--- a/apps/stress-test/.eslintrc.json
+++ b/apps/stress-test/.eslintrc.json
@@ -2,6 +2,7 @@
"extends": ["plugin:@fluentui/eslint-plugin/node"],
"root": true,
"rules": {
- "import/no-extraneous-dependencies": ["error", { "packageDir": ["../../", "./"] }]
+ "import/no-extraneous-dependencies": ["error", { "packageDir": ["../../", "./"] }],
+ "no-restricted-globals": "off"
}
}
diff --git a/apps/vr-tests-react-components/.eslintrc.json b/apps/vr-tests-react-components/.eslintrc.json
index 76ca2160a370b9..ffeab27cf4c88b 100644
--- a/apps/vr-tests-react-components/.eslintrc.json
+++ b/apps/vr-tests-react-components/.eslintrc.json
@@ -8,6 +8,7 @@
"@typescript-eslint/naming-convention": "off",
"@typescript-eslint/jsx-no-bind": "off",
"deprecation/deprecation": "off",
- "import/no-extraneous-dependencies": ["error", { "packageDir": [".", "../.."] }]
+ "import/no-extraneous-dependencies": ["error", { "packageDir": [".", "../.."] }],
+ "no-restricted-globals": "off"
}
}
diff --git a/change/@fluentui-cra-template-49db9598-b35e-4d1c-901b-ead2f5a354e0.json b/change/@fluentui-cra-template-49db9598-b35e-4d1c-901b-ead2f5a354e0.json
new file mode 100644
index 00000000000000..f9e92f127f9ae7
--- /dev/null
+++ b/change/@fluentui-cra-template-49db9598-b35e-4d1c-901b-ead2f5a354e0.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "chore: disallow `window` and `document` access",
+ "packageName": "@fluentui/cra-template",
+ "email": "seanmonahan@microsoft.com",
+ "dependentChangeType": "patch"
+}
diff --git a/change/@fluentui-eslint-plugin-ec3bdd41-2624-4271-bda6-b13ed5f7d0c5.json b/change/@fluentui-eslint-plugin-ec3bdd41-2624-4271-bda6-b13ed5f7d0c5.json
new file mode 100644
index 00000000000000..10bead64bbd14e
--- /dev/null
+++ b/change/@fluentui-eslint-plugin-ec3bdd41-2624-4271-bda6-b13ed5f7d0c5.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "chore: disallow `window` and `document` access for @fluentui/react-components",
+ "packageName": "@fluentui/eslint-plugin",
+ "email": "seanmonahan@microsoft.com",
+ "dependentChangeType": "patch"
+}
diff --git a/change/@fluentui-global-context-1e3db17a-f6cc-40b8-a367-feabee08ad3b.json b/change/@fluentui-global-context-1e3db17a-f6cc-40b8-a367-feabee08ad3b.json
new file mode 100644
index 00000000000000..8f952325fc2220
--- /dev/null
+++ b/change/@fluentui-global-context-1e3db17a-f6cc-40b8-a367-feabee08ad3b.json
@@ -0,0 +1,7 @@
+{
+ "type": "prerelease",
+ "comment": "chore: disallow `window` and `document` access",
+ "packageName": "@fluentui/global-context",
+ "email": "seanmonahan@microsoft.com",
+ "dependentChangeType": "patch"
+}
diff --git a/change/@fluentui-react-calendar-compat-0d185f2c-2c07-45d3-a65a-11a19f4ae6ba.json b/change/@fluentui-react-calendar-compat-0d185f2c-2c07-45d3-a65a-11a19f4ae6ba.json
new file mode 100644
index 00000000000000..3253bf30a954d7
--- /dev/null
+++ b/change/@fluentui-react-calendar-compat-0d185f2c-2c07-45d3-a65a-11a19f4ae6ba.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "chore: disallow `window` and `document` access",
+ "packageName": "@fluentui/react-calendar-compat",
+ "email": "seanmonahan@microsoft.com",
+ "dependentChangeType": "patch"
+}
diff --git a/change/@fluentui-react-motion-preview-5c6d53e5-4c7e-4418-a389-54ff5bceaf66.json b/change/@fluentui-react-motion-preview-5c6d53e5-4c7e-4418-a389-54ff5bceaf66.json
new file mode 100644
index 00000000000000..e36a0abab75926
--- /dev/null
+++ b/change/@fluentui-react-motion-preview-5c6d53e5-4c7e-4418-a389-54ff5bceaf66.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "chore: disallow `window` and `document` access",
+ "packageName": "@fluentui/react-motion-preview",
+ "email": "seanmonahan@microsoft.com",
+ "dependentChangeType": "patch"
+}
diff --git a/change/@fluentui-react-shared-contexts-a56a4c13-fffd-4819-a2ca-0334abbc53a9.json b/change/@fluentui-react-shared-contexts-a56a4c13-fffd-4819-a2ca-0334abbc53a9.json
new file mode 100644
index 00000000000000..ea37e72866d6d1
--- /dev/null
+++ b/change/@fluentui-react-shared-contexts-a56a4c13-fffd-4819-a2ca-0334abbc53a9.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "chore: disallow `window` and `document` access",
+ "packageName": "@fluentui/react-shared-contexts",
+ "email": "seanmonahan@microsoft.com",
+ "dependentChangeType": "patch"
+}
diff --git a/change/@fluentui-react-utilities-c5034c4b-aba2-4c9d-b59c-54708e33539c.json b/change/@fluentui-react-utilities-c5034c4b-aba2-4c9d-b59c-54708e33539c.json
new file mode 100644
index 00000000000000..7581a62e820c9c
--- /dev/null
+++ b/change/@fluentui-react-utilities-c5034c4b-aba2-4c9d-b59c-54708e33539c.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "chore: disallow `window` and `document` access",
+ "packageName": "@fluentui/react-utilities",
+ "email": "seanmonahan@microsoft.com",
+ "dependentChangeType": "patch"
+}
diff --git a/change/@fluentui-react-window-provider-5257f709-976b-4616-9470-013dfe88e991.json b/change/@fluentui-react-window-provider-5257f709-976b-4616-9470-013dfe88e991.json
new file mode 100644
index 00000000000000..218346cc9426df
--- /dev/null
+++ b/change/@fluentui-react-window-provider-5257f709-976b-4616-9470-013dfe88e991.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "chore: disallow `window` and `document` access",
+ "packageName": "@fluentui/react-window-provider",
+ "email": "seanmonahan@microsoft.com",
+ "dependentChangeType": "patch"
+}
diff --git a/packages/cra-template/template/src/index.tsx b/packages/cra-template/template/src/index.tsx
index 90113a51c98270..7e39450d98bf54 100644
--- a/packages/cra-template/template/src/index.tsx
+++ b/packages/cra-template/template/src/index.tsx
@@ -13,6 +13,7 @@ mergeStyles({
},
});
+// eslint-disable-next-line no-restricted-globals
ReactDOM.render(, document.getElementById('root'));
// If you want to start measuring performance in your app, pass a function
diff --git a/packages/eslint-plugin/src/configs/react-config.js b/packages/eslint-plugin/src/configs/react-config.js
index 2663520b69bd57..98a8643d1113c7 100644
--- a/packages/eslint-plugin/src/configs/react-config.js
+++ b/packages/eslint-plugin/src/configs/react-config.js
@@ -1,4 +1,5 @@
const configHelpers = require('../utils/configHelpers');
+const { react: restrictedGlobals } = require('./restricted-globals');
/** @type {import("eslint").Linter.Config} */
module.exports = {
@@ -28,6 +29,7 @@ module.exports = {
'@griffel/hook-naming': 'error',
'@griffel/no-shorthands': 'error',
'@griffel/styles-file': 'error',
+ 'no-restricted-globals': restrictedGlobals,
/**
* react eslint rules
* @see https://github.com/yannickcr/eslint-plugin-react
@@ -122,6 +124,7 @@ module.exports = {
files: [...configHelpers.testFiles],
rules: {
'react/jsx-no-bind': 'off',
+ 'no-restricted-globals': 'off',
},
},
{
@@ -129,6 +132,7 @@ module.exports = {
rules: {
// allow makeStyles calls in stories as examples should be defined in a single file
'@griffel/styles-file': 'off',
+ 'no-restricted-globals': 'off',
// allow arrow functions in stories for now (may want to change this later since using
// constantly-mutating functions can be an anti-pattern which we may not want to demonstrate
// in our converged components docs; it happened to be allowed starting out because .stories
diff --git a/packages/eslint-plugin/src/configs/react-legacy.js b/packages/eslint-plugin/src/configs/react-legacy.js
index 8d696ec41bfa1c..108f202b0d9a7e 100644
--- a/packages/eslint-plugin/src/configs/react-legacy.js
+++ b/packages/eslint-plugin/src/configs/react-legacy.js
@@ -9,6 +9,7 @@ module.exports = {
rules: {
'jsdoc/check-tag-names': 'off',
'@griffel/no-shorthands': 'off',
+ 'no-restricted-globals': 'off',
},
overrides: [],
};
diff --git a/packages/eslint-plugin/src/configs/restricted-globals.js b/packages/eslint-plugin/src/configs/restricted-globals.js
new file mode 100644
index 00000000000000..b8870e2883ecc0
--- /dev/null
+++ b/packages/eslint-plugin/src/configs/restricted-globals.js
@@ -0,0 +1,262 @@
+// These will be enabled in a follow up PR.
+// https://github.com/microsoft/fluentui/issues/29964
+
+// Via: https://developer.mozilla.org/en-US/docs/Web/API/Window
+// const windowKeys = [
+// // Instance properties
+// 'caches',
+// 'clientInformation',
+// 'closed',
+// 'console',
+// 'credentialless',
+// 'crypto',
+// 'customElements',
+// 'devicePixelRatio',
+// 'frameElement',
+// 'frames',
+// 'fullScreen',
+// 'history',
+// 'indexedDB',
+// 'innerHeight',
+// 'innerWidth',
+// 'isSecureContext',
+// 'launchQueue',
+// 'length',
+// 'localStorage',
+// 'location',
+// 'locationbar',
+// 'menubar',
+// 'name',
+// 'navigation',
+// 'navigator',
+// 'opener',
+// 'origin',
+// 'outerHeight',
+// 'outerWidth',
+// 'pageXOffset',
+// 'pageYOffset',
+// 'parent',
+// 'performance',
+// 'personalbar',
+// 'scheduler',
+// 'screen',
+// 'screenX',
+// 'screenY',
+// 'scrollbars',
+// 'scrollX',
+// 'scrollY',
+// 'self',
+// 'sessionStorage',
+// 'speechSynthesis',
+// 'statusbar',
+// 'toolbar',
+// 'top',
+// 'visualViewport',
+// 'window',
+
+// // Deprecated properties
+// 'defaultStatus',
+// 'event',
+// 'external',
+// 'orientation',
+// 'returnValue',
+// 'sidebar',
+// 'status',
+
+// // Instance methods
+// 'addEventListener',
+// 'alert',
+// 'atob',
+// 'blur',
+// 'btoa',
+// 'cancelAnimationFrame',
+// 'cancelIdleCallback',
+// 'clearImmediate',
+// 'clearInterval',
+// 'clearTimeout',
+// 'close',
+// 'confirm',
+// 'createImageBitmap',
+// 'dispatchEvent',
+// 'dump',
+// 'fetch',
+// 'find',
+// 'focus',
+// 'getComputedStyle',
+// 'getSelection',
+// 'matchMedia',
+// 'moveBy',
+// 'moveTo',
+// 'open',
+// 'postMessage',
+// 'print',
+// 'prompt',
+// 'queryLocalFonts',
+// 'removeEventListener',
+// 'reportError',
+// 'requestAnimationFrame',
+// 'requestIdleCallback',
+// 'resizeBy',
+// 'resizeTo',
+// 'scroll',
+// 'scrollBy',
+// 'scrollTo',
+// 'setImmediate',
+// 'setInterval',
+// 'setTimeout',
+// 'showDirectoryPicker',
+// 'showOpenFilePicker',
+// 'showSaveFilePicker',
+// 'stop',
+// 'updateCommands',
+
+// // Deprecated methods
+// 'back',
+// 'captureEvents',
+// 'forward',
+// 'releaseEvents',
+// 'showModalDialog',
+
+// // Events
+// 'onabort',
+// 'onafterprint',
+// 'onanimationend',
+// 'onanimationiteration',
+// 'onanimationstart',
+// 'onappinstalled',
+// 'onauxclick',
+// 'onbeforeinput',
+// 'onbeforeinstallprompt',
+// 'onbeforematch',
+// 'onbeforeprint',
+// 'onbeforetoggle',
+// 'onbeforeunload',
+// 'onbeforexrselect',
+// 'onblur',
+// 'oncancel',
+// 'oncanplay',
+// 'oncanplaythrough',
+// 'onchange',
+// 'onclick',
+// 'onclose',
+// 'oncontentvisibilityautostatechange',
+// 'oncontextlost',
+// 'oncontextmenu',
+// 'oncontextrestored',
+// 'oncuechange',
+// 'ondblclick',
+// 'ondevicemotion',
+// 'ondeviceorientation',
+// 'ondeviceorientationabsolute',
+// 'ondrag',
+// 'ondragend',
+// 'ondragenter',
+// 'ondragleave',
+// 'ondragover',
+// 'ondragstart',
+// 'ondrop',
+// 'ondurationchange',
+// 'onemptied',
+// 'onended',
+// 'onerror',
+// 'onfocus',
+// 'onformdata',
+// 'ongotpointercapture',
+// 'onhashchange',
+// 'oninput',
+// 'oninvalid',
+// 'onkeydown',
+// 'onkeypress',
+// 'onkeyup',
+// 'onlanguagechange',
+// 'onload',
+// 'onloadeddata',
+// 'onloadedmetadata',
+// 'onloadstart',
+// 'onlostpointercapture',
+// 'onmessage',
+// 'onmessageerror',
+// 'onmousedown',
+// 'onmouseenter',
+// 'onmouseleave',
+// 'onmousemove',
+// 'onmouseout',
+// 'onmouseover',
+// 'onmouseup',
+// 'onmousewheel',
+// 'onoffline',
+// 'ononline',
+// 'onoverscroll',
+// 'onpagehide',
+// 'onpageshow',
+// 'onpause',
+// 'onplay',
+// 'onplaying',
+// 'onpointercancel',
+// 'onpointerdown',
+// 'onpointerenter',
+// 'onpointerleave',
+// 'onpointermove',
+// 'onpointerout',
+// 'onpointerover',
+// 'onpointerrawupdate',
+// 'onpointerup',
+// 'onpopstate',
+// 'onprogress',
+// 'onratechange',
+// 'onrejectionhandled',
+// 'onreset',
+// 'onresize',
+// 'onscroll',
+// 'onscrollend',
+// 'onsearch',
+// 'onsecuritypolicyviolation',
+// 'onseeked',
+// 'onseeking',
+// 'onselect',
+// 'onselectionchange',
+// 'onselectstart',
+// 'onslotchange',
+// 'onstalled',
+// 'onstorage',
+// 'onsubmit',
+// 'onsuspend',
+// 'ontimeupdate',
+// 'ontimezonechange',
+// 'ontoggle',
+// 'ontransitioncancel',
+// 'ontransitionend',
+// 'ontransitionrun',
+// 'ontransitionstart',
+// 'onunhandledrejection',
+// 'onunload',
+// 'onvolumechange',
+// 'onwaiting',
+// 'onwebkitanimationend',
+// 'onwebkitanimationiteration',
+// 'onwebkitanimationstart',
+// 'onwebkittransitionend',
+// 'onwheel',
+// ];
+
+const react = [
+ 'error',
+ {
+ name: 'window',
+ message: 'Get a reference to `window` from `useFluent()`.',
+ },
+ {
+ name: 'document',
+ message: 'Get a reference to `document` from `useFluent()`.',
+ },
+ // ...windowKeys.map(key => {
+ // return {
+ // name: key,
+ // message: `Get a reference to \`window\` from \`useFluent()\` and access \`${key}\` from there.`,
+ // };
+ // }),
+];
+
+module.exports = {
+ react,
+};
diff --git a/packages/eslint-plugin/src/utils/configHelpers.js b/packages/eslint-plugin/src/utils/configHelpers.js
index 496ef038a2dc5f..37af2f6c4f7c0e 100644
--- a/packages/eslint-plugin/src/utils/configHelpers.js
+++ b/packages/eslint-plugin/src/utils/configHelpers.js
@@ -31,7 +31,7 @@ function getProjectMetadata(options) {
}
const testFiles = [
- '**/*{.,-}{test,spec,e2e}.{ts,tsx}',
+ '**/*{.,-}{test,spec,e2e,cy}.{ts,tsx}',
'**/{test,tests}/**',
'**/testUtilities.{ts,tsx}',
'**/common/{isConformant,snapshotSerializers}.{ts,tsx}',
diff --git a/packages/public-docsite-setup/.eslintrc.json b/packages/public-docsite-setup/.eslintrc.json
index 5c7aa8ffa91c02..cae3998301ac60 100644
--- a/packages/public-docsite-setup/.eslintrc.json
+++ b/packages/public-docsite-setup/.eslintrc.json
@@ -5,7 +5,8 @@
{
"files": ["bin/*.js", "src/loadSite.ts"],
"rules": {
- "no-console": "off"
+ "no-console": "off",
+ "no-restricted-globals": "off"
}
},
{
diff --git a/packages/react-components/global-context/src/global-context-selector.ts b/packages/react-components/global-context/src/global-context-selector.ts
index ec2d843afd7e66..591a1544925d70 100644
--- a/packages/react-components/global-context/src/global-context-selector.ts
+++ b/packages/react-components/global-context/src/global-context-selector.ts
@@ -5,6 +5,7 @@ import { getMajorVersion } from './utils';
import { GlobalObject } from './types';
const isBrowser = canUseDOM();
+// eslint-disable-next-line no-restricted-globals
const globalObject: GlobalObject = isBrowser ? window : global;
// Identifier for the symbol, for easy idenfitifaction of symbols created by this util
diff --git a/packages/react-components/global-context/src/global-context.ts b/packages/react-components/global-context/src/global-context.ts
index a994ddea8f0e49..39e3b8471fe0cf 100644
--- a/packages/react-components/global-context/src/global-context.ts
+++ b/packages/react-components/global-context/src/global-context.ts
@@ -4,6 +4,7 @@ import { GlobalObject } from './types';
import { getMajorVersion } from './utils';
const isBrowser = canUseDOM();
+// eslint-disable-next-line no-restricted-globals
const globalObject: GlobalObject = isBrowser ? window : global;
// Identifier for the symbol, for easy idenfitifaction of symbols created by this util
diff --git a/packages/react-components/react-calendar-compat/src/components/Calendar/Calendar.tsx b/packages/react-components/react-calendar-compat/src/components/Calendar/Calendar.tsx
index 5dbc4a88f88121..62638ff4d40844 100644
--- a/packages/react-components/react-calendar-compat/src/components/Calendar/Calendar.tsx
+++ b/packages/react-components/react-calendar-compat/src/components/Calendar/Calendar.tsx
@@ -1,6 +1,7 @@
import * as React from 'react';
import { Backspace, Enter, Escape, PageDown, PageUp, Space } from '@fluentui/keyboard-keys';
import { useControllableState } from '@fluentui/react-utilities';
+import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
import {
addMonths,
addYears,
@@ -10,7 +11,6 @@ import {
DEFAULT_DATE_FORMATTING,
FirstWeekOfYear,
focusAsync,
- getWindow,
} from '../../utils';
import { CalendarDay } from '../CalendarDay/CalendarDay';
import { CalendarMonth } from '../CalendarMonth/CalendarMonth';
@@ -85,20 +85,21 @@ function useVisibilityState({
showMonthPickerAsOverlay,
}: CalendarProps) {
/** State used to show/hide month picker */
+ const showMonthPickerAsOverlayState = useShowMonthPickerAsOverlay({
+ isDayPickerVisible: isDayPickerVisibleProp,
+ showMonthPickerAsOverlay,
+ });
+
const [isMonthPickerVisible, setIsMonthPickerVisible] = useControllableState({
defaultState: false,
initialState: true,
- state: getShowMonthPickerAsOverlay({ isDayPickerVisible: isDayPickerVisibleProp, showMonthPickerAsOverlay })
- ? undefined
- : isMonthPickerVisibleProp,
+ state: showMonthPickerAsOverlayState ? undefined : isMonthPickerVisibleProp,
});
/** State used to show/hide day picker */
const [isDayPickerVisible, setIsDayPickerVisible] = useControllableState({
defaultState: true,
initialState: true,
- state: getShowMonthPickerAsOverlay({ isDayPickerVisible: isDayPickerVisibleProp, showMonthPickerAsOverlay })
- ? undefined
- : isDayPickerVisibleProp,
+ state: showMonthPickerAsOverlayState ? undefined : isDayPickerVisibleProp,
});
const toggleDayMonthPickerVisibility = () => {
@@ -113,14 +114,16 @@ function useFocusLogic({ componentRef }: CalendarProps, isDayPickerVisible: bool
const dayPicker = React.useRef(null);
const monthPicker = React.useRef(null);
const focusOnUpdate = React.useRef(false);
+ const { targetDocument } = useFluent();
+ const win = targetDocument?.defaultView;
const focus = React.useCallback(() => {
if (isDayPickerVisible && dayPicker.current) {
- focusAsync(dayPicker.current);
+ focusAsync(dayPicker.current, win);
} else if (isMonthPickerVisible && monthPicker.current) {
- focusAsync(monthPicker.current);
+ focusAsync(monthPicker.current, win);
}
- }, [isDayPickerVisible, isMonthPickerVisible]);
+ }, [isDayPickerVisible, isMonthPickerVisible, win]);
React.useImperativeHandle(componentRef, () => ({ focus }), [focus]);
@@ -240,10 +243,12 @@ export const Calendar: React.FunctionComponent = React.forwardRef
navigateDay(date);
};
- const onHeaderSelect = getShowMonthPickerAsOverlay({
+ const showMonthPickerAsOverlay = useShowMonthPickerAsOverlay({
isDayPickerVisible: isDayPickerVisibleProp,
showMonthPickerAsOverlay: showMonthPickerAsOverlayProp,
- })
+ });
+
+ const onHeaderSelect = showMonthPickerAsOverlay
? (): void => {
toggleDayMonthPickerVisibility();
@@ -306,10 +311,6 @@ export const Calendar: React.FunctionComponent = React.forwardRef
break;
}
};
- const showMonthPickerAsOverlay = getShowMonthPickerAsOverlay({
- isDayPickerVisible: isDayPickerVisibleProp,
- showMonthPickerAsOverlay: showMonthPickerAsOverlayProp,
- });
const monthPickerOnly = !showMonthPickerAsOverlay && !isDayPickerVisible;
@@ -410,7 +411,8 @@ export const Calendar: React.FunctionComponent = React.forwardRef
);
Calendar.displayName = 'Calendar';
-function getShowMonthPickerAsOverlay({ isDayPickerVisible, showMonthPickerAsOverlay }: CalendarProps) {
- const win = getWindow();
+const useShowMonthPickerAsOverlay = ({ isDayPickerVisible, showMonthPickerAsOverlay }: CalendarProps) => {
+ const { targetDocument } = useFluent();
+ const win = targetDocument?.defaultView;
return showMonthPickerAsOverlay || (isDayPickerVisible && win && win.innerWidth <= MIN_SIZE_FORCE_OVERLAY);
-}
+};
diff --git a/packages/react-components/react-calendar-compat/src/utils/dom.ts b/packages/react-components/react-calendar-compat/src/utils/dom.ts
deleted file mode 100644
index 4ce45e2fa22b1f..00000000000000
--- a/packages/react-components/react-calendar-compat/src/utils/dom.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { canUseDOM } from '@fluentui/react-utilities';
-
-export function getWindow(targetElement?: Element | null): Window | undefined {
- if (!canUseDOM() || typeof window === 'undefined') {
- return undefined;
- }
-
- const el = targetElement as Element;
-
- return el && el.ownerDocument && el.ownerDocument.defaultView ? el.ownerDocument.defaultView : window;
-}
diff --git a/packages/react-components/react-calendar-compat/src/utils/focus.ts b/packages/react-components/react-calendar-compat/src/utils/focus.ts
index 97bc5ddb7bfa8b..ce48f80e48029b 100644
--- a/packages/react-components/react-calendar-compat/src/utils/focus.ts
+++ b/packages/react-components/react-calendar-compat/src/utils/focus.ts
@@ -1,5 +1,3 @@
-import { getWindow } from './dom';
-
let targetToFocusOnNextRepaint: HTMLElement | { focus: () => void } | null | undefined = undefined;
/**
@@ -8,7 +6,10 @@ let targetToFocusOnNextRepaint: HTMLElement | { focus: () => void } | null | und
* only the latest called focusAsync element will actually be focused
* @param element - The element to focus
*/
-export function focusAsync(element: HTMLElement | { focus: () => void } | undefined | null): void {
+export function focusAsync(
+ element: HTMLElement | { focus: () => void } | undefined | null,
+ win: Window | undefined | null,
+): void {
if (element) {
// An element was already queued to be focused, so replace that one with the new element
if (targetToFocusOnNextRepaint) {
@@ -18,8 +19,6 @@ export function focusAsync(element: HTMLElement | { focus: () => void } | undefi
targetToFocusOnNextRepaint = element;
- const win = getWindow(element as Element);
-
if (win) {
// element.focus() is a no-op if the element is no longer in the DOM, meaning this is always safe
win.requestAnimationFrame(() => {
diff --git a/packages/react-components/react-calendar-compat/src/utils/index.ts b/packages/react-components/react-calendar-compat/src/utils/index.ts
index 488020805bb61c..cb8c73d5cadcb6 100644
--- a/packages/react-components/react-calendar-compat/src/utils/index.ts
+++ b/packages/react-components/react-calendar-compat/src/utils/index.ts
@@ -3,5 +3,4 @@ export * from './constants';
export * from './dateFormatting';
export * from './dateGrid';
export * from './dateMath';
-export * from './dom';
export * from './focus';
diff --git a/packages/react-components/react-components/stories/AccessibilityScenarios/utils.tsx b/packages/react-components/react-components/stories/AccessibilityScenarios/utils.tsx
index 06438c6e95fc41..540d8e94f0f625 100644
--- a/packages/react-components/react-components/stories/AccessibilityScenarios/utils.tsx
+++ b/packages/react-components/react-components/stories/AccessibilityScenarios/utils.tsx
@@ -30,6 +30,7 @@ export const BackLink = () => Go back to main menu = ({ pageTitle, children }) => {
React.useEffect(() => {
+ // eslint-disable-next-line no-restricted-globals
document.title = pageTitle + APP_TITLE_SEPARATOR + APP_TITLE;
}, [pageTitle]);
diff --git a/packages/react-components/react-motion-preview/src/utils/dom-style.ts b/packages/react-components/react-motion-preview/src/utils/dom-style.ts
index faa0aef71fa582..bbde104c95ffd1 100644
--- a/packages/react-components/react-motion-preview/src/utils/dom-style.ts
+++ b/packages/react-components/react-motion-preview/src/utils/dom-style.ts
@@ -81,7 +81,11 @@ export const hasCSSOMSupport = (node: HTMLElementWithStyledMap) => {
* @returns - CSS styles.
*/
export const getElementComputedStyle = (node: HTMLElement): CSSStyleDeclaration => {
- const win = canUseDOM() && (node.ownerDocument?.defaultView ?? window);
+ const win =
+ canUseDOM() &&
+ (node.ownerDocument?.defaultView ??
+ // eslint-disable-next-line no-restricted-globals
+ window);
if (!win) {
return {
diff --git a/packages/react-components/react-shared-contexts/src/ProviderContext/ProviderContext.ts b/packages/react-components/react-shared-contexts/src/ProviderContext/ProviderContext.ts
index 5651db55545680..4b965b8e85a8fc 100644
--- a/packages/react-components/react-shared-contexts/src/ProviderContext/ProviderContext.ts
+++ b/packages/react-components/react-shared-contexts/src/ProviderContext/ProviderContext.ts
@@ -16,6 +16,7 @@ const ProviderContext = React.createContext(
) as React.Context;
const providerContextDefaultValue: ProviderContextValue = {
+ // eslint-disable-next-line no-restricted-globals
targetDocument: typeof document === 'object' ? document : undefined,
dir: 'ltr' as const,
};
diff --git a/packages/react-components/react-utilities/package.json b/packages/react-components/react-utilities/package.json
index 4f290812772e77..1c17653f62f735 100644
--- a/packages/react-components/react-utilities/package.json
+++ b/packages/react-components/react-utilities/package.json
@@ -32,6 +32,7 @@
},
"dependencies": {
"@fluentui/keyboard-keys": "^9.0.7",
+ "@fluentui/react-shared-contexts": "^9.13.0",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
diff --git a/packages/react-components/react-utilities/src/hooks/useOnClickOutside.ts b/packages/react-components/react-utilities/src/hooks/useOnClickOutside.ts
index 74b414a8b8ed58..1ae1f9b3e5f1bc 100644
--- a/packages/react-components/react-utilities/src/hooks/useOnClickOutside.ts
+++ b/packages/react-components/react-utilities/src/hooks/useOnClickOutside.ts
@@ -1,5 +1,6 @@
import * as React from 'react';
import { useEventCallback } from './useEventCallback';
+import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
/**
* @internal
@@ -45,6 +46,8 @@ const DEFAULT_CONTAINS: UseOnClickOrScrollOutsideOptions['contains'] = (parent,
* Utility to perform checks where a click/touch event was made outside a component
*/
export const useOnClickOutside = (options: UseOnClickOrScrollOutsideOptions) => {
+ const { targetDocument } = useFluent();
+ const win = targetDocument?.defaultView;
const { refs, callback, element, disabled, disabledFocusOnIframe, contains = DEFAULT_CONTAINS } = options;
const timeoutId = React.useRef(undefined);
@@ -80,7 +83,7 @@ export const useOnClickOutside = (options: UseOnClickOrScrollOutsideOptions) =>
// Store the current event to avoid triggering handlers immediately
// Note this depends on a deprecated but extremely well supported quirk of the web platform
// https://github.com/facebook/react/issues/20074
- let currentEvent = getWindowEvent(window);
+ let currentEvent = getWindowEvent(win);
const conditionalHandler = (event: MouseEvent | TouchEvent) => {
// Skip if this event is the same as the one running when we added the handlers
@@ -99,7 +102,7 @@ export const useOnClickOutside = (options: UseOnClickOrScrollOutsideOptions) =>
element?.addEventListener('mousedown', handleMouseDown, true);
// Garbage collect this event after it's no longer useful to avoid memory leaks
- timeoutId.current = window.setTimeout(() => {
+ timeoutId.current = win?.setTimeout(() => {
currentEvent = undefined;
}, 1);
@@ -109,13 +112,13 @@ export const useOnClickOutside = (options: UseOnClickOrScrollOutsideOptions) =>
element?.removeEventListener('contextmenu', conditionalHandler, true);
element?.removeEventListener('mousedown', handleMouseDown, true);
- clearTimeout(timeoutId.current);
+ win?.clearTimeout(timeoutId.current);
currentEvent = undefined;
};
- }, [listener, element, disabled, handleMouseDown]);
+ }, [listener, element, disabled, handleMouseDown, win]);
};
-const getWindowEvent = (target: Node | Window): Event | undefined => {
+const getWindowEvent = (target: Node | Window | null | undefined): Event | undefined => {
if (target) {
if (typeof (target as Window).window === 'object' && (target as Window).window === target) {
// eslint-disable-next-line deprecation/deprecation
diff --git a/packages/react-components/react-utilities/src/ssr/canUseDOM.ts b/packages/react-components/react-utilities/src/ssr/canUseDOM.ts
index 01520b98ccc7a8..97a67ead443ed5 100644
--- a/packages/react-components/react-utilities/src/ssr/canUseDOM.ts
+++ b/packages/react-components/react-utilities/src/ssr/canUseDOM.ts
@@ -3,11 +3,7 @@
*/
export function canUseDOM(): boolean {
return (
- typeof window !== 'undefined' &&
- !!(
- window.document &&
- // eslint-disable-next-line deprecation/deprecation
- window.document.createElement
- )
+ // eslint-disable-next-line deprecation/deprecation, no-restricted-globals
+ typeof window !== 'undefined' && !!(window.document && window.document.createElement)
);
}
diff --git a/packages/react-components/theme-designer/.eslintrc.json b/packages/react-components/theme-designer/.eslintrc.json
index a86d9b8501ad5b..eab62c7c347c87 100644
--- a/packages/react-components/theme-designer/.eslintrc.json
+++ b/packages/react-components/theme-designer/.eslintrc.json
@@ -2,6 +2,7 @@
"extends": ["plugin:@fluentui/eslint-plugin/react"],
"root": true,
"rules": {
- "@griffel/styles-file": "off"
+ "@griffel/styles-file": "off",
+ "no-restricted-globals": "off"
}
}
diff --git a/packages/react-window-provider/src/WindowProvider.tsx b/packages/react-window-provider/src/WindowProvider.tsx
index 61758d0c12ddf9..4b03b2d7ceb21a 100644
--- a/packages/react-window-provider/src/WindowProvider.tsx
+++ b/packages/react-window-provider/src/WindowProvider.tsx
@@ -16,6 +16,7 @@ export type WindowProviderProps = {
*/
// eslint-disable-next-line @fluentui/no-context-default-value
export const WindowContext = React.createContext({
+ // eslint-disable-next-line no-restricted-globals
window: typeof window === 'object' ? window : undefined,
});
diff --git a/packages/set-version/.eslintrc.json b/packages/set-version/.eslintrc.json
index 242cf7dcccffbd..636059d5b036f9 100644
--- a/packages/set-version/.eslintrc.json
+++ b/packages/set-version/.eslintrc.json
@@ -3,6 +3,7 @@
"root": true,
"rules": {
"deprecation/deprecation": "off",
- "prefer-const": "off"
+ "prefer-const": "off",
+ "no-restricted-globals": "off"
}
}