Feature/use focus trap (#3075)

* addded tab order for modal elements

* fixed Tab Order

* feat: Add useFocusTrap hook for managing focus within modals

* chore: improvements

* chore: removed console log

---------

Co-authored-by: Srikar <srikar.y.12@gmail.com>
Co-authored-by: srikary12 <121927567+srikary12@users.noreply.github.com>
Co-authored-by: Anoop M D <anoop.md1421@gmail.com>
This commit is contained in:
Pragadesh-45 2024-09-13 11:42:55 +05:30 committed by GitHub
parent 776866b3b4
commit a08573f120
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 55 additions and 3 deletions

View File

@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState, useRef } from 'react';
import StyledWrapper from './StyledWrapper'; import StyledWrapper from './StyledWrapper';
import useFocusTrap from 'hooks/useFocusTrap';
const ModalHeader = ({ title, handleCancel, customHeader, hideClose }) => ( const ModalHeader = ({ title, handleCancel, customHeader, hideClose }) => (
<div className="bruno-modal-header"> <div className="bruno-modal-header">
@ -69,6 +70,7 @@ const Modal = ({
onClick, onClick,
closeModalFadeTimeout = 500 closeModalFadeTimeout = 500
}) => { }) => {
const modalRef = useRef(null);
const [isClosing, setIsClosing] = useState(false); const [isClosing, setIsClosing] = useState(false);
const escFunction = (event) => { const escFunction = (event) => {
const escKeyCode = 27; const escKeyCode = 27;
@ -77,6 +79,8 @@ const Modal = ({
} }
}; };
useFocusTrap(modalRef);
const closeModal = (args) => { const closeModal = (args) => {
setIsClosing(true); setIsClosing(true);
setTimeout(() => handleCancel(args), closeModalFadeTimeout); setTimeout(() => handleCancel(args), closeModalFadeTimeout);
@ -85,7 +89,6 @@ const Modal = ({
useEffect(() => { useEffect(() => {
if (disableEscapeKey) return; if (disableEscapeKey) return;
document.addEventListener('keydown', escFunction, false); document.addEventListener('keydown', escFunction, false);
return () => { return () => {
document.removeEventListener('keydown', escFunction, false); document.removeEventListener('keydown', escFunction, false);
}; };
@ -100,7 +103,13 @@ const Modal = ({
} }
return ( return (
<StyledWrapper className={classes} onClick={onClick ? (e) => onClick(e) : null}> <StyledWrapper className={classes} onClick={onClick ? (e) => onClick(e) : null}>
<div className={`bruno-modal-card modal-${size}`}> <div
className={`bruno-modal-card modal-${size}`}
ref={modalRef}
role="dialog"
aria-labelledby="modal-title"
aria-describedby="modal-description"
>
<ModalHeader <ModalHeader
title={title} title={title}
hideClose={hideClose} hideClose={hideClose}

View File

@ -0,0 +1,43 @@
import { useEffect, useRef } from 'react';
const useFocusTrap = (modalRef) => {
const firstFocusableElementRef = useRef(null);
const lastFocusableElementRef = useRef(null);
useEffect(() => {
const modalElement = modalRef.current;
if (!modalElement) return;
const focusableElements = modalElement.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
if (focusableElements.length === 0) return;
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
firstFocusableElementRef.current = firstElement;
lastFocusableElementRef.current = lastElement;
const handleKeyDown = (event) => {
if (event.key === 'Tab') {
if (event.shiftKey && document.activeElement === firstElement) {
event.preventDefault();
lastElement.focus();
} else if (!event.shiftKey && document.activeElement === lastElement) {
event.preventDefault();
firstElement.focus();
}
}
};
modalElement.addEventListener('keydown', handleKeyDown);
return () => {
modalElement.removeEventListener('keydown', handleKeyDown);
};
}, [modalRef]);
};
export default useFocusTrap;