diff --git a/app/build-config/craco.config.js b/app/build-config/craco.config.js index 6aa47bfda0..144bba4abb 100644 --- a/app/build-config/craco.config.js +++ b/app/build-config/craco.config.js @@ -165,6 +165,8 @@ function configureWebpack(config, { paths }) { changePluginByName(config, 'HtmlWebpackPlugin', (plugin) => { plugin.userOptions.isWrapUuiAppInShadowDom = isWrapUuiAppInShadowDom; + // Template receives plugin.options, not userOptions (options is a copy made at plugin construction time) + plugin.options.isWrapUuiAppInShadowDom = isWrapUuiAppInShadowDom; }); changePluginByName(config, 'ForkTsCheckerWebpackPlugin', (plugin) => { // custom formatter can be removed when next bug is fixed: diff --git a/app/src/helpers/appRootUtils.ts b/app/src/helpers/appRootUtils.ts index 4d9a73ff1d..638e9e49eb 100644 --- a/app/src/helpers/appRootUtils.ts +++ b/app/src/helpers/appRootUtils.ts @@ -16,6 +16,7 @@ export function getAppRootNode() { div = document.createElement('div'); div.id = SHADOW_ROOT_ID; div.className = document.body.className; + div.style.height = '100%'; document.body.className = ''; defaultRoot.shadowRoot.appendChild(div); } diff --git a/changelog.md b/changelog.md index daa8e848c2..67a1e95835 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,7 @@ # 6.4.4 - xx.xx.2026 **What's New** +* [DropdownContainer]: added Shadow DOM support. * [Blocker]: added `inset` prop (`BlockerInset`: top, bottom, left, right in px) to control the blocker's coverage area. * Added Cursor AI integration with skills and developer documentation to improve AI-assisted development workflow. * [Dropdown]: added `fallbackPlacements` prop to customize alternative placements when preferred placement doesn't fit @@ -17,6 +18,7 @@ **What's Fixed** +* [DropdownContainer]: fixed `autoFocus` always being `true` even when `false` is passed through params * [DataPickerBody]: empty search results are announced via a **polite** off-screen `role="status"` region [#1506](https://github.com/epam/UUI/issues/1506) Case №9. * [VirtualList]: fixed loading `Blocker` not fully covering the visible area. * [FiltersPanel]: fixed filters being centered instead of left-aligned inside dropdown popups ([#3065](https://github.com/epam/UUI/issues/3065)) diff --git a/public/docs/content/unitTestingGuide-cookbook.json b/public/docs/content/unitTestingGuide-cookbook.json index c5d94f9d46..4802d31ebe 100644 --- a/public/docs/content/unitTestingGuide-cookbook.json +++ b/public/docs/content/unitTestingGuide-cookbook.json @@ -609,7 +609,7 @@ "type": "paragraph", "children": [ { - "text": "react-focus-lock", + "text": "@epam/uui-react-focus-lock-fork", "uui-richTextEditor-bold": true } ] @@ -679,7 +679,7 @@ "text": "(" }, { - "text": "'react-focus-lock'", + "text": "'@epam/uui-react-focus-lock-fork'", "uui-richTextEditor-bold": true }, { @@ -693,7 +693,7 @@ "text": "(" }, { - "text": "'react-focus-lock'", + "text": "'@epam/uui-react-focus-lock-fork'", "uui-richTextEditor-bold": true }, { diff --git a/public/docs/content/unitTestingGuide-getting-started.json b/public/docs/content/unitTestingGuide-getting-started.json index a406b92e5c..c347180475 100644 --- a/public/docs/content/unitTestingGuide-getting-started.json +++ b/public/docs/content/unitTestingGuide-getting-started.json @@ -78,7 +78,7 @@ "text": ", " }, { - "text": "react-focus-lock", + "text": "@epam/uui-react-focus-lock-fork", "uui-richTextEditor-code": true }, { diff --git a/public/docs/docsGenOutput/docsGenOutput.json b/public/docs/docsGenOutput/docsGenOutput.json index 506dcc909b..85db282151 100644 --- a/public/docs/docsGenOutput/docsGenOutput.json +++ b/public/docs/docsGenOutput/docsGenOutput.json @@ -51371,7 +51371,7 @@ "editor": { "type": "bool" }, - "from": "node_modules/react-focus-lock/dist/cjs/interfaces.d.ts:ReactFocusLockProps", + "from": "node_modules/@epam/uui-react-focus-lock-fork/dist/cjs/interfaces.d.ts:ReactFocusLockProps", "required": false }, { @@ -51386,7 +51386,7 @@ "raw": "string | React.ComponentClass & { children: React.ReactNode; }, any> | React.FunctionComponent & { children: React.ReactNode; }>", "html": "string | React.ComponentClass<Record<string, any> & { children: React.ReactNode; }, any> | React.FunctionComponent<Record<string, any> & { children: React.ReactNode; }>" }, - "from": "node_modules/react-focus-lock/dist/cjs/interfaces.d.ts:ReactFocusLockProps", + "from": "node_modules/@epam/uui-react-focus-lock-fork/dist/cjs/interfaces.d.ts:ReactFocusLockProps", "required": false }, { @@ -51401,7 +51401,7 @@ "raw": "(HTMLElement | React.RefObject)[]", "html": "(HTMLElement | React.RefObject<any>)[]" }, - "from": "node_modules/react-focus-lock/dist/cjs/interfaces.d.ts:ReactFocusLockProps", + "from": "node_modules/@epam/uui-react-focus-lock-fork/dist/cjs/interfaces.d.ts:ReactFocusLockProps", "required": false }, { @@ -51421,7 +51421,7 @@ "raw": "boolean | FocusOptions | (returnTo: Element) => boolean | FocusOptions", "html": "boolean | FocusOptions | (returnTo: Element) => boolean | FocusOptions" }, - "from": "node_modules/react-focus-lock/dist/cjs/interfaces.d.ts:ReactFocusLockProps", + "from": "node_modules/@epam/uui-react-focus-lock-fork/dist/cjs/interfaces.d.ts:ReactFocusLockProps", "required": false } ], @@ -78218,7 +78218,7 @@ "editor": { "type": "bool" }, - "from": "node_modules/react-focus-lock/dist/cjs/interfaces.d.ts:ReactFocusLockProps", + "from": "node_modules/@epam/uui-react-focus-lock-fork/dist/cjs/interfaces.d.ts:ReactFocusLockProps", "required": false }, { @@ -78233,7 +78233,7 @@ "raw": "string | React.ComponentClass & { children: React.ReactNode; }, any> | React.FunctionComponent & { children: React.ReactNode; }>", "html": "string | React.ComponentClass<Record<string, any> & { children: React.ReactNode; }, any> | React.FunctionComponent<Record<string, any> & { children: React.ReactNode; }>" }, - "from": "node_modules/react-focus-lock/dist/cjs/interfaces.d.ts:ReactFocusLockProps", + "from": "node_modules/@epam/uui-react-focus-lock-fork/dist/cjs/interfaces.d.ts:ReactFocusLockProps", "required": false }, { @@ -78248,7 +78248,7 @@ "raw": "(HTMLElement | React.RefObject)[]", "html": "(HTMLElement | React.RefObject<any>)[]" }, - "from": "node_modules/react-focus-lock/dist/cjs/interfaces.d.ts:ReactFocusLockProps", + "from": "node_modules/@epam/uui-react-focus-lock-fork/dist/cjs/interfaces.d.ts:ReactFocusLockProps", "required": false }, { @@ -78268,7 +78268,7 @@ "raw": "boolean | FocusOptions | (returnTo: Element) => boolean | FocusOptions", "html": "boolean | FocusOptions | (returnTo: Element) => boolean | FocusOptions" }, - "from": "node_modules/react-focus-lock/dist/cjs/interfaces.d.ts:ReactFocusLockProps", + "from": "node_modules/@epam/uui-react-focus-lock-fork/dist/cjs/interfaces.d.ts:ReactFocusLockProps", "required": false }, { @@ -81954,7 +81954,7 @@ "raw": "(HTMLElement | React.RefObject)[]", "html": "(HTMLElement | React.RefObject<any>)[]" }, - "from": "node_modules/react-focus-lock/dist/cjs/interfaces.d.ts:ReactFocusLockProps", + "from": "node_modules/@epam/uui-react-focus-lock-fork/dist/cjs/interfaces.d.ts:ReactFocusLockProps", "required": false } ], @@ -121330,14 +121330,14 @@ "exported": false } }, - "node_modules/react-focus-lock/dist/cjs/interfaces.d.ts:ReactFocusLockProps": { + "node_modules/@epam/uui-react-focus-lock-fork/dist/cjs/interfaces.d.ts:ReactFocusLockProps": { "summary": { - "module": "node_modules/react-focus-lock/dist/cjs/interfaces.d.ts", + "module": "node_modules/@epam/uui-react-focus-lock-fork/dist/cjs/interfaces.d.ts", "typeName": { "name": "ReactFocusLockProps", "nameFull": "ReactFocusLockProps" }, - "src": "node_modules/react-focus-lock/dist/cjs/interfaces.d.ts", + "src": "node_modules/@epam/uui-react-focus-lock-fork/dist/cjs/interfaces.d.ts", "exported": false } }, diff --git a/test-utils/src/jsdom/setupJsDom.js b/test-utils/src/jsdom/setupJsDom.js index c2e26fdd7f..f2a044b360 100644 --- a/test-utils/src/jsdom/setupJsDom.js +++ b/test-utils/src/jsdom/setupJsDom.js @@ -90,8 +90,8 @@ function enableMockForCommon3rdPartyDeps() { }; }); - testRunner.mock('react-focus-lock', () => ({ - ...testRunner.requireActual('react-focus-lock'), + testRunner.mock('@epam/uui-react-focus-lock-fork', () => ({ + ...testRunner.requireActual('@epam/uui-react-focus-lock-fork'), __esModule: true, /** * @param {object} props - Component's props diff --git a/uui-components/package.json b/uui-components/package.json index 4de0fd378d..40b87eaa30 100644 --- a/uui-components/package.json +++ b/uui-components/package.json @@ -20,7 +20,7 @@ "classnames": "2.2.6", "dayjs": "1.11.12", "react-fast-compare": "^3.2.2", - "react-focus-lock": "2.13.5", + "@epam/uui-react-focus-lock-fork": "2.13.8", "react-transition-group": "4.4.5" }, "peerDependencies": { diff --git a/uui-components/src/navigation/MainMenu/Burger/Burger.tsx b/uui-components/src/navigation/MainMenu/Burger/Burger.tsx index bc6637b205..c6ad5901d8 100644 --- a/uui-components/src/navigation/MainMenu/Burger/Burger.tsx +++ b/uui-components/src/navigation/MainMenu/Burger/Burger.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import cx from 'classnames'; -import FocusLock from 'react-focus-lock'; +import FocusLock from '@epam/uui-react-focus-lock-fork'; import { IHasCX, Icon, IHasRawProps, IHasForwardedRef, } from '@epam/uui-core'; diff --git a/uui-components/src/overlays/Dropdown.tsx b/uui-components/src/overlays/Dropdown.tsx index 67a200882b..7dc0ac9556 100644 --- a/uui-components/src/overlays/Dropdown.tsx +++ b/uui-components/src/overlays/Dropdown.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useLayoutEffect, useMemo, useContext, us import { useFloating, autoUpdate, flip, shift, useMergeRefs, hide, arrow, useDismiss, } from '@floating-ui/react'; -import { FreeFocusInside } from 'react-focus-lock'; +import { FreeFocusInside } from '@epam/uui-react-focus-lock-fork'; import { isEventTargetInsideClickable, UuiContext } from '@epam/uui-core'; import type { LayoutLayer, DropdownProps } from '@epam/uui-core'; import { getFallbackPlacements } from '../helpers'; diff --git a/uui-components/src/overlays/DropdownContainer.tsx b/uui-components/src/overlays/DropdownContainer.tsx index b2a9df9916..3710c7c3ce 100644 --- a/uui-components/src/overlays/DropdownContainer.tsx +++ b/uui-components/src/overlays/DropdownContainer.tsx @@ -1,12 +1,11 @@ import * as React from 'react'; -import FocusLock from 'react-focus-lock'; +import FocusLock, { ReactFocusLockProps } from '@epam/uui-react-focus-lock-fork'; import { uuiElement, IHasCX, IHasChildren, cx, IHasRawProps, uuiMarkers, IHasForwardedRef, IDropdownBodyProps, IHasStyleAttrs, } from '@epam/uui-core'; import { VPanel } from '../layout/flexItems/VPanel'; import PopoverArrow from './PopoverArrow'; -import { ReactFocusLockProps } from 'react-focus-lock'; export interface DropdownContainerProps extends IHasCX, @@ -60,7 +59,7 @@ export const DropdownContainer = React.forwardRef((props: DropdownContainerProps function renderDropdownContainer() { return ( : undefined } + forwardedRef={ !focusLock ? (ref as React.ForwardedRef) : undefined } cx={ cx(uuiElement.dropdownBody, uuiMarkers.lockFocus, props.cx) } style={ { ...props.style, @@ -91,7 +90,7 @@ export const DropdownContainer = React.forwardRef((props: DropdownContainerProps persistentFocus={ persistentFocus } lockProps={ { ...({ onKeyDown: props?.onKeyDown }), ...props.lockProps } } shards={ props.shards } - autoFocus={ props.autoFocus || true } + autoFocus={ props.autoFocus ?? true } as={ props.as } > {renderDropdownContainer()} diff --git a/uui-components/src/overlays/ModalBlocker.tsx b/uui-components/src/overlays/ModalBlocker.tsx index 45ab1dd6f1..be9ccd3d0b 100644 --- a/uui-components/src/overlays/ModalBlocker.tsx +++ b/uui-components/src/overlays/ModalBlocker.tsx @@ -1,5 +1,5 @@ import React, { useContext, useEffect } from 'react'; -import FocusLock from 'react-focus-lock'; +import FocusLock from '@epam/uui-react-focus-lock-fork'; import css from './ModalBlocker.module.scss'; import { ModalBlockerProps, UuiContext, cx, uuiElement } from '@epam/uui-core'; diff --git a/uui/components/navigation/MainMenu/MainMenuDropdown.tsx b/uui/components/navigation/MainMenu/MainMenuDropdown.tsx index 91075d70d5..f082370d27 100644 --- a/uui/components/navigation/MainMenu/MainMenuDropdown.tsx +++ b/uui/components/navigation/MainMenu/MainMenuDropdown.tsx @@ -1,5 +1,5 @@ import React, { KeyboardEvent } from 'react'; -import FocusLock from 'react-focus-lock'; +import FocusLock from '@epam/uui-react-focus-lock-fork'; import cx from 'classnames'; import { Dropdown, MainMenuDropdownProps } from '@epam/uui-components'; import { MainMenuButton } from './MainMenuButton'; diff --git a/uui/components/navigation/MainMenu/__tests__/__snapshots__/MainMenuDropdown.test.tsx.snap b/uui/components/navigation/MainMenu/__tests__/__snapshots__/MainMenuDropdown.test.tsx.snap index 1fccc9fda0..4a85a2a090 100644 --- a/uui/components/navigation/MainMenu/__tests__/__snapshots__/MainMenuDropdown.test.tsx.snap +++ b/uui/components/navigation/MainMenu/__tests__/__snapshots__/MainMenuDropdown.test.tsx.snap @@ -32,54 +32,40 @@ exports[`MainMenuDropdown should be rendered correctly in opened state 1`] = ` style="position: fixed; top: 0px; left: 0px; z-index: 2000;" >
-