1
0
mirror of https://gitlab.nic.cz/turris/reforis/foris-js.git synced 2025-07-03 16:02:26 +02:00

Compare commits

..

16 Commits

Author SHA1 Message Date
61ff82ca26 Fix linting issues 2024-06-06 16:57:08 +02:00
01e1fca913 fixup! Update Snapshots 2024-06-06 16:45:53 +02:00
ff53e2487d fixup! Update dependencies in package.json 2024-06-06 16:45:41 +02:00
e1722fa3e2 Update Snapshots 2024-06-06 16:32:08 +02:00
0f1f98e70c NPM audit fix 2024-06-06 16:32:00 +02:00
35f484b371 Update dependencies in package.json 2024-06-06 16:27:50 +02:00
7057f6ee7d Bump v6.0.0 2024-06-06 16:21:34 +02:00
3fa5ab7c07 Merge branch 'remove-use-tooltip-hook' into 'dev'
Remove useTooltip hook

See merge request turris/reforis/foris-js!218
2024-04-30 13:39:29 +02:00
ff48d6ca36 Remove useTooltip hook 2024-04-30 13:37:25 +02:00
39257567d4 Merge branch 'update-bootstrap' into 'dev'
Update Bootstrap library to version 5.3.x

See merge request turris/reforis/foris-js!217
2024-04-29 15:28:09 +02:00
5ed48bf2a3 Update Snapshots 2024-04-29 15:19:22 +02:00
c8fbdc5bba Fix tests 2024-04-29 15:19:21 +02:00
46bd8edcea Add JS_DIR variable to Makefile 2024-04-29 15:19:20 +02:00
42fcc02d83 Update Bootstrap library to version 5.3.x 2024-04-29 15:19:20 +02:00
96785f0774 Merge branch 'fix-fullscreen-spinner' into 'dev'
Update Spinner.css styles for better positioning and responsiveness

See merge request turris/reforis/foris-js!216
2024-04-10 15:35:27 +02:00
7823bff6d9 Update Spinner.css styles for better positioning and responsiveness 2024-04-10 14:46:18 +02:00
52 changed files with 14390 additions and 15232 deletions

View File

@ -11,6 +11,7 @@ MSGID_BUGS_ADDRESS="tech.support@turris.cz"
DEV_PYTHON=python3 DEV_PYTHON=python3
VENV_NAME?=venv VENV_NAME?=venv
JS_DIR=js
VENV_BIN=$(shell pwd)/$(VENV_NAME)/bin VENV_BIN=$(shell pwd)/$(VENV_NAME)/bin
.PHONY: all .PHONY: all

View File

@ -19,6 +19,7 @@ module.exports = {
collectCoverageFrom: ["src/**/*.{js,jsx}"], collectCoverageFrom: ["src/**/*.{js,jsx}"],
coverageDirectory: "coverage", coverageDirectory: "coverage",
testPathIgnorePatterns: ["/node_modules/", "/__fixtures__/", "/dist/"], testPathIgnorePatterns: ["/node_modules/", "/__fixtures__/", "/dist/"],
testEnvironment: "jsdom",
verbose: false, verbose: false,
setupFilesAfterEnv: [ setupFilesAfterEnv: [
"@testing-library/react/cleanup-after-each", "@testing-library/react/cleanup-after-each",

28187
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "foris", "name": "foris",
"version": "5.6.1", "version": "6.0.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": {
@ -14,49 +14,50 @@
"license": "GPL-3.0", "license": "GPL-3.0",
"main": "./src/index.js", "main": "./src/index.js",
"dependencies": { "dependencies": {
"axios": "^0.21.1", "axios": "^1.7.2",
"immutability-helper": "3.0.1", "immutability-helper": "^3.1.1",
"moment": "^2.24.0", "moment": "^2.30.1",
"qrcode.react": "^1.0.1", "qrcode.react": "^3.1.0",
"react-datetime": "^3.1.1", "react-datetime": "^3.2.0",
"react-uid": "^2.2.0" "react-uid": "^2.3.3"
}, },
"peerDependencies": { "peerDependencies": {
"bootstrap": "^4.6.2", "bootstrap": "^5.3.3",
"prop-types": "15.8.1", "prop-types": "15.8.1",
"react": "16.9.0", "react": "16.9.0",
"react-dom": "16.9.0", "react-dom": "16.9.0",
"react-router-dom": "^5.1.2" "react-router-dom": "^5.1.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.12.10", "@babel/cli": "^7.24.7",
"@babel/core": "^7.9.0", "@babel/core": "^7.24.7",
"@babel/plugin-transform-runtime": "^7.9.0", "@babel/plugin-transform-runtime": "^7.24.7",
"@babel/preset-env": "^7.9.0", "@babel/preset-env": "^7.24.7",
"@babel/preset-react": "^7.9.4", "@babel/preset-react": "^7.24.7",
"@fortawesome/fontawesome-free": "^5.13.0", "@fortawesome/fontawesome-free": "^6.5.2",
"@testing-library/react": "^8.0.9", "@testing-library/react": "^8.0.9",
"babel-loader": "^8.1.0", "babel-loader": "^8.1.0",
"babel-polyfill": "^6.26.0", "babel-polyfill": "^6.26.0",
"bootstrap": "^4.6.2", "bootstrap": "^5.3.3",
"css-loader": "^5.2.4", "css-loader": "^5.2.4",
"eslint": "^6.8.0", "eslint": "^8.57.0",
"eslint-config-prettier": "^6.11.0", "eslint-config-prettier": "^9.1.0",
"eslint-config-reforis": "^1.0.0", "eslint-config-reforis": "^2.1.1",
"eslint-plugin-prettier": "^3.1.4", "eslint-plugin-prettier": "^5.1.3",
"file-loader": "^6.0.0", "file-loader": "^6.0.0",
"jest": "^25.2.0", "jest": "^29.7.0",
"jest-mock-axios": "^3.2.0", "jest-environment-jsdom": "^29.7.0",
"moment-timezone": "^0.5.34", "jest-mock-axios": "^4.7.3",
"prettier": "2.0.5", "moment-timezone": "^0.5.45",
"prettier": "^3.3.1",
"prop-types": "15.8.1", "prop-types": "15.8.1",
"react": "16.9.0", "react": "16.9.0",
"react-dom": "16.9.0", "react-dom": "16.9.0",
"react-router-dom": "^5.1.2", "react-router-dom": "^5.1.2",
"react-styleguidist": "^11.2.0", "react-styleguidist": "^12.0.1",
"snapshot-diff": "^0.7.0", "snapshot-diff": "^0.10.0",
"style-loader": "^1.2.1", "style-loader": "^1.2.1",
"webpack": "^5.68.0" "webpack": "^5.91.0"
}, },
"scripts": { "scripts": {
"lint": "eslint src", "lint": "eslint src",

View File

@ -111,9 +111,7 @@ const useAPIPatch = createAPIHook("PATCH");
const useAPIPut = createAPIHook("PUT"); const useAPIPut = createAPIHook("PUT");
const useAPIDelete = createAPIHook("DELETE"); const useAPIDelete = createAPIHook("DELETE");
export { useAPIGet, useAPIPost, useAPIPatch, useAPIPut, useAPIDelete }; function useAPIPolling(endpoint, until, delay = 1000) {
export function useAPIPolling(endpoint, delay = 1000, until) {
// delay ms // delay ms
const [state, setState] = useState({ state: API_STATE.INIT }); const [state, setState] = useState({ state: API_STATE.INIT });
const [getResponse, get] = useAPIGet(endpoint); const [getResponse, get] = useAPIGet(endpoint);
@ -133,3 +131,12 @@ export function useAPIPolling(endpoint, delay = 1000, until) {
return [state]; return [state];
} }
export {
useAPIGet,
useAPIPost,
useAPIPatch,
useAPIPut,
useAPIDelete,
useAPIPolling,
};

View File

@ -6,6 +6,7 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
export const ALERT_TYPES = Object.freeze({ export const ALERT_TYPES = Object.freeze({
@ -38,16 +39,17 @@ Alert.defaultProps = {
export function Alert({ type, onDismiss, children }) { export function Alert({ type, onDismiss, children }) {
return ( return (
<div <div
className={`alert ${ className={`alert alert-${type} ${
onDismiss ? "alert-dismissible" : "" onDismiss ? "alert-dismissible" : ""
} alert-${type}`} }`.trim()}
> >
{onDismiss ? ( {onDismiss && (
<button type="button" className="close" onClick={onDismiss}> <button
&times; type="button"
</button> className="btn-close"
) : ( onClick={onDismiss}
false aria-label={_("Close")}
/>
)} )}
{children} {children}
</div> </div>

View File

@ -6,6 +6,7 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
Button.propTypes = { Button.propTypes = {
@ -33,7 +34,7 @@ export function Button({
}) { }) {
let buttonClass = className ? `btn ${className}` : "btn btn-primary"; let buttonClass = className ? `btn ${className}` : "btn btn-primary";
if (forisFormSize) { if (forisFormSize) {
buttonClass = `${buttonClass} col-sm-12 col-md-3 col-lg-2`; buttonClass = `${buttonClass} col-12 col-md-3 col-lg-2`;
} }
return ( return (
@ -44,7 +45,7 @@ export function Button({
> >
{loading && ( {loading && (
<span <span
className="spinner-border spinner-border-sm mr-1" className="spinner-border spinner-border-sm me-1"
role="status" role="status"
aria-hidden="true" aria-hidden="true"
/> />

View File

@ -6,6 +6,7 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { useUID } from "react-uid"; import { useUID } from "react-uid";
@ -24,25 +25,24 @@ CheckBox.defaultProps = {
export function CheckBox({ label, helpText, disabled, ...props }) { export function CheckBox({ label, helpText, disabled, ...props }) {
const uid = useUID(); const uid = useUID();
return ( return (
<div className="form-group"> <div className="mb-3 form-check">
<div className="custom-control custom-checkbox "> <input
<input className="form-check-input"
className="custom-control-input" type="checkbox"
type="checkbox" id={uid}
id={uid} disabled={disabled}
disabled={disabled} {...props}
{...props} />
/> <label className="form-check-label" htmlFor={uid}>
<label className="custom-control-label" htmlFor={uid}> {label}
{label} </label>
{helpText && ( {helpText && (
<small className="form-text text-muted"> <div className="form-text">
{helpText} <small>{helpText}</small>
</small> </div>
)} )}
</label>
</div>
</div> </div>
); );
} }

View File

@ -6,7 +6,9 @@
*/ */
import React, { useState, useRef } from "react"; import React, { useState, useRef } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Input } from "./Input"; import { Input } from "./Input";
CopyInput.propTypes = { CopyInput.propTypes = {

View File

@ -6,9 +6,10 @@
*/ */
import React from "react"; import React from "react";
import moment from "moment/moment";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import Datetime from "react-datetime"; import Datetime from "react-datetime";
import moment from "moment/moment";
import "react-datetime/css/react-datetime.css"; import "react-datetime/css/react-datetime.css";
import "./DataTimeInput.css"; import "./DataTimeInput.css";
@ -46,13 +47,13 @@ export function DataTimeInput({
children, children,
...props ...props
}) { }) {
function renderInput(datetimeProps) { const renderInput = (datetimeProps) => {
return ( return (
<Input {...props} {...datetimeProps}> <Input {...props} {...datetimeProps}>
{children} {children}
</Input> </Input>
); );
} };
return ( return (
<Datetime <Datetime

View File

@ -6,6 +6,7 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
DownloadButton.propTypes = { DownloadButton.propTypes = {

View File

@ -6,11 +6,14 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Input } from "./Input"; import { Input } from "./Input";
export const EmailInput = ({ ...props }) => <Input type="email" {...props} />; export function EmailInput({ ...props }) {
return <Input type="email" {...props} />;
}
EmailInput.propTypes = { EmailInput.propTypes = {
/** Field label. */ /** Field label. */

View File

@ -8,6 +8,7 @@
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Input } from "./Input"; import { Input } from "./Input";
FileInput.propTypes = { FileInput.propTypes = {

View File

@ -6,11 +6,12 @@
*/ */
import React, { forwardRef } from "react"; import React, { forwardRef } from "react";
import { useUID } from "react-uid";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { useUID } from "react-uid";
/** Base bootstrap input component. */ /** Base bootstrap input component. */
export const Input = forwardRef( const Input = forwardRef(
( (
{ {
type, type,
@ -27,18 +28,21 @@ export const Input = forwardRef(
) => { ) => {
const uid = useUID(); const uid = useUID();
const inputClassName = `form-control ${className || ""} ${ const inputClassName = `${className || ""} ${
error ? "is-invalid" : "" error ? "is-invalid" : ""
}`.trim(); }`.trim();
return ( return (
<div className="form-group"> <div className="mb-3">
<label className={labelClassName} htmlFor={uid}> <label
className={`form-label ${labelClassName || ""}`.trim()}
htmlFor={uid}
>
{label} {label}
</label> </label>
<div className={`input-group ${groupClassName || ""}`.trim()}> <div className={`input-group ${groupClassName || ""}`.trim()}>
<input <input
className={inputClassName} className={`form-control ${inputClassName}`.trim()}
type={type} type={type}
id={uid} id={uid}
ref={ref} ref={ref}
@ -46,10 +50,12 @@ export const Input = forwardRef(
/> />
{children} {children}
</div> </div>
{error ? <div className="invalid-feedback">{error}</div> : null} {error && <div className="invalid-feedback">{error}</div>}
{helpText ? ( {helpText && (
<small className="form-text text-muted">{helpText}</small> <div className="form-text">
) : null} <small>{helpText}</small>
</div>
)}
</div> </div>
); );
} }
@ -68,3 +74,5 @@ Input.propTypes = {
labelClassName: PropTypes.string, labelClassName: PropTypes.string,
groupClassName: PropTypes.string, groupClassName: PropTypes.string,
}; };
export { Input };

View File

@ -6,10 +6,11 @@
*/ */
import React, { useRef, useEffect } from "react"; import React, { useRef, useEffect } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Portal } from "../utils/Portal";
import { useClickOutside } from "../utils/hooks"; import { useClickOutside } from "../utils/hooks";
import { Portal } from "../utils/Portal";
import "./Modal.css"; import "./Modal.css";
Modal.propTypes = { Modal.propTypes = {
@ -92,11 +93,10 @@ export function ModalHeader({ setShown, title }) {
<h5 className="modal-title">{title}</h5> <h5 className="modal-title">{title}</h5>
<button <button
type="button" type="button"
className="close" className="btn-close"
onClick={() => setShown(false)} onClick={() => setShown(false)}
> aria-label={_("Close")}
<span aria-hidden="true">&times;</span> />
</button>
</div> </div>
); );
} }

View File

@ -6,10 +6,11 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { useConditionalTimeout } from "../utils/hooks";
import { Input } from "./Input"; import { Input } from "./Input";
import { useConditionalTimeout } from "../utils/hooks";
import "./NumberInput.css"; import "./NumberInput.css";
NumberInput.propTypes = { NumberInput.propTypes = {
@ -23,7 +24,7 @@ NumberInput.propTypes = {
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/** Function called when value changes. */ /** Function called when value changes. */
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
/** Additional description dispaled to the right of input value. */ /** Additional description displayed to the right of input value. */
inlineText: PropTypes.string, inlineText: PropTypes.string,
}; };
@ -49,27 +50,27 @@ export function NumberInput({ onChange, inlineText, value, ...props }) {
return ( return (
<Input type="number" onChange={onChange} value={value} {...props}> <Input type="number" onChange={onChange} value={value} {...props}>
<div className="input-group-append"> {inlineText && (
{inlineText && <p className="input-group-text">{inlineText}</p>} <span className="input-group-text">{inlineText}</span>
<button )}
type="button" <button
className="btn btn-outline-secondary" type="button"
onMouseDown={() => enableIncrease(true)} className="btn btn-outline-secondary"
onMouseUp={() => enableIncrease(false)} onMouseDown={() => enableIncrease(true)}
aria-label="Increase" onMouseUp={() => enableIncrease(false)}
> aria-label="Increase"
<i className="fas fa-plus" /> >
</button> <i className="fas fa-plus" />
<button </button>
type="button" <button
className="btn btn-outline-secondary" type="button"
onMouseDown={() => enableDecrease(true)} className="btn btn-outline-secondary"
onMouseUp={() => enableDecrease(false)} onMouseDown={() => enableDecrease(true)}
aria-label="Decrease" onMouseUp={() => enableDecrease(false)}
> aria-label="Decrease"
<i className="fas fa-minus" /> >
</button> <i className="fas fa-minus" />
</div> </button>
</Input> </Input>
); );
} }

View File

@ -6,6 +6,7 @@
*/ */
import React, { useState } from "react"; import React, { useState } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Input } from "./Input"; import { Input } from "./Input";
@ -34,24 +35,20 @@ export function PasswordInput({ withEye, newPass, ...props }) {
autoComplete={newPass ? "new-password" : "current-password"} autoComplete={newPass ? "new-password" : "current-password"}
{...props} {...props}
> >
{withEye ? ( {withEye && (
<div className="input-group-append"> <button
<button type="button"
type="button" className="input-group-text"
className="input-group-text" onClick={(e) => {
onClick={(e) => { e.preventDefault();
e.preventDefault(); setHidden((shouldBeHidden) => !shouldBeHidden);
setHidden((shouldBeHidden) => !shouldBeHidden); }}
}} >
> <i
<i className={`fa ${isHidden ? "fa-eye" : "fa-eye-slash"}`}
className={`fa ${ />
isHidden ? "fa-eye" : "fa-eye-slash" </button>
}`} )}
/>
</button>
</div>
) : null}
</Input> </Input>
); );
} }

View File

@ -6,6 +6,7 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { useUID } from "react-uid"; import { useUID } from "react-uid";
@ -17,7 +18,7 @@ RadioSet.propTypes = {
/** Choices . */ /** Choices . */
choices: PropTypes.arrayOf( choices: PropTypes.arrayOf(
PropTypes.shape({ PropTypes.shape({
/** Choice lable . */ /** Choice label . */
label: PropTypes.oneOfType([ label: PropTypes.oneOfType([
PropTypes.string, PropTypes.string,
PropTypes.element, PropTypes.element,
@ -64,7 +65,7 @@ export function RadioSet({
}); });
return ( return (
<div className="form-group"> <div className="mb-3">
{label && ( {label && (
<label htmlFor={uid} className="d-block"> <label htmlFor={uid} className="d-block">
{label} {label}
@ -72,7 +73,9 @@ export function RadioSet({
)} )}
{radios} {radios}
{helpText && ( {helpText && (
<small className="form-text text-muted">{helpText}</small> <div className="form-text">
<small>{helpText}</small>
</div>
)} )}
</div> </div>
); );
@ -92,27 +95,25 @@ Radio.propTypes = {
export function Radio({ label, id, helpText, inline, ...props }) { export function Radio({ label, id, helpText, inline, ...props }) {
return ( return (
<> <div
<div className={`mb-2 ${
className={`custom-control custom-radio ${ inline ? "form-check form-check-inline" : ""
inline ? "custom-control-inline" : "" }`.trim()}
}`.trim()} >
> <input
<input id={id}
id={id} className="form-check-input me-2"
className="custom-control-input" type="radio"
type="radio" {...props}
{...props} />
/> <label className="form-check-label" htmlFor={id}>
<label className="custom-control-label" htmlFor={id}> {label}
{label}
</label>
{helpText && ( {helpText && (
<small className="form-text text-muted mt-0 mb-3"> <div className="form-text">
{helpText} <small>{helpText}</small>
</small> </div>
)} )}
</div> </label>
</> </div>
); );
} }

View File

@ -6,6 +6,7 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { useUID } from "react-uid"; import { useUID } from "react-uid";
@ -30,14 +31,18 @@ export function Select({ label, choices, helpText, ...props }) {
)); ));
return ( return (
<div className="form-group"> <div className="mb-3">
<label htmlFor={uid}>{label}</label> <label className="form-label" htmlFor={uid}>
<select className="custom-select" id={uid} {...props}> {label}
</label>
<select className="form-select" id={uid} {...props}>
{options} {options}
</select> </select>
{helpText ? ( {helpText && (
<small className="form-text text-muted">{helpText}</small> <div className="form-text">
) : null} <small>{helpText}</small>
</div>
)}
</div> </div>
); );
} }

View File

@ -1,3 +1,11 @@
.spinner-fs-wrapper {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1101; /* increase z-index by 1 to ensure it's on top of spinner-fs-background */
}
.spinner-wrapper .spinner-border { .spinner-wrapper .spinner-border {
width: 4rem; width: 4rem;
height: 4rem; height: 4rem;
@ -7,10 +15,8 @@
.spinner-fs-background { .spinner-fs-background {
background-color: rgba(2, 2, 2, 0.5); background-color: rgba(2, 2, 2, 0.5);
color: rgb(230, 230, 230); color: rgb(230, 230, 230);
position: fixed; width: 100vw;
width: 100%; height: 100vh;
height: 100%;
top: 0;
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -6,6 +6,7 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import "./Spinner.css"; import "./Spinner.css";
@ -16,7 +17,7 @@ Spinner.propTypes = {
PropTypes.arrayOf(PropTypes.node), PropTypes.arrayOf(PropTypes.node),
PropTypes.node, PropTypes.node,
]), ]),
/** Render component with full-screen mode (using apropriate `.css` styles) */ /** Render component with full-screen mode (using appropriate `.css` styles) */
fullScreen: PropTypes.bool.isRequired, fullScreen: PropTypes.bool.isRequired,
className: PropTypes.string, className: PropTypes.string,
}; };

View File

@ -6,6 +6,7 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { useUID } from "react-uid"; import { useUID } from "react-uid";
@ -22,28 +23,30 @@ Switch.propTypes = {
export function Switch({ label, helpText, switchHeading, ...props }) { export function Switch({ label, helpText, switchHeading, ...props }) {
const uid = useUID(); const uid = useUID();
return ( return (
<div className={`form-group ${switchHeading ? "switch" : ""}`.trim()}> <div
<div className={`form-check form-switch mb-3 ${
className={`custom-control custom-switch ${ switchHeading ? "d-flex align-items-center" : null
!helpText ? "custom-control-inline" : "" }`.trim()}
} ${switchHeading ? "switch-heading" : ""}`.trim()} >
> <input
<input type="checkbox"
type="checkbox" className={`form-check-input ${
className="custom-control-input" switchHeading ? "me-2" : ""
id={uid} }`.trim()}
{...props} role="switch"
/> id={uid}
<label className="custom-control-label" htmlFor={uid}> {...props}
{label} />
</label> <label className="form-check-label" htmlFor={uid}>
{helpText && ( {label}
<small className="form-text text-muted mt-0 mb-3"> </label>
{helpText} {helpText && (
</small> <div className="form-text">
)} <small>{helpText}</small>
</div> </div>
)}
</div> </div>
); );
} }

View File

@ -6,11 +6,14 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Input } from "./Input"; import { Input } from "./Input";
export const TextInput = ({ ...props }) => <Input type="text" {...props} />; export function TextInput({ ...props }) {
return <Input type="text" {...props} />;
}
TextInput.propTypes = { TextInput.propTypes = {
/** Field label. */ /** Field label. */

View File

@ -29,7 +29,7 @@ exports[`<Button /> Render button with spinner 1`] = `
> >
<span <span
aria-hidden="true" aria-hidden="true"
class="spinner-border spinner-border-sm mr-1" class="spinner-border spinner-border-sm me-1"
role="status" role="status"
/> />
<span> <span>

View File

@ -2,55 +2,51 @@
exports[`<Checkbox/> Render checkbox 1`] = ` exports[`<Checkbox/> Render checkbox 1`] = `
<div <div
class="form-group" class="mb-3 form-check"
> >
<div <input
class="custom-control custom-checkbox " checked=""
class="form-check-input"
id="1"
type="checkbox"
/>
<label
class="form-check-label"
for="1"
> >
<input Test label
checked="" </label>
class="custom-control-input" <div
id="1" class="form-text"
type="checkbox" >
/> <small>
<label Some help text
class="custom-control-label" </small>
for="1"
>
Test label
<small
class="form-text text-muted"
>
Some help text
</small>
</label>
</div> </div>
</div> </div>
`; `;
exports[`<Checkbox/> Render uncheked checkbox 1`] = ` exports[`<Checkbox/> Render uncheked checkbox 1`] = `
<div <div
class="form-group" class="mb-3 form-check"
> >
<div <input
class="custom-control custom-checkbox " class="form-check-input"
id="1"
type="checkbox"
/>
<label
class="form-check-label"
for="1"
> >
<input Test label
class="custom-control-input" </label>
id="1" <div
type="checkbox" class="form-text"
/> >
<label <small>
class="custom-control-label" Some help text
for="1" </small>
>
Test label
<small
class="form-text text-muted"
>
Some help text
</small>
</label>
</div> </div>
</div> </div>
`; `;

View File

@ -2,9 +2,10 @@
exports[`<NumberInput/> Render number input 1`] = ` exports[`<NumberInput/> Render number input 1`] = `
<div <div
class="form-group" class="mb-3"
> >
<label <label
class="form-label"
for="1" for="1"
> >
Test label Test label
@ -18,33 +19,31 @@ exports[`<NumberInput/> Render number input 1`] = `
type="number" type="number"
value="1" value="1"
/> />
<div <button
class="input-group-append" aria-label="Increase"
class="btn btn-outline-secondary"
type="button"
> >
<button <i
aria-label="Increase" class="fas fa-plus"
class="btn btn-outline-secondary" />
type="button" </button>
> <button
<i aria-label="Decrease"
class="fas fa-plus" class="btn btn-outline-secondary"
/> type="button"
</button> >
<button <i
aria-label="Decrease" class="fas fa-minus"
class="btn btn-outline-secondary" />
type="button" </button>
>
<i
class="fas fa-minus"
/>
</button>
</div>
</div> </div>
<small <div
class="form-text text-muted" class="form-text"
> >
Some help text <small>
</small> Some help text
</small>
</div>
</div> </div>
`; `;

View File

@ -2,9 +2,10 @@
exports[`<PasswordInput/> Render password input 1`] = ` exports[`<PasswordInput/> Render password input 1`] = `
<div <div
class="form-group" class="mb-3"
> >
<label <label
class="form-label"
for="1" for="1"
> >
Test label Test label
@ -20,10 +21,12 @@ exports[`<PasswordInput/> Render password input 1`] = `
value="Some password" value="Some password"
/> />
</div> </div>
<small <div
class="form-text text-muted" class="form-text"
> >
Some help text <small>
</small> Some help text
</small>
</div>
</div> </div>
`; `;

View File

@ -2,7 +2,7 @@
exports[`<RadioSet/> Render radio set 1`] = ` exports[`<RadioSet/> Render radio set 1`] = `
<div <div
class="form-group" class="mb-3"
> >
<label <label
class="d-block" class="d-block"
@ -11,61 +11,63 @@ exports[`<RadioSet/> Render radio set 1`] = `
Radios set label Radios set label
</label> </label>
<div <div
class="custom-control custom-radio" class="mb-2"
> >
<input <input
checked="" checked=""
class="custom-control-input" class="form-check-input me-2"
id="test_name-0" id="test_name-0"
name="test_name" name="test_name"
type="radio" type="radio"
value="value" value="value"
/> />
<label <label
class="custom-control-label" class="form-check-label"
for="test_name-0" for="test_name-0"
> >
label label
</label> </label>
</div> </div>
<div <div
class="custom-control custom-radio" class="mb-2"
> >
<input <input
class="custom-control-input" class="form-check-input me-2"
id="test_name-1" id="test_name-1"
name="test_name" name="test_name"
type="radio" type="radio"
value="another value" value="another value"
/> />
<label <label
class="custom-control-label" class="form-check-label"
for="test_name-1" for="test_name-1"
> >
another label another label
</label> </label>
</div> </div>
<div <div
class="custom-control custom-radio" class="mb-2"
> >
<input <input
class="custom-control-input" class="form-check-input me-2"
id="test_name-2" id="test_name-2"
name="test_name" name="test_name"
type="radio" type="radio"
value="another on value" value="another on value"
/> />
<label <label
class="custom-control-label" class="form-check-label"
for="test_name-2" for="test_name-2"
> >
another one label another one label
</label> </label>
</div> </div>
<small <div
class="form-text text-muted" class="form-text"
> >
Some help text <small>
</small> Some help text
</small>
</div>
</div> </div>
`; `;

View File

@ -3,15 +3,16 @@
exports[`<Select/> Test with snapshot. 1`] = ` exports[`<Select/> Test with snapshot. 1`] = `
<div> <div>
<div <div
class="form-group" class="mb-3"
> >
<label <label
class="form-label"
for="1" for="1"
> >
Test label Test label
</label> </label>
<select <select
class="custom-select" class="form-select"
id="1" id="1"
> >
<option <option
@ -30,11 +31,13 @@ exports[`<Select/> Test with snapshot. 1`] = `
three three
</option> </option>
</select> </select>
<small <div
class="form-text text-muted" class="form-text"
> >
Help text <small>
</small> Help text
</small>
</div>
</div> </div>
</div> </div>
`; `;

View File

@ -2,26 +2,25 @@
exports[`<Switch/> Render switch 1`] = ` exports[`<Switch/> Render switch 1`] = `
<div <div
class="form-group" class="form-check form-switch mb-3 null"
> >
<div <input
class="custom-control custom-switch" checked=""
class="form-check-input"
id="1"
role="switch"
type="checkbox"
/>
<label
class="form-check-label"
for="1"
> >
<input Test label
checked="" </label>
class="custom-control-input" <div
id="1" class="form-text"
type="checkbox" >
/> <small>
<label
class="custom-control-label"
for="1"
>
Test label
</label>
<small
class="form-text text-muted mt-0 mb-3"
>
Some help text Some help text
</small> </small>
</div> </div>
@ -30,25 +29,24 @@ exports[`<Switch/> Render switch 1`] = `
exports[`<Switch/> Render uncheked switch 1`] = ` exports[`<Switch/> Render uncheked switch 1`] = `
<div <div
class="form-group" class="form-check form-switch mb-3 null"
> >
<div <input
class="custom-control custom-switch" class="form-check-input"
id="1"
role="switch"
type="checkbox"
/>
<label
class="form-check-label"
for="1"
> >
<input Test label
class="custom-control-input" </label>
id="1" <div
type="checkbox" class="form-text"
/> >
<label <small>
class="custom-control-label"
for="1"
>
Test label
</label>
<small
class="form-text text-muted mt-0 mb-3"
>
Some help text Some help text
</small> </small>
</div> </div>

View File

@ -2,9 +2,10 @@
exports[`<TextInput/> Render text input 1`] = ` exports[`<TextInput/> Render text input 1`] = `
<div <div
class="form-group" class="mb-3"
> >
<label <label
class="form-label"
for="1" for="1"
> >
Test label Test label
@ -19,10 +20,12 @@ exports[`<TextInput/> Render text input 1`] = `
value="Some text" value="Some text"
/> />
</div> </div>
<small <div
class="form-text text-muted" class="form-text"
> >
Some help text <small>
</small> Some help text
</small>
</div>
</div> </div>
`; `;

View File

@ -6,15 +6,15 @@
*/ */
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { useAPIPost } from "../api/hooks"; import { useAPIPost } from "../api/hooks";
import { API_STATE } from "../api/utils"; import { API_STATE } from "../api/utils";
import { ForisURLs } from "../utils/forisUrls";
import { Button } from "../bootstrap/Button"; import { Button } from "../bootstrap/Button";
import { Modal, ModalHeader, ModalBody, ModalFooter } from "../bootstrap/Modal"; import { Modal, ModalHeader, ModalBody, ModalFooter } from "../bootstrap/Modal";
import { useAlert } from "../context/alertContext/AlertContext"; import { useAlert } from "../context/alertContext/AlertContext";
import { ForisURLs } from "../utils/forisUrls";
export function RebootButton(props) { export function RebootButton(props) {
const [triggered, setTriggered] = useState(false); const [triggered, setTriggered] = useState(false);

View File

@ -6,14 +6,15 @@
*/ */
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Button } from "../../bootstrap/Button";
import { useAlert } from "../../context/alertContext/AlertContext";
import { ALERT_TYPES } from "../../bootstrap/Alert";
import { useAPIPost } from "../../api/hooks"; import { useAPIPost } from "../../api/hooks";
import { API_STATE } from "../../api/utils"; import { API_STATE } from "../../api/utils";
import { ALERT_TYPES } from "../../bootstrap/Alert";
import { Button } from "../../bootstrap/Button";
import { formFieldsSize } from "../../bootstrap/constants"; import { formFieldsSize } from "../../bootstrap/constants";
import { useAlert } from "../../context/alertContext/AlertContext";
ResetWiFiSettings.propTypes = { ResetWiFiSettings.propTypes = {
ws: PropTypes.object.isRequired, ws: PropTypes.object.isRequired,
@ -58,7 +59,7 @@ export function ResetWiFiSettings({ ws, endpoint }) {
"If a number of wireless cards doesn't match, you may try to reset the Wi-Fi settings. Note that this will remove the current Wi-Fi configuration and restore the default values." "If a number of wireless cards doesn't match, you may try to reset the Wi-Fi settings. Note that this will remove the current Wi-Fi configuration and restore the default values."
)} )}
</p> </p>
<div className="text-right"> <div className="text-end">
<Button <Button
className="btn-primary" className="btn-primary"
forisFormSize forisFormSize

View File

@ -6,15 +6,17 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Switch } from "../../bootstrap/Switch";
import { HELP_TEXTS, HTMODES, HWMODES, ENCRYPTIONMODES } from "./constants";
import WifiGuestForm from "./WiFiGuestForm";
import WiFiQRCode from "./WiFiQRCode";
import { PasswordInput } from "../../bootstrap/PasswordInput"; import { PasswordInput } from "../../bootstrap/PasswordInput";
import { RadioSet } from "../../bootstrap/RadioSet"; import { RadioSet } from "../../bootstrap/RadioSet";
import { Select } from "../../bootstrap/Select"; import { Select } from "../../bootstrap/Select";
import { Switch } from "../../bootstrap/Switch";
import { TextInput } from "../../bootstrap/TextInput"; import { TextInput } from "../../bootstrap/TextInput";
import WiFiQRCode from "./WiFiQRCode";
import WifiGuestForm from "./WiFiGuestForm";
import { HELP_TEXTS, HTMODES, HWMODES, ENCRYPTIONMODES } from "./constants";
WiFiForm.propTypes = { WiFiForm.propTypes = {
formData: PropTypes.shape({ devices: PropTypes.arrayOf(PropTypes.object) }) formData: PropTypes.shape({ devices: PropTypes.arrayOf(PropTypes.object) })
@ -92,7 +94,7 @@ function DeviceForm({
return ( return (
<> <>
<Switch <Switch
label={<h2>{_(`Wi-Fi ${deviceID + 1}`)}</h2>} label={<h2 className="mb-0">{_(`Wi-Fi ${deviceID + 1}`)}</h2>}
checked={formData.enabled} checked={formData.enabled}
onChange={setFormValue((value) => ({ onChange={setFormValue((value) => ({
devices: { devices: {
@ -119,12 +121,10 @@ function DeviceForm({
}))} }))}
{...props} {...props}
> >
<div className="input-group-append"> <WiFiQRCode
<WiFiQRCode SSID={formData.SSID}
SSID={formData.SSID} password={formData.password}
password={formData.password} />
/>
</div>
</TextInput> </TextInput>
<PasswordInput <PasswordInput
@ -269,8 +269,8 @@ function getChannelChoices(device) {
channelChoices[availableChannel.number.toString()] = ` channelChoices[availableChannel.number.toString()] = `
${availableChannel.number} ${availableChannel.number}
(${availableChannel.frequency} MHz ${ (${availableChannel.frequency} MHz ${
availableChannel.radar ? " ,DFS" : "" availableChannel.radar ? " ,DFS" : ""
}) })
`; `;
}); });
}); });

View File

@ -6,13 +6,14 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { TextInput } from "../../bootstrap/TextInput";
import { Switch } from "../../bootstrap/Switch";
import { PasswordInput } from "../../bootstrap/PasswordInput";
import WiFiQRCode from "./WiFiQRCode";
import { HELP_TEXTS } from "./constants"; import { HELP_TEXTS } from "./constants";
import WiFiQRCode from "./WiFiQRCode";
import { PasswordInput } from "../../bootstrap/PasswordInput";
import { Switch } from "../../bootstrap/Switch";
import { TextInput } from "../../bootstrap/TextInput";
WifiGuestForm.propTypes = { WifiGuestForm.propTypes = {
formData: PropTypes.shape({ formData: PropTypes.shape({

View File

@ -6,10 +6,11 @@
*/ */
import React, { useState } from "react"; import React, { useState } from "react";
import QRCode from "qrcode.react";
import PropTypes from "prop-types";
import { ForisURLs } from "../../utils/forisUrls"; import PropTypes from "prop-types";
import QRCode from "qrcode.react";
import { createAndDownloadPdf, toQRCodeContent } from "./qrCodeHelpers";
import { Button } from "../../bootstrap/Button"; import { Button } from "../../bootstrap/Button";
import { import {
Modal, Modal,
@ -17,7 +18,7 @@ import {
ModalFooter, ModalFooter,
ModalHeader, ModalHeader,
} from "../../bootstrap/Modal"; } from "../../bootstrap/Modal";
import { createAndDownloadPdf, toQRCodeContent } from "./qrCodeHelpers"; import { ForisURLs } from "../../utils/forisUrls";
WiFiQRCode.propTypes = { WiFiQRCode.propTypes = {
SSID: PropTypes.string.isRequired, SSID: PropTypes.string.isRequired,
@ -87,7 +88,7 @@ function QRCodeModal({ shown, setShown, SSID, password }) {
createAndDownloadPdf(SSID, password); createAndDownloadPdf(SSID, password);
}} }}
> >
<i className="fas fa-arrow-down mr-2" /> <i className="fas fa-file-download me-2" />
{_("Download PDF")} {_("Download PDF")}
</Button> </Button>
</ModalFooter> </ModalFooter>

View File

@ -6,11 +6,12 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { ForisForm } from "../../form/components/ForisForm";
import WiFiForm from "./WiFiForm";
import { ResetWiFiSettings } from "./ResetWiFiSettings"; import { ResetWiFiSettings } from "./ResetWiFiSettings";
import WiFiForm from "./WiFiForm";
import { ForisForm } from "../../form/components/ForisForm";
WiFiSettings.propTypes = { WiFiSettings.propTypes = {
ws: PropTypes.object.isRequired, ws: PropTypes.object.isRequired,

View File

@ -25,15 +25,10 @@ exports[`<RebootButton/> Render modal. 1`] = `
Warning! Warning!
</h5> </h5>
<button <button
class="close" aria-label="Close"
class="btn-close"
type="button" type="button"
> />
<span
aria-hidden="true"
>
×
</span>
</button>
</div> </div>
<div <div
class="modal-body" class="modal-body"

View File

@ -6,6 +6,7 @@
*/ */
import React, { useState, useContext, useCallback } from "react"; import React, { useState, useContext, useCallback } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Alert, ALERT_TYPES } from "../../bootstrap/Alert"; import { Alert, ALERT_TYPES } from "../../bootstrap/Alert";

View File

@ -48,7 +48,7 @@ describe("AlertContext", () => {
// Alert is present // Alert is present
expect(getByText(componentContainer, "Alert content")).toBeDefined(); expect(getByText(componentContainer, "Alert content")).toBeDefined();
fireEvent.click(componentContainer.querySelector(".close")); fireEvent.click(componentContainer.querySelector(".btn-close"));
// Alert is gone // Alert is gone
expect(queryByText(componentContainer, "Alert content")).toBeNull(); expect(queryByText(componentContainer, "Alert content")).toBeNull();
}); });

View File

@ -6,14 +6,13 @@ exports[`AlertContext should render alert 1`] = `
id="alert-container" id="alert-container"
> >
<div <div
class="alert alert-dismissible alert-danger" class="alert alert-danger alert-dismissible"
> >
<button <button
class="close" aria-label="Close"
class="btn-close"
type="button" type="button"
> />
×
</button>
Alert content Alert content
</div> </div>
</div> </div>

View File

@ -6,12 +6,12 @@
*/ */
import React, { useContext, useEffect } from "react"; import React, { useContext, useEffect } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { useAPIGet } from "../../api/hooks"; import { useAPIGet } from "../../api/hooks";
import { ForisURLs } from "../../utils/forisUrls";
import { Spinner } from "../../bootstrap/Spinner"; import { Spinner } from "../../bootstrap/Spinner";
import { ForisURLs } from "../../utils/forisUrls";
CustomizationContextProvider.propTypes = { CustomizationContextProvider.propTypes = {
children: PropTypes.oneOfType([ children: PropTypes.oneOfType([

View File

@ -3,13 +3,13 @@
exports[`<SubmitButton/> Render load 1`] = ` exports[`<SubmitButton/> Render load 1`] = `
<div> <div>
<button <button
class="btn btn-primary col-sm-12 col-md-3 col-lg-2 d-inline-flex justify-content-center align-items-center" class="btn btn-primary col-12 col-md-3 col-lg-2 d-inline-flex justify-content-center align-items-center"
disabled="" disabled=""
type="submit" type="submit"
> >
<span <span
aria-hidden="true" aria-hidden="true"
class="spinner-border spinner-border-sm mr-1" class="spinner-border spinner-border-sm me-1"
role="status" role="status"
/> />
<span> <span>
@ -22,7 +22,7 @@ exports[`<SubmitButton/> Render load 1`] = `
exports[`<SubmitButton/> Render ready 1`] = ` exports[`<SubmitButton/> Render ready 1`] = `
<div> <div>
<button <button
class="btn btn-primary col-sm-12 col-md-3 col-lg-2 d-inline-flex justify-content-center align-items-center" class="btn btn-primary col-12 col-md-3 col-lg-2 d-inline-flex justify-content-center align-items-center"
type="submit" type="submit"
> >
<span> <span>
@ -35,13 +35,13 @@ exports[`<SubmitButton/> Render ready 1`] = `
exports[`<SubmitButton/> Render saving 1`] = ` exports[`<SubmitButton/> Render saving 1`] = `
<div> <div>
<button <button
class="btn btn-primary col-sm-12 col-md-3 col-lg-2 d-inline-flex justify-content-center align-items-center" class="btn btn-primary col-12 col-md-3 col-lg-2 d-inline-flex justify-content-center align-items-center"
disabled="" disabled=""
type="submit" type="submit"
> >
<span <span
aria-hidden="true" aria-hidden="true"
class="spinner-border spinner-border-sm mr-1" class="spinner-border spinner-border-sm me-1"
role="status" role="status"
/> />
<span> <span>

View File

@ -6,19 +6,19 @@
*/ */
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Prompt } from "react-router-dom"; import { Prompt } from "react-router-dom";
import { ALERT_TYPES } from "../../bootstrap/Alert"; import { STATES as SUBMIT_BUTTON_STATES, SubmitButton } from "./SubmitButton";
import { useAPIPost } from "../../api/hooks";
import { API_STATE } from "../../api/utils"; import { API_STATE } from "../../api/utils";
import { ALERT_TYPES } from "../../bootstrap/Alert";
import { formFieldsSize } from "../../bootstrap/constants"; import { formFieldsSize } from "../../bootstrap/constants";
import { Spinner } from "../../bootstrap/Spinner"; import { Spinner } from "../../bootstrap/Spinner";
import { useAlert } from "../../context/alertContext/AlertContext"; import { useAlert } from "../../context/alertContext/AlertContext";
import { useAPIPost } from "../../api/hooks";
import { useForisModule, useForm } from "../hooks";
import { STATES as SUBMIT_BUTTON_STATES, SubmitButton } from "./SubmitButton";
import { ErrorMessage } from "../../utils/ErrorMessage"; import { ErrorMessage } from "../../utils/ErrorMessage";
import { useForisModule, useForm } from "../hooks";
ForisForm.propTypes = { ForisForm.propTypes = {
/** Optional WebSocket object. See `scr/common/WebSockets.js`. /** Optional WebSocket object. See `scr/common/WebSockets.js`.
@ -190,7 +190,7 @@ export function ForisForm({
<Prompt message={getMessageOnLeavingPage} /> <Prompt message={getMessageOnLeavingPage} />
<form onSubmit={onSubmit} ref={formReference}> <form onSubmit={onSubmit} ref={formReference}>
{childrenWithFormProps} {childrenWithFormProps}
<div className="text-right"> <div className="text-end">
<SubmitButton <SubmitButton
state={getSubmitButtonState()} state={getSubmitButtonState()}
disabled={submitButtonIsDisabled} disabled={submitButtonIsDisabled}

View File

@ -6,6 +6,7 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Button } from "../../bootstrap/Button"; import { Button } from "../../bootstrap/Button";

View File

@ -6,6 +6,7 @@
*/ */
import { useCallback, useEffect, useReducer } from "react"; import { useCallback, useEffect, useReducer } from "react";
import update from "immutability-helper"; import update from "immutability-helper";
import { useAPIGet } from "../api/hooks"; import { useAPIGet } from "../api/hooks";

View File

@ -6,6 +6,7 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
ErrorMessage.propTypes = { ErrorMessage.propTypes = {

View File

@ -7,17 +7,18 @@
import React from "react"; import React from "react";
import { Spinner } from "../bootstrap/Spinner";
import { API_STATE } from "../api/utils";
import { ErrorMessage } from "./ErrorMessage"; import { ErrorMessage } from "./ErrorMessage";
import { API_STATE } from "../api/utils";
import { Spinner } from "../bootstrap/Spinner";
function withEither(conditionalFn, Either) { function withEither(conditionalFn, Either) {
return (Component) => (props) => { return (Component) =>
if (conditionalFn(props)) { function (props) {
return <Either {...props} />; if (conditionalFn(props)) {
} return <Either {...props} />;
return <Component {...props} />; }
}; return <Component {...props} />;
};
} }
// Loading // Loading

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2019 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.

View File

@ -23,12 +23,15 @@ export const ERROR_MESSAGES = {
const REs = { const REs = {
IPv4: /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/, IPv4: /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
IPv6: /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/, IPv6: /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/,
IPv6Prefix: /^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))$/, IPv6Prefix:
/^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))$/,
domain: /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/, domain: /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/,
hostname: /^[a-z\d]([a-z\d-]{0,61}[a-z\d])?(\.[a-z\d]([a-z\d-]{0,61}[a-z\d])?)*$/i, hostname:
/^[a-z\d]([a-z\d-]{0,61}[a-z\d])?(\.[a-z\d]([a-z\d-]{0,61}[a-z\d])?)*$/i,
DUID: /^([0-9a-fA-F]{2}){4}([0-9a-fA-F]{2})*$/, DUID: /^([0-9a-fA-F]{2}){4}([0-9a-fA-F]{2})*$/,
MAC: /^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/, MAC: /^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/,
MultipleEmails: /^([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+ *)( *, *[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+ *)*$/, MultipleEmails:
/^([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+ *)( *, *[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+ *)*$/,
}; };
const createValidator = (fieldType) => (value) => { const createValidator = (fieldType) => (value) => {

View File

@ -105,8 +105,5 @@ module.exports = {
}, },
], ],
}, },
devServer: {
publicPath: "/",
},
}, },
}; };