1
0
mirror of https://gitlab.nic.cz/turris/reforis/foris-js.git synced 2025-06-18 13:56:17 +02:00

Compare commits

..

2 Commits

Author SHA1 Message Date
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
446ec1bbe5 Refactor Modal component to use useFocusTrap hook 2024-09-16 18:00:37 +02:00
10 changed files with 41 additions and 65 deletions

View File

@ -8,19 +8,6 @@ and this project adheres to
## [Unreleased] ## [Unreleased]
## [6.2.0] - 2024-09-20
### Added
- Added useFocusTrap hook
- Added extendSession endpoint
### Changed
- Refactored Spinner.css to use CSS variable for color
- Refactored Modal component to use useFocusTrap hook
- Refactored Alert component to use useFocusTrap hook
## [6.1.1] - 2024-08-30 ## [6.1.1] - 2024-08-30
### Added ### Added
@ -374,8 +361,7 @@ and this project adheres to
## [0.0.7] - 2019-09-02 ## [0.0.7] - 2019-09-02
[unreleased]: [unreleased]:
https://gitlab.nic.cz/turris/reforis/foris-js/-/compare/v6.2.0...dev https://gitlab.nic.cz/turris/reforis/foris-js/-/compare/v6.1.1...master
[6.2.0]: https://gitlab.nic.cz/turris/reforis/foris-js/-/compare/v6.1.1...v6.2.0
[6.1.1]: https://gitlab.nic.cz/turris/reforis/foris-js/-/compare/v6.1.0...v6.1.1 [6.1.1]: https://gitlab.nic.cz/turris/reforis/foris-js/-/compare/v6.1.0...v6.1.1
[6.1.0]: https://gitlab.nic.cz/turris/reforis/foris-js/-/compare/v6.0.3...v6.1.0 [6.1.0]: https://gitlab.nic.cz/turris/reforis/foris-js/-/compare/v6.0.3...v6.1.0
[6.0.3]: https://gitlab.nic.cz/turris/reforis/foris-js/-/compare/v6.0.2...v6.0.3 [6.0.3]: https://gitlab.nic.cz/turris/reforis/foris-js/-/compare/v6.0.2...v6.0.3

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "foris", "name": "foris",
"version": "6.2.0", "version": "6.1.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "foris", "name": "foris",
"version": "6.2.0", "version": "6.1.1",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.6.0", "@fortawesome/fontawesome-svg-core": "^6.6.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "foris", "name": "foris",
"version": "6.2.0", "version": "6.1.1",
"description": "Foris JS library is a set of components and utils for reForis application and plugins.", "description": "Foris JS library is a set of components and utils for reForis application and plugins.",
"author": "CZ.NIC, z.s.p.o.", "author": "CZ.NIC, z.s.p.o.",
"repository": { "repository": {

View File

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

View File

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

View File

@ -9,7 +9,7 @@
.spinner-wrapper .spinner-border { .spinner-wrapper .spinner-border {
width: 4rem; width: 4rem;
height: 4rem; height: 4rem;
color: var(--bs-primary); color: #00a2e2;
} }
.spinner-fs-background { .spinner-fs-background {

View File

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

View File

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

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://www.nic.cz/) * Copyright (C) 2019-2021 CZ.NIC z.s.p.o. (http://www.nic.cz/)
* *
* This is free software, licensed under the GNU General Public License v3. * This is free software, licensed under the GNU General Public License v3.
* See /LICENSE for more information. * See /LICENSE for more information.
@ -9,10 +9,8 @@ export const REFORIS_URL_PREFIX = "/reforis";
export const REFORIS_API_URL_PREFIX = `${REFORIS_URL_PREFIX}/api`; export const REFORIS_API_URL_PREFIX = `${REFORIS_URL_PREFIX}/api`;
export const ForisURLs = { export const ForisURLs = {
// turris-auth
login: `/login?${REFORIS_URL_PREFIX}/`, login: `/login?${REFORIS_URL_PREFIX}/`,
logout: `/logout`, logout: `/logout`,
extendSession: `/extend-session`,
static: `${REFORIS_URL_PREFIX}/static/reforis`, static: `${REFORIS_URL_PREFIX}/static/reforis`,
wifi: `${REFORIS_URL_PREFIX}/network-settings/wifi`, wifi: `${REFORIS_URL_PREFIX}/network-settings/wifi`,

View File

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