mirror of
				https://gitlab.nic.cz/turris/reforis/foris-js.git
				synced 2025-11-03 23:00:31 +01:00 
			
		
		
		
	Merge branch 'dev' into 'master'
Dev See merge request turris/reforis/foris-js!240
This commit is contained in:
		
							
								
								
									
										16
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -8,6 +8,19 @@ 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
 | 
				
			||||||
@@ -361,7 +374,8 @@ 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.1.1...master
 | 
					    https://gitlab.nic.cz/turris/reforis/foris-js/-/compare/v6.2.0...dev
 | 
				
			||||||
 | 
					[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
									
									
									
								
							
							
						
						
									
										4
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -1,12 +1,12 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    "name": "foris",
 | 
					    "name": "foris",
 | 
				
			||||||
    "version": "6.1.1",
 | 
					    "version": "6.2.0",
 | 
				
			||||||
    "lockfileVersion": 2,
 | 
					    "lockfileVersion": 2,
 | 
				
			||||||
    "requires": true,
 | 
					    "requires": true,
 | 
				
			||||||
    "packages": {
 | 
					    "packages": {
 | 
				
			||||||
        "": {
 | 
					        "": {
 | 
				
			||||||
            "name": "foris",
 | 
					            "name": "foris",
 | 
				
			||||||
            "version": "6.1.1",
 | 
					            "version": "6.2.0",
 | 
				
			||||||
            "license": "GPL-3.0",
 | 
					            "license": "GPL-3.0",
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@fortawesome/fontawesome-svg-core": "^6.6.0",
 | 
					                "@fortawesome/fontawesome-svg-core": "^6.6.0",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    "name": "foris",
 | 
					    "name": "foris",
 | 
				
			||||||
    "version": "6.1.1",
 | 
					    "version": "6.2.0",
 | 
				
			||||||
    "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": {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,10 +5,12 @@
 | 
				
			|||||||
 * See /LICENSE for more information.
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import React from "react";
 | 
					import React, { useRef } 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",
 | 
				
			||||||
@@ -37,11 +39,15 @@ 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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ import React, { useRef, useEffect } from "react";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import PropTypes from "prop-types";
 | 
					import PropTypes from "prop-types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { useClickOutside } from "../utils/hooks";
 | 
					import { useClickOutside, useFocusTrap } from "../utils/hooks";
 | 
				
			||||||
import Portal from "../utils/Portal";
 | 
					import Portal from "../utils/Portal";
 | 
				
			||||||
import "./Modal.css";
 | 
					import "./Modal.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -29,10 +29,11 @@ Modal.propTypes = {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function Modal({ shown, setShown, scrollable, size, children }) {
 | 
					export function Modal({ shown, setShown, scrollable, size, children }) {
 | 
				
			||||||
    const dialogRef = useRef();
 | 
					    const modalRef = useRef();
 | 
				
			||||||
    let modalSize = "modal-";
 | 
					    let modalSize = "modal-";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useClickOutside(dialogRef, () => setShown(false));
 | 
					    useClickOutside(modalRef, () => setShown(false));
 | 
				
			||||||
 | 
					    useFocusTrap(modalRef, shown);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        const handleEsc = (event) => {
 | 
					        const handleEsc = (event) => {
 | 
				
			||||||
@@ -65,11 +66,13 @@ export function Modal({ shown, setShown, scrollable, size, children }) {
 | 
				
			|||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Portal containerId="modal-container">
 | 
					        <Portal containerId="modal-container">
 | 
				
			||||||
            <div
 | 
					            <div
 | 
				
			||||||
 | 
					                ref={modalRef}
 | 
				
			||||||
                className={`modal fade ${shown ? "show" : ""}`.trim()}
 | 
					                className={`modal fade ${shown ? "show" : ""}`.trim()}
 | 
				
			||||||
                role="dialog"
 | 
					                role="dialog"
 | 
				
			||||||
 | 
					                aria-modal="true"
 | 
				
			||||||
 | 
					                aria-labelledby="modal-title"
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
                <div
 | 
					                <div
 | 
				
			||||||
                    ref={dialogRef}
 | 
					 | 
				
			||||||
                    className={`${modalSize.trim()} modal-dialog modal-dialog-centered ${
 | 
					                    className={`${modalSize.trim()} modal-dialog modal-dialog-centered ${
 | 
				
			||||||
                        scrollable ? "modal-dialog-scrollable" : ""
 | 
					                        scrollable ? "modal-dialog-scrollable" : ""
 | 
				
			||||||
                    }`.trim()}
 | 
					                    }`.trim()}
 | 
				
			||||||
@@ -90,7 +93,7 @@ ModalHeader.propTypes = {
 | 
				
			|||||||
export function ModalHeader({ setShown, title }) {
 | 
					export function ModalHeader({ setShown, title }) {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <div className="modal-header">
 | 
					        <div className="modal-header">
 | 
				
			||||||
            <h5 className="modal-title">{title}</h5>
 | 
					            <h1 className="modal-title fs-5">{title}</h1>
 | 
				
			||||||
            <button
 | 
					            <button
 | 
				
			||||||
                type="button"
 | 
					                type="button"
 | 
				
			||||||
                className="btn-close"
 | 
					                className="btn-close"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@
 | 
				
			|||||||
.spinner-wrapper .spinner-border {
 | 
					.spinner-wrapper .spinner-border {
 | 
				
			||||||
    width: 4rem;
 | 
					    width: 4rem;
 | 
				
			||||||
    height: 4rem;
 | 
					    height: 4rem;
 | 
				
			||||||
    color: #00a2e2;
 | 
					    color: var(--bs-primary);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.spinner-fs-background {
 | 
					.spinner-fs-background {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,8 @@ 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"
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
@@ -19,11 +21,11 @@ exports[`<RebootButton/> Render modal. 1`] = `
 | 
				
			|||||||
          <div
 | 
					          <div
 | 
				
			||||||
            class="modal-header"
 | 
					            class="modal-header"
 | 
				
			||||||
          >
 | 
					          >
 | 
				
			||||||
            <h5
 | 
					            <h1
 | 
				
			||||||
              class="modal-title"
 | 
					              class="modal-title fs-5"
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
              Warning!
 | 
					              Warning!
 | 
				
			||||||
            </h5>
 | 
					            </h1>
 | 
				
			||||||
            <button
 | 
					            <button
 | 
				
			||||||
              aria-label="Close"
 | 
					              aria-label="Close"
 | 
				
			||||||
              class="btn-close"
 | 
					              class="btn-close"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ 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"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * Copyright (C) 2019-2021 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
					 * Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://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,8 +9,10 @@ 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`,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,3 +40,40 @@ export function useClickOutside(element, callback) {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Trap focus inside element. */
 | 
				
			||||||
 | 
					export function useFocusTrap(elementRef, condition = true) {
 | 
				
			||||||
 | 
					    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];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        currentElement.addEventListener("keydown", handleTab);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        firstElement.focus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return () => {
 | 
				
			||||||
 | 
					            currentElement.removeEventListener("keydown", handleTab);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }, [elementRef, condition]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user