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

Merge branch 'update-bootstrap' into 'dev'

Update Bootstrap library to version 5.3.x

See merge request turris/reforis/foris-js!217
This commit is contained in:
Aleksandr Gumroian 2024-04-29 15:28:09 +02:00
commit 39257567d4
33 changed files with 521 additions and 526 deletions

View File

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

78
package-lock.json generated
View File

@ -22,11 +22,11 @@
"@babel/plugin-transform-runtime": "^7.9.0",
"@babel/preset-env": "^7.9.0",
"@babel/preset-react": "^7.9.4",
"@fortawesome/fontawesome-free": "^5.13.0",
"@fortawesome/fontawesome-free": "^6.5.2",
"@testing-library/react": "^8.0.9",
"babel-loader": "^8.1.0",
"babel-polyfill": "^6.26.0",
"bootstrap": "^4.6.2",
"bootstrap": "^5.3.3",
"css-loader": "^5.2.4",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.11.0",
@ -47,7 +47,7 @@
"webpack": "^5.68.0"
},
"peerDependencies": {
"bootstrap": "^4.6.2",
"bootstrap": "^5.3.3",
"prop-types": "15.8.1",
"react": "16.9.0",
"react-dom": "16.9.0",
@ -1822,9 +1822,9 @@
}
},
"node_modules/@fortawesome/fontawesome-free": {
"version": "5.15.3",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.3.tgz",
"integrity": "sha512-rFnSUN/QOtnOAgqFRooTA3H57JLDm0QEG/jPdk+tLQNL/eWd+Aok8g3qCI+Q1xuDPWpGW/i9JySpJVsq8Q0s9w==",
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz",
"integrity": "sha512-hRILoInAx8GNT5IMkrtIt9blOdrqHOnPBH+k70aWUAqPZPgopb9G5EQJFpaBx/S8zp2fC+mPW349Bziuk1o28Q==",
"dev": true,
"hasInstallScript": true,
"engines": {
@ -2814,9 +2814,9 @@
}
},
"node_modules/@popperjs/core": {
"version": "2.11.2",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz",
"integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==",
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"dev": true,
"funding": {
"type": "opencollective",
@ -4330,9 +4330,9 @@
}
},
"node_modules/bootstrap": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz",
"integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==",
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
"integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
"dev": true,
"funding": [
{
@ -4345,8 +4345,7 @@
}
],
"peerDependencies": {
"jquery": "1.9.1 - 3",
"popper.js": "^1.16.1"
"@popperjs/core": "^2.11.8"
}
},
"node_modules/brace-expansion": {
@ -11673,13 +11672,6 @@
"node": ">=8"
}
},
"node_modules/jquery": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
"integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==",
"dev": true,
"peer": true
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -13791,18 +13783,6 @@
"integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
"dev": true
},
"node_modules/popper.js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
"deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1",
"dev": true,
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/portfinder": {
"version": "1.0.28",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
@ -20398,9 +20378,9 @@
}
},
"@fortawesome/fontawesome-free": {
"version": "5.15.3",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.3.tgz",
"integrity": "sha512-rFnSUN/QOtnOAgqFRooTA3H57JLDm0QEG/jPdk+tLQNL/eWd+Aok8g3qCI+Q1xuDPWpGW/i9JySpJVsq8Q0s9w==",
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz",
"integrity": "sha512-hRILoInAx8GNT5IMkrtIt9blOdrqHOnPBH+k70aWUAqPZPgopb9G5EQJFpaBx/S8zp2fC+mPW349Bziuk1o28Q==",
"dev": true
},
"@gar/promisify": {
@ -21165,9 +21145,9 @@
}
},
"@popperjs/core": {
"version": "2.11.2",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz",
"integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==",
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"dev": true
},
"@sheerun/mutationobserver-shim": {
@ -22452,9 +22432,9 @@
}
},
"bootstrap": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz",
"integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==",
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
"integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
"dev": true,
"requires": {}
},
@ -28040,13 +28020,6 @@
}
}
},
"jquery": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
"integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==",
"dev": true,
"peer": true
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -29672,13 +29645,6 @@
"integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
"dev": true
},
"popper.js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
"dev": true,
"peer": true
},
"portfinder": {
"version": "1.0.28",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",

View File

@ -22,7 +22,7 @@
"react-uid": "^2.2.0"
},
"peerDependencies": {
"bootstrap": "^4.6.2",
"bootstrap": "^5.3.3",
"prop-types": "15.8.1",
"react": "16.9.0",
"react-dom": "16.9.0",
@ -34,11 +34,11 @@
"@babel/plugin-transform-runtime": "^7.9.0",
"@babel/preset-env": "^7.9.0",
"@babel/preset-react": "^7.9.4",
"@fortawesome/fontawesome-free": "^5.13.0",
"@fortawesome/fontawesome-free": "^6.5.2",
"@testing-library/react": "^8.0.9",
"babel-loader": "^8.1.0",
"babel-polyfill": "^6.26.0",
"bootstrap": "^4.6.2",
"bootstrap": "^5.3.3",
"css-loader": "^5.2.4",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.11.0",

View File

@ -38,16 +38,17 @@ Alert.defaultProps = {
export function Alert({ type, onDismiss, children }) {
return (
<div
className={`alert ${
className={`alert alert-${type} ${
onDismiss ? "alert-dismissible" : ""
} alert-${type}`}
}`.trim()}
>
{onDismiss ? (
<button type="button" className="close" onClick={onDismiss}>
&times;
</button>
) : (
false
{onDismiss && (
<button
type="button"
className="btn-close"
onClick={onDismiss}
aria-label={_("Close")}
/>
)}
{children}
</div>

View File

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

View File

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

View File

@ -27,18 +27,21 @@ export const Input = forwardRef(
) => {
const uid = useUID();
const inputClassName = `form-control ${className || ""} ${
const inputClassName = `${className || ""} ${
error ? "is-invalid" : ""
}`.trim();
return (
<div className="form-group">
<label className={labelClassName} htmlFor={uid}>
<div className="mb-3">
<label
className={`form-label ${labelClassName || ""}`.trim()}
htmlFor={uid}
>
{label}
</label>
<div className={`input-group ${groupClassName || ""}`.trim()}>
<input
className={inputClassName}
className={`form-control ${inputClassName}`.trim()}
type={type}
id={uid}
ref={ref}
@ -46,10 +49,12 @@ export const Input = forwardRef(
/>
{children}
</div>
{error ? <div className="invalid-feedback">{error}</div> : null}
{helpText ? (
<small className="form-text text-muted">{helpText}</small>
) : null}
{error && <div className="invalid-feedback">{error}</div>}
{helpText && (
<div className="form-text">
<small>{helpText}</small>
</div>
)}
</div>
);
}

View File

@ -92,11 +92,10 @@ export function ModalHeader({ setShown, title }) {
<h5 className="modal-title">{title}</h5>
<button
type="button"
className="close"
className="btn-close"
onClick={() => setShown(false)}
>
<span aria-hidden="true">&times;</span>
</button>
aria-label={_("Close")}
/>
</div>
);
}

View File

@ -23,7 +23,7 @@ NumberInput.propTypes = {
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/** Function called when value changes. */
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,
};
@ -49,27 +49,27 @@ export function NumberInput({ onChange, inlineText, value, ...props }) {
return (
<Input type="number" onChange={onChange} value={value} {...props}>
<div className="input-group-append">
{inlineText && <p className="input-group-text">{inlineText}</p>}
<button
type="button"
className="btn btn-outline-secondary"
onMouseDown={() => enableIncrease(true)}
onMouseUp={() => enableIncrease(false)}
aria-label="Increase"
>
<i className="fas fa-plus" />
</button>
<button
type="button"
className="btn btn-outline-secondary"
onMouseDown={() => enableDecrease(true)}
onMouseUp={() => enableDecrease(false)}
aria-label="Decrease"
>
<i className="fas fa-minus" />
</button>
</div>
{inlineText && (
<span className="input-group-text">{inlineText}</span>
)}
<button
type="button"
className="btn btn-outline-secondary"
onMouseDown={() => enableIncrease(true)}
onMouseUp={() => enableIncrease(false)}
aria-label="Increase"
>
<i className="fas fa-plus" />
</button>
<button
type="button"
className="btn btn-outline-secondary"
onMouseDown={() => enableDecrease(true)}
onMouseUp={() => enableDecrease(false)}
aria-label="Decrease"
>
<i className="fas fa-minus" />
</button>
</Input>
);
}

View File

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

View File

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

View File

@ -30,14 +30,18 @@ export function Select({ label, choices, helpText, ...props }) {
));
return (
<div className="form-group">
<label htmlFor={uid}>{label}</label>
<select className="custom-select" id={uid} {...props}>
<div className="mb-3">
<label className="form-label" htmlFor={uid}>
{label}
</label>
<select className="form-select" id={uid} {...props}>
{options}
</select>
{helpText ? (
<small className="form-text text-muted">{helpText}</small>
) : null}
{helpText && (
<div className="form-text">
<small>{helpText}</small>
</div>
)}
</div>
);
}

View File

@ -16,7 +16,7 @@ Spinner.propTypes = {
PropTypes.arrayOf(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,
className: PropTypes.string,
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -58,7 +58,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."
)}
</p>
<div className="text-right">
<div className="text-end">
<Button
className="btn-primary"
forisFormSize

View File

@ -92,7 +92,7 @@ function DeviceForm({
return (
<>
<Switch
label={<h2>{_(`Wi-Fi ${deviceID + 1}`)}</h2>}
label={<h2 className="mb-0">{_(`Wi-Fi ${deviceID + 1}`)}</h2>}
checked={formData.enabled}
onChange={setFormValue((value) => ({
devices: {
@ -119,12 +119,10 @@ function DeviceForm({
}))}
{...props}
>
<div className="input-group-append">
<WiFiQRCode
SSID={formData.SSID}
password={formData.password}
/>
</div>
<WiFiQRCode
SSID={formData.SSID}
password={formData.password}
/>
</TextInput>
<PasswordInput

View File

@ -87,7 +87,7 @@ function QRCodeModal({ shown, setShown, SSID, password }) {
createAndDownloadPdf(SSID, password);
}}
>
<i className="fas fa-arrow-down mr-2" />
<i className="fas fa-file-download me-2" />
{_("Download PDF")}
</Button>
</ModalFooter>

View File

@ -5,7 +5,7 @@ exports[`<WiFiSettings/> Snapshot 2.4 GHz 1`] = `
- First value
+ Second value
@@ -241,207 +241,95 @@
@@ -243,207 +243,95 @@
value=\\"0\\"
>
auto
@ -255,53 +255,51 @@ exports[`<WiFiSettings/> Snapshot both modules disabled. 1`] = `
>
<form>
<div
class="form-group switch"
class="form-check form-switch mb-3 d-flex align-items-center"
>
<div
class="custom-control custom-switch custom-control-inline switch-heading"
<input
class="form-check-input me-2"
id="1"
role="switch"
type="checkbox"
/>
<label
class="form-check-label"
for="1"
>
<input
class="custom-control-input"
id="1"
type="checkbox"
/>
<label
class="custom-control-label"
for="1"
<h2
class="mb-0"
>
<h2>
Wi-Fi 1
</h2>
</label>
</div>
Wi-Fi 1
</h2>
</label>
</div>
<hr />
<div
class="form-group switch"
class="form-check form-switch mb-3 d-flex align-items-center"
>
<div
class="custom-control custom-switch custom-control-inline switch-heading"
<input
class="form-check-input me-2"
id="2"
role="switch"
type="checkbox"
/>
<label
class="form-check-label"
for="2"
>
<input
class="custom-control-input"
id="2"
type="checkbox"
/>
<label
class="custom-control-label"
for="2"
<h2
class="mb-0"
>
<h2>
Wi-Fi 2
</h2>
</label>
</div>
Wi-Fi 2
</h2>
</label>
</div>
<div
class="text-right"
class="text-end"
>
<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"
>
<span>
@ -321,10 +319,10 @@ exports[`<WiFiSettings/> Snapshot both modules disabled. 1`] = `
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>
<div
class="text-right"
class="text-end"
>
<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="button"
>
<span>
@ -341,16 +339,17 @@ exports[`<WiFiSettings/> Snapshot guest network. 1`] = `
- First value
+ Second value
@@ -524,10 +524,92 @@
>
@@ -527,10 +527,94 @@
<small>
Enables Wi-Fi for guests, which is separated from LAN network. Devices connected to this network are allowed to access the internet, but aren't allowed to access other devices and the configuration interface of the router. Parameters of the guest network can be set in the Guest network tab.
</small>
</div>
</div>
+ <div
+ class=\\"form-group\\"
+ class=\\"mb-3\\"
+ >
+ <label
+ class=\\"form-label\\"
+ for=\\"24\\"
+ >
+ SSID
@ -380,16 +379,19 @@ exports[`<WiFiSettings/> Snapshot guest network. 1`] = `
+ </button>
+ </div>
+ </div>
+ <small
+ class=\\"form-text text-muted\\"
+ <div
+ class=\\"form-text\\"
+ >
+ SSID which contains non-standard characters could cause problems on some devices.
+ </small>
+ <small>
+ SSID which contains non-standard characters could cause problems on some devices.
+ </small>
+ </div>
+ </div>
+ <div
+ class=\\"form-group\\"
+ class=\\"mb-3\\"
+ >
+ <label
+ class=\\"form-label\\"
+ for=\\"25\\"
+ >
+ Password
@ -399,47 +401,45 @@ exports[`<WiFiSettings/> Snapshot guest network. 1`] = `
+ >
+ <input
+ autocomplete=\\"current-password\\"
+ class=\\"form-control is-invalid\\"
+ class=\\"form-control is-invalid\\"
+ id=\\"25\\"
+ required=\\"\\"
+ type=\\"password\\"
+ value=\\"\\"
+ />
+ <div
+ class=\\"input-group-append\\"
+ <button
+ class=\\"input-group-text\\"
+ type=\\"button\\"
+ >
+ <button
+ class=\\"input-group-text\\"
+ type=\\"button\\"
+ >
+ <i
+ class=\\"fa fa-eye\\"
+ />
+ </button>
+ </div>
+ <i
+ class=\\"fa fa-eye\\"
+ />
+ </button>
+ </div>
+ <div
+ class=\\"invalid-feedback\\"
+ >
+ Password must contain at least 8 symbols
+ </div>
+ <small
+ class=\\"form-text text-muted\\"
+ <div
+ class=\\"form-text\\"
+ >
+ WPA2/3 pre-shared key, that is required to connect to the network.
+ </small>
+ <small>
+ WPA2/3 pre-shared key, that is required to connect to the network.
+ </small>
+ </div>
+ </div>
<hr />
<div
class=\\"form-group switch\\"
class=\\"form-check form-switch mb-3 d-flex align-items-center\\"
>
<div
@@ -551,10 +633,11 @@
<input
@@ -553,10 +637,11 @@
<div
class=\\"text-right\\"
class=\\"text-end\\"
>
<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=\\"\\"
type=\\"submit\\"
>
@ -453,16 +453,17 @@ exports[`<WiFiSettings/> Snapshot one module enabled. 1`] = `
- First value
+ Second value
@@ -22,10 +22,512 @@
Wi-Fi 1
</h2>
</label>
</div>
@@ -21,10 +21,516 @@
>
Wi-Fi 1
</h2>
</label>
</div>
+ <div
+ class=\\"form-group\\"
+ class=\\"mb-3\\"
+ >
+ <label
+ class=\\"form-label\\"
+ for=\\"4\\"
+ >
+ SSID
@ -477,32 +478,31 @@ exports[`<WiFiSettings/> Snapshot one module enabled. 1`] = `
+ type=\\"text\\"
+ value=\\"TestSSID1\\"
+ />
+ <div
+ class=\\"input-group-append\\"
+ <button
+ class=\\"input-group-text\\"
+ type=\\"button\\"
+ >
+ <button
+ class=\\"input-group-text\\"
+ type=\\"button\\"
+ >
+ <img
+ alt=\\"QR\\"
+ src=\\"/reforis/static/reforis/imgs/QR_icon.svg\\"
+ style=\\"opacity: 0.67;\\"
+ width=\\"20\\"
+ />
+ </button>
+ </div>
+ <img
+ alt=\\"QR\\"
+ src=\\"/reforis/static/reforis/imgs/QR_icon.svg\\"
+ style=\\"opacity: 0.67;\\"
+ width=\\"20\\"
+ />
+ </button>
+ </div>
+ <small
+ class=\\"form-text text-muted\\"
+ <div
+ class=\\"form-text\\"
+ >
+ SSID which contains non-standard characters could cause problems on some devices.
+ </small>
+ <small>
+ SSID which contains non-standard characters could cause problems on some devices.
+ </small>
+ </div>
+ </div>
+ <div
+ class=\\"form-group\\"
+ class=\\"mb-3\\"
+ >
+ <label
+ class=\\"form-label\\"
+ for=\\"5\\"
+ >
+ Password
@ -518,51 +518,48 @@ exports[`<WiFiSettings/> Snapshot one module enabled. 1`] = `
+ type=\\"password\\"
+ value=\\"TestPass\\"
+ />
+ <div
+ class=\\"input-group-append\\"
+ <button
+ class=\\"input-group-text\\"
+ type=\\"button\\"
+ >
+ <button
+ class=\\"input-group-text\\"
+ type=\\"button\\"
+ >
+ <i
+ class=\\"fa fa-eye\\"
+ />
+ </button>
+ </div>
+ <i
+ class=\\"fa fa-eye\\"
+ />
+ </button>
+ </div>
+ <small
+ class=\\"form-text text-muted\\"
+ <div
+ class=\\"form-text\\"
+ >
+ WPA2/3 pre-shared key, that is required to connect to the network.
+ </small>
+ <small>
+ WPA2/3 pre-shared key, that is required to connect to the network.
+ </small>
+ </div>
+ </div>
+ <div
+ class=\\"form-group\\"
+ class=\\"form-check form-switch mb-3 null\\"
+ >
+ <div
+ class=\\"custom-control custom-switch\\"
+ <input
+ class=\\"form-check-input\\"
+ id=\\"6\\"
+ role=\\"switch\\"
+ type=\\"checkbox\\"
+ />
+ <label
+ class=\\"form-check-label\\"
+ for=\\"6\\"
+ >
+ <input
+ class=\\"custom-control-input\\"
+ id=\\"6\\"
+ type=\\"checkbox\\"
+ />
+ <label
+ class=\\"custom-control-label\\"
+ for=\\"6\\"
+ >
+ Hide SSID
+ </label>
+ <small
+ class=\\"form-text text-muted mt-0 mb-3\\"
+ >
+ Hide SSID
+ </label>
+ <div
+ class=\\"form-text\\"
+ >
+ <small>
+ If set, network is not visible when scanning for available networks.
+ </small>
+ </div>
+ </div>
+ <div
+ class=\\"form-group\\"
+ class=\\"mb-3\\"
+ >
+ <label
+ class=\\"d-block\\"
@ -571,56 +568,59 @@ exports[`<WiFiSettings/> Snapshot one module enabled. 1`] = `
+ GHz
+ </label>
+ <div
+ class=\\"custom-control custom-radio custom-control-inline\\"
+ class=\\"mb-2 form-check form-check-inline\\"
+ >
+ <input
+ class=\\"custom-control-input\\"
+ class=\\"form-check-input me-2\\"
+ id=\\"hwmode-0-0\\"
+ name=\\"hwmode-0\\"
+ type=\\"radio\\"
+ value=\\"11g\\"
+ />
+ <label
+ class=\\"custom-control-label\\"
+ class=\\"form-check-label\\"
+ for=\\"hwmode-0-0\\"
+ >
+ 2.4
+ </label>
+ </div>
+ <div
+ class=\\"custom-control custom-radio custom-control-inline\\"
+ class=\\"mb-2 form-check form-check-inline\\"
+ >
+ <input
+ checked=\\"\\"
+ class=\\"custom-control-input\\"
+ class=\\"form-check-input me-2\\"
+ id=\\"hwmode-0-1\\"
+ name=\\"hwmode-0\\"
+ type=\\"radio\\"
+ value=\\"11a\\"
+ />
+ <label
+ class=\\"custom-control-label\\"
+ class=\\"form-check-label\\"
+ for=\\"hwmode-0-1\\"
+ >
+ 5
+ </label>
+ </div>
+ <small
+ class=\\"form-text text-muted\\"
+ <div
+ class=\\"form-text\\"
+ >
+ The 2.4 GHz band is more widely supported by clients, but tends to have more interference. The 5 GHz band is a newer standard and may not be supported by all your devices. It usually has less interference, but the signal does not carry so well indoors.
+ </small>
+ <small>
+ The 2.4 GHz band is more widely supported by clients, but tends to have more interference. The 5 GHz band is a newer standard and may not be supported by all your devices. It usually has less interference, but the signal does not carry so well indoors.
+ </small>
+ </div>
+ </div>
+ <div
+ class=\\"form-group\\"
+ class=\\"mb-3\\"
+ >
+ <label
+ class=\\"form-label\\"
+ for=\\"8\\"
+ >
+ 802.11n/ac/ax mode
+ </label>
+ <select
+ class=\\"custom-select\\"
+ class=\\"form-select\\"
+ id=\\"8\\"
+ >
+ <option
@ -654,22 +654,25 @@ exports[`<WiFiSettings/> Snapshot one module enabled. 1`] = `
+ 802.11ac - 80 MHz wide channel
+ </option>
+ </select>
+ <small
+ class=\\"form-text text-muted\\"
+ <div
+ class=\\"form-text\\"
+ >
+ Change this to adjust 802.11n/ac/ax mode of operation. 802.11n with 40 MHz wide channels can yield higher throughput but can cause more interference in the network. If you don't know what to choose, use the default option with 20 MHz wide channel.
+ </small>
+ <small>
+ Change this to adjust 802.11n/ac/ax mode of operation. 802.11n with 40 MHz wide channels can yield higher throughput but can cause more interference in the network. If you don't know what to choose, use the default option with 20 MHz wide channel.
+ </small>
+ </div>
+ </div>
+ <div
+ class=\\"form-group\\"
+ class=\\"mb-3\\"
+ >
+ <label
+ class=\\"form-label\\"
+ for=\\"9\\"
+ >
+ Channel
+ </label>
+ <select
+ class=\\"custom-select\\"
+ class=\\"form-select\\"
+ id=\\"9\\"
+ >
+ <option
@ -880,15 +883,16 @@ exports[`<WiFiSettings/> Snapshot one module enabled. 1`] = `
+ </select>
+ </div>
+ <div
+ class=\\"form-group\\"
+ class=\\"mb-3\\"
+ >
+ <label
+ class=\\"form-label\\"
+ for=\\"10\\"
+ >
+ Encryption
+ </label>
+ <select
+ class=\\"custom-select\\"
+ class=\\"form-select\\"
+ id=\\"10\\"
+ >
+ <option
@ -907,63 +911,63 @@ exports[`<WiFiSettings/> Snapshot one module enabled. 1`] = `
+ WPA2 only
+ </option>
+ </select>
+ <small
+ class=\\"form-text text-muted\\"
+ <div
+ class=\\"form-text\\"
+ >
+ The WPA3 standard is the new most secure encryption method that is suggested to be used with any device that supports it. The older devices without WPA3 support require older WPA2. If you experience issues with connecting older devices, try to enable WPA2.
+ </small>
+ <small>
+ The WPA3 standard is the new most secure encryption method that is suggested to be used with any device that supports it. The older devices without WPA3 support require older WPA2. If you experience issues with connecting older devices, try to enable WPA2.
+ </small>
+ </div>
+ </div>
+ <div
+ class=\\"form-group\\"
+ class=\\"form-check form-switch mb-3 null\\"
+ >
+ <div
+ class=\\"custom-control custom-switch\\"
+ <input
+ class=\\"form-check-input\\"
+ id=\\"11\\"
+ role=\\"switch\\"
+ type=\\"checkbox\\"
+ />
+ <label
+ class=\\"form-check-label\\"
+ for=\\"11\\"
+ >
+ <input
+ class=\\"custom-control-input\\"
+ id=\\"11\\"
+ type=\\"checkbox\\"
+ />
+ <label
+ class=\\"custom-control-label\\"
+ for=\\"11\\"
+ >
+ Disable Management Frame Protection
+ </label>
+ <small
+ class=\\"form-text text-muted mt-0 mb-3\\"
+ >
+ Disable Management Frame Protection
+ </label>
+ <div
+ class=\\"form-text\\"
+ >
+ <small>
+ In case you have trouble connecting to WiFi Access Point, try disabling Management Frame Protection.
+ </small>
+ </div>
+ </div>
+ <div
+ class=\\"form-group\\"
+ class=\\"form-check form-switch mb-3 null\\"
+ >
+ <div
+ class=\\"custom-control custom-switch\\"
+ <input
+ class=\\"form-check-input\\"
+ id=\\"12\\"
+ role=\\"switch\\"
+ type=\\"checkbox\\"
+ />
+ <label
+ class=\\"form-check-label\\"
+ for=\\"12\\"
+ >
+ <input
+ class=\\"custom-control-input\\"
+ id=\\"12\\"
+ type=\\"checkbox\\"
+ />
+ <label
+ class=\\"custom-control-label\\"
+ for=\\"12\\"
+ >
+ Enable Guest Wi-Fi
+ </label>
+ <small
+ class=\\"form-text text-muted mt-0 mb-3\\"
+ >
+ Enable Guest Wi-Fi
+ </label>
+ <div
+ class=\\"form-text\\"
+ >
+ <small>
+ Enables Wi-Fi for guests, which is separated from LAN network. Devices connected to this network are allowed to access the internet, but aren't allowed to access other devices and the configuration interface of the router. Parameters of the guest network can be set in the Guest network tab.
+ </small>
+ </div>
+ </div>
<hr />
<div
class=\\"form-group switch\\"
class=\\"form-check form-switch mb-3 d-flex align-items-center\\"
>
<div"
<input"
`;

View File

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

View File

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

View File

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

View File

@ -3,13 +3,13 @@
exports[`<SubmitButton/> Render load 1`] = `
<div>
<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=""
type="submit"
>
<span
aria-hidden="true"
class="spinner-border spinner-border-sm mr-1"
class="spinner-border spinner-border-sm me-1"
role="status"
/>
<span>
@ -22,7 +22,7 @@ exports[`<SubmitButton/> Render load 1`] = `
exports[`<SubmitButton/> Render ready 1`] = `
<div>
<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"
>
<span>
@ -35,13 +35,13 @@ exports[`<SubmitButton/> Render ready 1`] = `
exports[`<SubmitButton/> Render saving 1`] = `
<div>
<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=""
type="submit"
>
<span
aria-hidden="true"
class="spinner-border spinner-border-sm mr-1"
class="spinner-border spinner-border-sm me-1"
role="status"
/>
<span>

View File

@ -190,7 +190,7 @@ export function ForisForm({
<Prompt message={getMessageOnLeavingPage} />
<form onSubmit={onSubmit} ref={formReference}>
{childrenWithFormProps}
<div className="text-right">
<div className="text-end">
<SubmitButton
state={getSubmitButtonState()}
disabled={submitButtonIsDisabled}

View File

@ -69,7 +69,7 @@ export {
withErrorMessage,
} from "./utils/conditionalHOCs";
export { ErrorMessage } from "./utils/ErrorMessage";
export { useClickOutside } from "./utils/hooks";
export { useClickOutside, useTooltip } from "./utils/hooks";
export { toLocaleDateString } from "./utils/datetime";
export { displayCard } from "./utils/displayCard";
export { isPluginInstalled } from "./utils/isPluginInstalled";

View File

@ -1,11 +1,12 @@
/*
* 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.
* See /LICENSE for more information.
*/
import { useState, useEffect } from "react";
import { useState, useEffect, useRef } from "react";
import { Tooltip } from "bootstrap/dist/js/bootstrap.bundle.min";
/** Execute callback when condition is set to true. */
export function useConditionalTimeout(
@ -40,3 +41,22 @@ export function useClickOutside(element, callback) {
};
});
}
/** useTooltip hook for Bootstrap tooltips. */
export function useTooltip(description, placement = "top", trigger = "hover") {
const tooltipRef = useRef();
useEffect(() => {
const tooltip = new Tooltip(tooltipRef.current, {
title: description,
placement,
trigger,
});
return () => {
tooltip.dispose();
};
});
return tooltipRef;
}