mirror of
https://github.com/usebruno/bruno.git
synced 2024-11-22 07:53:34 +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 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}
|
||||||
|
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