mirror of
https://github.com/usebruno/bruno.git
synced 2024-11-24 17:03:47 +01:00
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:
parent
776866b3b4
commit
a08573f120
@ -1,5 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import useFocusTrap from 'hooks/useFocusTrap';
|
||||
|
||||
const ModalHeader = ({ title, handleCancel, customHeader, hideClose }) => (
|
||||
<div className="bruno-modal-header">
|
||||
@ -69,6 +70,7 @@ const Modal = ({
|
||||
onClick,
|
||||
closeModalFadeTimeout = 500
|
||||
}) => {
|
||||
const modalRef = useRef(null);
|
||||
const [isClosing, setIsClosing] = useState(false);
|
||||
const escFunction = (event) => {
|
||||
const escKeyCode = 27;
|
||||
@ -77,6 +79,8 @@ const Modal = ({
|
||||
}
|
||||
};
|
||||
|
||||
useFocusTrap(modalRef);
|
||||
|
||||
const closeModal = (args) => {
|
||||
setIsClosing(true);
|
||||
setTimeout(() => handleCancel(args), closeModalFadeTimeout);
|
||||
@ -85,7 +89,6 @@ const Modal = ({
|
||||
useEffect(() => {
|
||||
if (disableEscapeKey) return;
|
||||
document.addEventListener('keydown', escFunction, false);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('keydown', escFunction, false);
|
||||
};
|
||||
@ -100,7 +103,13 @@ const Modal = ({
|
||||
}
|
||||
return (
|
||||
<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
|
||||
title={title}
|
||||
hideClose={hideClose}
|
||||
|
43
packages/bruno-app/src/hooks/useFocusTrap/index.js
Normal file
43
packages/bruno-app/src/hooks/useFocusTrap/index.js
Normal 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;
|
Loading…
Reference in New Issue
Block a user