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

Compare commits

..

10 Commits

Author SHA1 Message Date
dff5f87e91 Merge branch 'refactor-number-input' into 'dev'
Enhance NumberInput component with keyboard & touch accessibility

See merge request turris/reforis/foris-js!259
2024-12-12 16:49:51 +01:00
38de792390 Update Snapshots 2024-12-12 18:47:38 +03:00
c23616811a Fix tests 2024-12-12 18:47:33 +03:00
f1132c6b22 Enhance NumberInput component with keyboard & touch accessibility 2024-12-12 18:44:21 +03:00
ffa1121d39 Merge branch 'add-encryption-selection-to-guest-form' into 'dev'
Add encryption selection to WiFiGuestForm

Closes #27

See merge request turris/reforis/foris-js!258
2024-12-09 16:59:57 +01:00
ee6865e3bb Update Snapshots 2024-12-09 16:52:51 +01:00
6352060da3 Add encryption selection to WiFiGuestForm 2024-12-09 16:52:50 +01:00
a63b5bfa4e Merge branch 'refactor-modal' into 'dev'
Add optional close button to ModalHeader component

See merge request turris/reforis/foris-js!257
2024-12-04 15:12:07 +01:00
4b2e47f8f9 Refactor pagination condition in RichTable component 2024-12-04 14:02:52 +01:00
66f83b24bd Add optional close button to ModalHeader component 2024-12-04 14:02:35 +01:00
7 changed files with 108 additions and 18 deletions

View File

@ -88,18 +88,21 @@ export function Modal({ shown, setShown, scrollable, size, children }) {
ModalHeader.propTypes = { ModalHeader.propTypes = {
setShown: PropTypes.func.isRequired, setShown: PropTypes.func.isRequired,
title: PropTypes.string.isRequired, title: PropTypes.string.isRequired,
showCloseButton: PropTypes.bool,
}; };
export function ModalHeader({ setShown, title }) { export function ModalHeader({ setShown, title, showCloseButton = true }) {
return ( return (
<div className="modal-header"> <div className="modal-header">
<h1 className="modal-title fs-5">{title}</h1> <h1 className="modal-title fs-5">{title}</h1>
{showCloseButton && (
<button <button
type="button" type="button"
className="btn-close" className="btn-close"
onClick={() => setShown(false)} onClick={() => setShown(false)}
aria-label={_("Close")} aria-label={_("Close")}
/> />
)}
</div> </div>
); );
} }

View File

@ -50,6 +50,20 @@ function NumberInput({ onChange, inlineText, value, ...props }) {
-1 -1
); );
function handleKeyDown(event, enableFunction) {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
enableFunction(true);
}
}
function handleKeyUp(event, enableFunction) {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
enableFunction(false);
}
}
return ( return (
<Input type="number" onChange={onChange} value={value} {...props}> <Input type="number" onChange={onChange} value={value} {...props}>
{inlineText && ( {inlineText && (
@ -60,7 +74,15 @@ function NumberInput({ onChange, inlineText, value, ...props }) {
className="btn btn-outline-secondary" className="btn btn-outline-secondary"
onMouseDown={() => enableIncrease(true)} onMouseDown={() => enableIncrease(true)}
onMouseUp={() => enableIncrease(false)} onMouseUp={() => enableIncrease(false)}
aria-label="Increase" onMouseLeave={() => enableIncrease(false)}
onTouchStart={() => enableIncrease(true)}
onTouchEnd={() => enableIncrease(false)}
onTouchCancel={() => enableIncrease(false)}
onKeyDown={(event) => handleKeyDown(event, enableIncrease)}
onKeyUp={(event) => handleKeyUp(event, enableIncrease)}
onBlur={() => enableIncrease(false)}
title={_("Increase value. Hint: Hold to increase faster.")}
aria-label={_("Increase value. Hint: Hold to increase faster.")}
> >
<FontAwesomeIcon icon={faPlus} /> <FontAwesomeIcon icon={faPlus} />
</button> </button>
@ -69,7 +91,15 @@ function NumberInput({ onChange, inlineText, value, ...props }) {
className="btn btn-outline-secondary" className="btn btn-outline-secondary"
onMouseDown={() => enableDecrease(true)} onMouseDown={() => enableDecrease(true)}
onMouseUp={() => enableDecrease(false)} onMouseUp={() => enableDecrease(false)}
aria-label="Decrease" onMouseLeave={() => enableDecrease(false)}
onTouchStart={() => enableDecrease(true)}
onTouchEnd={() => enableDecrease(false)}
onTouchCancel={() => enableDecrease(false)}
onKeyDown={(event) => handleKeyDown(event, enableDecrease)}
onKeyUp={(event) => handleKeyUp(event, enableDecrease)}
onBlur={() => enableDecrease(false)}
title={_("Decrease value. Hint: Hold to decrease faster.")}
aria-label={_("Decrease value. Hint: Hold to decrease faster.")}
> >
<FontAwesomeIcon icon={faMinus} /> <FontAwesomeIcon icon={faMinus} />
</button> </button>

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.
@ -32,7 +32,7 @@ describe("<NumberInput/>", () => {
}); });
it("Increase number with button", async () => { it("Increase number with button", async () => {
const increaseButton = getByLabelText(componentContainer, "Increase"); const increaseButton = getByLabelText(componentContainer, /Increase/);
fireEvent.mouseDown(increaseButton); fireEvent.mouseDown(increaseButton);
await wait(() => await wait(() =>
expect(onChangeMock).toHaveBeenCalledWith({ target: { value: 2 } }) expect(onChangeMock).toHaveBeenCalledWith({ target: { value: 2 } })
@ -40,7 +40,7 @@ describe("<NumberInput/>", () => {
}); });
it("Decrease number with button", async () => { it("Decrease number with button", async () => {
const decreaseButton = getByLabelText(componentContainer, "Decrease"); const decreaseButton = getByLabelText(componentContainer, /Decrease/);
fireEvent.mouseDown(decreaseButton); fireEvent.mouseDown(decreaseButton);
await wait(() => await wait(() =>
expect(onChangeMock).toHaveBeenCalledWith({ target: { value: 0 } }) expect(onChangeMock).toHaveBeenCalledWith({ target: { value: 0 } })

View File

@ -20,8 +20,9 @@ exports[`<NumberInput/> Render number input 1`] = `
value="1" value="1"
/> />
<button <button
aria-label="Increase" aria-label="Increase value. Hint: Hold to increase faster."
class="btn btn-outline-secondary" class="btn btn-outline-secondary"
title="Increase value. Hint: Hold to increase faster."
type="button" type="button"
> >
<i <i
@ -29,8 +30,9 @@ exports[`<NumberInput/> Render number input 1`] = `
/> />
</button> </button>
<button <button
aria-label="Decrease" aria-label="Decrease value. Hint: Hold to decrease faster."
class="btn btn-outline-secondary" class="btn btn-outline-secondary"
title="Decrease value. Hint: Hold to decrease faster."
type="button" type="button"
> >
<i <i

View File

@ -64,13 +64,15 @@ function RichTable({
}, },
}); });
const paginationIsNeeded = tableData.length > pageSize && withPagination;
return ( return (
<div className="table-responsive"> <div className="table-responsive">
<table className="table table-hover text-nowrap"> <table className="table table-hover text-nowrap">
<RichTableHeader table={table} flexRender={flexRender} /> <RichTableHeader table={table} flexRender={flexRender} />
<RichTableBody table={table} flexRender={flexRender} /> <RichTableBody table={table} flexRender={flexRender} />
</table> </table>
{withPagination && ( {paginationIsNeeded && (
<RichTablePagination <RichTablePagination
table={table} table={table}
tablePageSize={pageSize} tablePageSize={pageSize}

View File

@ -9,9 +9,10 @@ import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { HELP_TEXTS } from "./constants"; import { HELP_TEXTS, ENCRYPTIONMODES } from "./constants";
import WiFiQRCode from "./WiFiQRCode"; import WiFiQRCode from "./WiFiQRCode";
import PasswordInput from "../../bootstrap/PasswordInput"; import PasswordInput from "../../bootstrap/PasswordInput";
import Select from "../../bootstrap/Select";
import Switch from "../../bootstrap/Switch"; import Switch from "../../bootstrap/Switch";
import TextInput from "../../bootstrap/TextInput"; import TextInput from "../../bootstrap/TextInput";
@ -21,6 +22,7 @@ WifiGuestForm.propTypes = {
SSID: PropTypes.string.isRequired, SSID: PropTypes.string.isRequired,
password: PropTypes.string.isRequired, password: PropTypes.string.isRequired,
enabled: PropTypes.bool.isRequired, enabled: PropTypes.bool.isRequired,
encryption: PropTypes.string.isRequired,
}), }),
formErrors: PropTypes.shape({ formErrors: PropTypes.shape({
SSID: PropTypes.string, SSID: PropTypes.string,
@ -89,6 +91,20 @@ export default function WifiGuestForm({
}))} }))}
{...props} {...props}
/> />
<Select
label={_("Encryption")}
choices={ENCRYPTIONMODES}
helpText={HELP_TEXTS.wpa3}
value={formData.encryption}
onChange={setFormValue((value) => ({
devices: {
[formData.id]: {
guest_wifi: { encryption: { $set: value } },
},
},
}))}
{...props}
/>
</> </>
) : null} ) : null}
</> </>

View File

@ -339,7 +339,7 @@ exports[`<WiFiSettings/> Snapshot guest network. 1`] = `
- First value - First value
+ Second value + Second value
@@ -524,10 +524,87 @@ @@ -524,10 +524,124 @@
<small> <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. 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> </small>
@ -421,13 +421,50 @@ exports[`<WiFiSettings/> Snapshot guest network. 1`] = `
+ WPA2/3 pre-shared key, that is required to connect to the network. + WPA2/3 pre-shared key, that is required to connect to the network.
+ </small> + </small>
+ </div> + </div>
+ </div>
+ <div
+ class="mb-3"
+ >
+ <label
+ class="form-label"
+ for="14"
+ >
+ Encryption
+ </label>
+ <select
+ class="form-select"
+ id="14"
+ >
+ <option
+ value="WPA3"
+ >
+ WPA3 only
+ </option>
+ <option
+ value="WPA2/3"
+ >
+ WPA3 with WPA2 as fallback (default)
+ </option>
+ <option
+ value="WPA2"
+ >
+ WPA2 only
+ </option>
+ </select>
+ <div
+ class="form-text"
+ >
+ <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>
<hr /> <hr />
<div <div
class="form-check form-switch mb-3 d-flex align-items-center" class="form-check form-switch mb-3 d-flex align-items-center"
> >
<input <input
@@ -550,10 +627,11 @@ @@ -550,10 +664,11 @@
<div <div
class="text-end" class="text-end"
> >