1
0
mirror of https://gitlab.nic.cz/turris/reforis/foris-js.git synced 2024-11-14 17:35:35 +01:00

Compare commits

..

2 Commits

Author SHA1 Message Date
Aleksandr Gumroian
29a7c491a4 Merge branch 'modal-focus-trap' into 'dev'
Refactor Modal component to use useFocusTrap hook

See merge request turris/reforis/foris-js!237
2024-09-16 18:00:48 +02:00
Aleksandr Gumroian
446ec1bbe5
Refactor Modal component to use useFocusTrap hook 2024-09-16 18:00:37 +02:00
5 changed files with 35 additions and 43 deletions

View File

@ -5,12 +5,10 @@
* See /LICENSE for more information.
*/
import React, { useRef } from "react";
import React from "react";
import PropTypes from "prop-types";
import { useFocusTrap } from "../utils/hooks";
export const ALERT_TYPES = Object.freeze({
PRIMARY: "primary",
SECONDARY: "secondary",
@ -39,15 +37,11 @@ Alert.defaultProps = {
};
function Alert({ type, onDismiss, children }) {
const alertRef = useRef();
useFocusTrap(alertRef, !!onDismiss);
return (
<div
ref={alertRef}
className={`alert alert-${type} ${
onDismiss ? "alert-dismissible" : ""
}`.trim()}
role="alert"
>
{onDismiss && (
<button

View File

@ -33,7 +33,7 @@ export function Modal({ shown, setShown, scrollable, size, children }) {
let modalSize = "modal-";
useClickOutside(modalRef, () => setShown(false));
useFocusTrap(modalRef, shown);
useFocusTrap(modalRef);
useEffect(() => {
const handleEsc = (event) => {
@ -46,7 +46,7 @@ export function Modal({ shown, setShown, scrollable, size, children }) {
return () => {
window.removeEventListener("keydown", handleEsc);
};
}, [setShown]);
}, [shown]);
switch (size) {
case "sm":

View File

@ -6,8 +6,6 @@ exports[`<RebootButton/> Render modal. 1`] = `
id="modal-container"
>
<div
aria-labelledby="modal-title"
aria-modal="true"
class="modal fade show"
role="dialog"
>
@ -21,11 +19,11 @@ exports[`<RebootButton/> Render modal. 1`] = `
<div
class="modal-header"
>
<h1
class="modal-title fs-5"
<h5
class="modal-title"
>
Warning!
</h1>
</h5>
<button
aria-label="Close"
class="btn-close"

View File

@ -7,7 +7,6 @@ exports[`AlertContext should render alert 1`] = `
>
<div
class="alert alert-danger alert-dismissible"
role="alert"
>
<button
aria-label="Close"

View File

@ -41,39 +41,40 @@ export function useClickOutside(element, callback) {
});
}
/* Trap focus inside element. */
export function useFocusTrap(elementRef, condition = true) {
/* Trap focus inside modal. */
export function useFocusTrap(modalRef) {
useEffect(() => {
if (!condition) {
return;
}
const currentElement = elementRef.current;
const focusableElements = currentElement.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
if (shown) {
const modal = modalRef.current;
const focusableElements = modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
const handleTab = (event) => {
if (event.key === "Tab") {
if (event.shiftKey) {
if (document.activeElement === firstElement) {
lastElement.focus();
event.preventDefault();
const handleTab = (event) => {
if (event.key === "Tab") {
if (event.shiftKey) {
if (document.activeElement === firstElement) {
lastElement.focus();
event.preventDefault();
}
} else {
if (document.activeElement === lastElement) {
firstElement.focus();
event.preventDefault();
}
}
} else if (document.activeElement === lastElement) {
firstElement.focus();
event.preventDefault();
}
}
};
};
currentElement.addEventListener("keydown", handleTab);
modal.addEventListener("keydown", handleTab);
firstElement.focus();
firstElement.focus();
return () => {
currentElement.removeEventListener("keydown", handleTab);
};
}, [elementRef, condition]);
return () => {
modal.removeEventListener("keydown", handleTab);
};
}
}, [shown]);
}