diff --git a/src/__internal__/focus-trap/focus-trap.component.js b/src/__internal__/focus-trap/focus-trap.component.js index 7dadb01b99..023439f350 100644 --- a/src/__internal__/focus-trap/focus-trap.component.js +++ b/src/__internal__/focus-trap/focus-trap.component.js @@ -1,4 +1,4 @@ -import React, { +import { useCallback, useEffect, useLayoutEffect, @@ -19,8 +19,8 @@ const FocusTrap = ({ autoFocus = true, focusFirstElement, bespokeTrap, + wrapperRef, }) => { - const ref = useRef(); const firstOpen = useRef(true); const [focusableElements, setFocusableElements] = useState(); const [firstElement, setFirstElement] = useState(); @@ -38,16 +38,20 @@ const FocusTrap = ({ ); useLayoutEffect(() => { - const elements = Array.from( - ref.current.querySelectorAll(defaultFocusableSelectors) - ).filter((el) => Number(el.tabIndex) !== -1); - - if (hasNewInputs(elements)) { - setFocusableElements(Array.from(elements)); - setFirstElement(elements[0]); - setLastElement(elements[elements.length - 1]); + if (wrapperRef) { + const ref = wrapperRef.current || wrapperRef; + + const elements = Array.from( + ref.querySelectorAll(defaultFocusableSelectors) + ).filter((el) => Number(el.tabIndex) !== -1); + + if (hasNewInputs(elements)) { + setFocusableElements(Array.from(elements)); + setFirstElement(elements[0]); + setLastElement(elements[elements.length - 1]); + } } - }, [children, hasNewInputs]); + }, [children, hasNewInputs, wrapperRef]); useEffect(() => { if (autoFocus && firstOpen.current && (focusFirstElement || firstElement)) { @@ -101,7 +105,7 @@ const FocusTrap = ({ }; }, [firstElement, lastElement, focusableElements, bespokeTrap]); - return
{children}
; + return children; }; FocusTrap.propTypes = { @@ -115,6 +119,8 @@ FocusTrap.propTypes = { ]), /** a custom callback that will override the default focus trap behaviour */ bespokeTrap: PropTypes.func, + /** a ref to the container wrapping the focusable elements */ + wrapperRef: PropTypes.shape({ current: PropTypes.any }), }; export default FocusTrap; diff --git a/src/__internal__/focus-trap/focus-trap.spec.js b/src/__internal__/focus-trap/focus-trap.spec.js index c1ef0c0870..d0fcb9b5a1 100644 --- a/src/__internal__/focus-trap/focus-trap.spec.js +++ b/src/__internal__/focus-trap/focus-trap.spec.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useRef } from "react"; import { mount } from "enzyme"; import FocusTrap from "./focus-trap.component"; @@ -9,6 +9,19 @@ import { jest.useFakeTimers(); +// eslint-disable-next-line +const MockComponent = ({ children, ...rest }) => { + const ref = useRef(); + + return ( + +
+ {children} +
+
+ ); +}; + describe("FocusTrap", () => { const element = document.createElement("div"); const htmlElement = document.body.appendChild(element); @@ -25,12 +38,10 @@ describe("FocusTrap", () => { beforeEach(() => { wrapper = mount( - -
- - -
-
, + + + + , { attachTo: htmlElement } ); }); @@ -56,12 +67,10 @@ describe("FocusTrap", () => { document.querySelectorAll("button")[0].focus() ); wrapper = mount( - -
- - -
-
, + + + + , { attachTo: htmlElement } ); }); @@ -116,12 +125,10 @@ describe("FocusTrap", () => { beforeEach(() => { bespokeFn = jest.fn(); mount( - -
- - -
-
, + + + + , { attachTo: htmlElement } ); }); @@ -151,12 +158,10 @@ describe("FocusTrap", () => { beforeEach(() => { wrapper = mount( - -
- - -
-
, + + + + , { attachTo: htmlElement } ); }); @@ -221,11 +226,9 @@ describe("FocusTrap", () => { beforeEach(() => { wrapper = mount( - -
-

Test content

-
-
, + +

Test content

+
, { attachTo: htmlElement } ); }); @@ -248,18 +251,16 @@ describe("FocusTrap", () => { beforeEach(() => { wrapper = mount( - -
- - - - -
-
, + + + + + + , { attachTo: htmlElement } ); }); @@ -283,31 +284,25 @@ describe("FocusTrap", () => { beforeEach(() => { wrapper = mount( - -
- jest.fn()} - value="1" - legendWidth={40} - > - - - - - -
-
, + + jest.fn()} + value="1" + legendWidth={40} + > + + + + + + , { attachTo: htmlElement } ); }); @@ -364,31 +359,25 @@ describe("FocusTrap", () => { beforeEach(() => { wrapper = mount( - -
- - jest.fn()} - value="1" - legendWidth={40} - > - - - - -
-
, + + + jest.fn()} + value="1" + legendWidth={40} + > + + + + + , { attachTo: htmlElement } ); }); diff --git a/src/components/dialog-full-screen/dialog-full-screen.component.js b/src/components/dialog-full-screen/dialog-full-screen.component.js index f8931e2375..2a8a5def74 100644 --- a/src/components/dialog-full-screen/dialog-full-screen.component.js +++ b/src/components/dialog-full-screen/dialog-full-screen.component.js @@ -102,7 +102,7 @@ class DialogFullScreen extends Modal { */ get modalHTML() { return ( - + { this._dialog = d; diff --git a/src/components/dialog/dialog.component.js b/src/components/dialog/dialog.component.js index 8505d21d05..84ed0472e3 100644 --- a/src/components/dialog/dialog.component.js +++ b/src/components/dialog/dialog.component.js @@ -266,6 +266,7 @@ class Dialog extends Modal { autoFocus={!this.props.disableAutoFocus} focusFirstElement={this.props.focusFirstElement} bespokeTrap={this.props.bespokeFocusTrap} + wrapperRef={this._dialog} > {this.renderDialog(dialogProps)} diff --git a/src/components/sidebar/sidebar.component.js b/src/components/sidebar/sidebar.component.js index ebb52a0dcb..753d82e9a6 100644 --- a/src/components/sidebar/sidebar.component.js +++ b/src/components/sidebar/sidebar.component.js @@ -68,7 +68,9 @@ class Sidebar extends Modal { if (this.props.enableBackgroundUI) { return this.renderSidebar(); } - return {this.renderSidebar()}; + return ( + {this.renderSidebar()} + ); } }