diff --git a/package-lock.json b/package-lock.json
index da88342..e13e0a3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1505,6 +1505,16 @@
"@types/react": "*"
}
},
+ "@types/react-bootstrap-typeahead": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/@types/react-bootstrap-typeahead/-/react-bootstrap-typeahead-3.4.0.tgz",
+ "integrity": "sha512-J2JFcXWoY4234vnBGGh41PdTLMLQT9tZzkn1iBzuf/lRJv+tnGX2BtmbFUmKhwFzGFbjHqKubUvtHnFsYNohRQ==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*",
+ "csstype": "^2.2.0"
+ }
+ },
"@types/react-dom": {
"version": "16.0.11",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.0.11.tgz",
diff --git a/package.json b/package.json
index af45a0b..e8e5d45 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,7 @@
"@types/node": "^10.12.18",
"@types/react": "^16.7.20",
"@types/react-bootstrap-daterangepicker": "0.0.26",
+ "@types/react-bootstrap-typeahead": "^3.4.0",
"@types/react-dom": "^16.0.11",
"@types/react-router-dom": "^4.3.1",
"@types/react-table": "^6.7.21",
diff --git a/src/components/settings/ConditionalForwardingSettings.js b/src/components/settings/ConditionalForwardingSettings.tsx
similarity index 79%
rename from src/components/settings/ConditionalForwardingSettings.js
rename to src/components/settings/ConditionalForwardingSettings.tsx
index 326cbaf..0361e06 100644
--- a/src/components/settings/ConditionalForwardingSettings.js
+++ b/src/components/settings/ConditionalForwardingSettings.tsx
@@ -9,8 +9,22 @@
* Please see LICENSE file for your rights under this license. */
import React, { Fragment } from "react";
-import PropTypes from "prop-types";
import { Col, FormGroup, Input, Label } from "reactstrap";
+import i18next from "i18next";
+
+export interface ConditionalForwardingObject {
+ enabled: boolean,
+ routerIp: string,
+ domain: string
+}
+
+export interface ConditionalForwardingSettingsProps {
+ settings: ConditionalForwardingObject,
+ onUpdate: (settings: ConditionalForwardingObject) => void,
+ isRouterIpValid: boolean;
+ isDomainValid: boolean;
+ t: i18next.TranslationFunction
+}
const ConditionalForwardingSettings = ({
settings,
@@ -18,7 +32,7 @@ const ConditionalForwardingSettings = ({
isRouterIpValid,
isDomainValid,
t
-}) => (
+}: ConditionalForwardingSettingsProps) => (
);
-ConditionalForwardingSettings.propTypes = {
- settings: PropTypes.object.isRequired,
- onUpdate: PropTypes.func.isRequired,
- isRouterIpValid: PropTypes.bool.isRequired,
- isDomainValid: PropTypes.bool.isRequired,
- t: PropTypes.func.isRequired
-};
-
export default ConditionalForwardingSettings;
diff --git a/src/components/settings/DHCPInfo.js b/src/components/settings/DHCPInfo.tsx
similarity index 80%
rename from src/components/settings/DHCPInfo.js
rename to src/components/settings/DHCPInfo.tsx
index 4e15ae7..922b120 100644
--- a/src/components/settings/DHCPInfo.js
+++ b/src/components/settings/DHCPInfo.tsx
@@ -8,31 +8,26 @@
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
-import React, { Component } from "react";
-import { withNamespaces } from "react-i18next";
-import { ignoreCancel, makeCancelable } from "../../util";
-import api from "../../util/api";
-import {
- Button,
- Col,
- Form,
- FormGroup,
- Input,
- InputGroup,
- InputGroupAddon,
- Label
-} from "reactstrap";
-import {
- isPositiveNumber,
- isValidHostname,
- isValidIpv4
-} from "../../util/validate";
-import Alert from "../common/Alert";
+import React, { ChangeEvent, Component, FormEvent } from "react";
+import { WithNamespaces, withNamespaces } from "react-i18next";
+import { CancelablePromise, ignoreCancel, makeCancelable } from "../../util";
+import api, { ApiDhcpSettings, ApiResultResponse } from "../../util/api";
+import { Button, Col, Form, FormGroup, Input, InputGroup, InputGroupAddon, Label } from "reactstrap";
+import { isValidHostname, isValidIpv4 } from "../../util/validate";
+import Alert, { AlertType } from "../common/Alert";
-class DHCPInfo extends Component {
- state = {
+export interface DHCPInfoState {
+ alertMessage: string;
+ alertType: AlertType;
+ showAlert: boolean;
+ processing: boolean;
+ settings: ApiDhcpSettings;
+}
+
+class DHCPInfo extends Component {
+ state: DHCPInfoState = {
alertMessage: "",
- alertType: "",
+ alertType: "info",
showAlert: false,
processing: false,
settings: {
@@ -40,27 +35,20 @@ class DHCPInfo extends Component {
ip_start: "",
ip_end: "",
router_ip: "",
- lease_time: "",
+ lease_time: 0,
domain: "",
ipv6_support: false
}
};
+ private loadHandler: undefined | CancelablePromise;
+ private updateHandler: undefined | CancelablePromise;
+
loadDHCPInfo = () => {
this.loadHandler = makeCancelable(api.getDHCPInfo());
this.loadHandler.promise
.then(res => {
- this.setState({
- settings: {
- active: res.active,
- ip_start: res.ip_start,
- ip_end: res.ip_end,
- router_ip: res.router_ip,
- lease_time: res.lease_time,
- domain: res.domain,
- ipv6_support: res.ipv6_support
- }
- });
+ this.setState({ settings: res });
})
.catch(ignoreCancel);
};
@@ -70,7 +58,9 @@ class DHCPInfo extends Component {
}
componentWillUnmount() {
- this.loadHandler.cancel();
+ if (this.loadHandler) {
+ this.loadHandler.cancel();
+ }
if (this.updateHandler) {
this.updateHandler.cancel();
@@ -85,9 +75,10 @@ class DHCPInfo extends Component {
* @param attr {string} the event target attribute to use
* @returns {function(Event)}
*/
- onChange = (key, attr) => {
- return e => {
- const value = e.target[attr];
+ onChange = (key: string, attr: string) => {
+ return (e: ChangeEvent) => {
+ // @ts-ignore
+ const value: string = e.target[attr];
this.setState(oldState => ({
settings: {
@@ -103,7 +94,7 @@ class DHCPInfo extends Component {
*
* @param e the submit event
*/
- saveSettings = e => {
+ saveSettings = (e: FormEvent) => {
e.preventDefault();
const { t } = this.props;
@@ -155,7 +146,7 @@ class DHCPInfo extends Component {
* @param value the value to check
* @param validator the validation function
*/
- isSettingValid = (value, validator) => {
+ isSettingValid = (value: string, validator: (value: string) => boolean) => {
return (
(!this.state.settings.active && value.length === 0) || validator(value)
);
@@ -180,10 +171,7 @@ class DHCPInfo extends Component {
this.state.settings.router_ip,
isValidIpv4
);
- const isLeaseTimeValid = this.isSettingValid(
- this.state.settings.lease_time,
- isPositiveNumber
- );
+ const isLeaseTimeValid = this.state.settings.lease_time >= 0;
const isDomainValid = this.isSettingValid(
this.state.settings.domain,
isValidHostname
@@ -262,7 +250,14 @@ class DHCPInfo extends Component {
id="leaseTime"
disabled={!this.state.settings.active}
value={this.state.settings.lease_time}
- onChange={this.onChange("lease_time", "value")}
+ onChange={(e: ChangeEvent) =>
+ this.setState(oldState => ({
+ settings: {
+ ...oldState.settings,
+ lease_time: parseInt(e.target.value)
+ }
+ }))
+ }
invalid={!isLeaseTimeValid}
/>
Hours
diff --git a/src/components/settings/DNSInfo.js b/src/components/settings/DNSInfo.tsx
similarity index 79%
rename from src/components/settings/DNSInfo.js
rename to src/components/settings/DNSInfo.tsx
index 78a8fab..3a77b00 100644
--- a/src/components/settings/DNSInfo.js
+++ b/src/components/settings/DNSInfo.tsx
@@ -8,21 +8,31 @@
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
-import React, { Component } from "react";
-import { withNamespaces } from "react-i18next";
-import { ignoreCancel, makeCancelable } from "../../util";
-import api from "../../util/api";
+import React, { Component, FormEvent } from "react";
+import { WithNamespaces, withNamespaces } from "react-i18next";
+import { CancelablePromise, ignoreCancel, makeCancelable } from "../../util";
+import api, { ApiDnsSettings, ApiResultResponse } from "../../util/api";
import DnsList from "./DnsList";
import { Button, Col, Form, FormGroup } from "reactstrap";
-import ConditionalForwardingSettings from "./ConditionalForwardingSettings";
-import DnsOptionSettings from "./DnsOptionSettings";
-import Alert from "../common/Alert";
+import ConditionalForwardingSettings, { ConditionalForwardingObject } from "./ConditionalForwardingSettings";
+import DnsOptionSettings, { DnsOptionsObject } from "./DnsOptionSettings";
+import Alert, { AlertType } from "../common/Alert";
import { isValidHostname, isValidIpv4 } from "../../util/validate";
-class DNSInfo extends Component {
- state = {
+export interface DNSInfoState {
+ alertMessage: string;
+ alertType: AlertType;
+ showAlert: boolean;
+ processing: boolean;
+ upstreamDns: Array;
+ conditionalForwarding: ConditionalForwardingObject;
+ options: DnsOptionsObject;
+}
+
+class DNSInfo extends Component {
+ state: DNSInfoState = {
alertMessage: "",
- alertType: "",
+ alertType: "info",
showAlert: false,
processing: false,
upstreamDns: [],
@@ -39,6 +49,9 @@ class DNSInfo extends Component {
}
};
+ private loadHandler: undefined | CancelablePromise;
+ private updateHandler: undefined | CancelablePromise;
+
loadDNSInfo = () => {
this.loadHandler = makeCancelable(api.getDNSInfo());
this.loadHandler.promise
@@ -70,26 +83,30 @@ class DNSInfo extends Component {
}
componentWillUnmount() {
- this.loadHandler.cancel();
+ if (this.loadHandler) {
+ this.loadHandler.cancel();
+ }
}
- handleUpstreamAdd = upstream => {
+ handleUpstreamAdd = (upstream: string) => {
this.setState({
upstreamDns: this.state.upstreamDns.concat(upstream)
});
};
- handleUpstreamRemove = upstream => {
+ handleUpstreamRemove = (upstream: string) => {
this.setState({
upstreamDns: this.state.upstreamDns.filter(item => item !== upstream)
});
};
- handleConditionalForwardingUpdate = conditionalForwarding => {
+ handleConditionalForwardingUpdate = (
+ conditionalForwarding: ConditionalForwardingObject
+ ) => {
this.setState({ conditionalForwarding });
};
- handleDnsOptionsUpdate = options => {
+ handleDnsOptionsUpdate = (options: DnsOptionsObject) => {
this.setState({ options });
};
@@ -98,7 +115,7 @@ class DNSInfo extends Component {
*
* @param e the submit event
*/
- saveSettings = e => {
+ saveSettings = (e: FormEvent) => {
e.preventDefault();
const { t } = this.props;
@@ -157,7 +174,7 @@ class DNSInfo extends Component {
});
};
- isCFSettingValid = (value, validator) => {
+ isCFSettingValid = (value: string, validator: (value: string) => boolean) => {
return (
(!this.state.conditionalForwarding.enabled && value.length === 0) ||
validator(value)
diff --git a/src/components/settings/DnsList.js b/src/components/settings/DnsList.tsx
similarity index 76%
rename from src/components/settings/DnsList.js
rename to src/components/settings/DnsList.tsx
index dd3b8d3..5668536 100644
--- a/src/components/settings/DnsList.js
+++ b/src/components/settings/DnsList.tsx
@@ -9,12 +9,17 @@
* Please see LICENSE file for your rights under this license. */
import React from "react";
-import PropTypes from "prop-types";
import { ListGroup } from "reactstrap";
import DnsListItem from "./DnsListItem";
import DnsListNewItem from "./DnsListNewItem";
import { isValidIpv4OptionalPort } from "../../util/validate";
+export interface DnsListProps {
+ upstreams: Array;
+ onAdd: (upstream: string) => void;
+ onRemove: (upstream: string) => void;
+}
+
/**
* Check if an upstream address is unique and valid
*
@@ -22,10 +27,10 @@ import { isValidIpv4OptionalPort } from "../../util/validate";
* @param upstreams the list of current upstreams
* @returns {boolean} if the upstream address is unique and valid
*/
-export const isAddressValid = (address, upstreams) =>
+export const isAddressValid = (address: string, upstreams: Array) =>
!upstreams.includes(address) && isValidIpv4OptionalPort(address);
-const DnsList = ({ upstreams, onAdd, onRemove }) => (
+const DnsList = ({ upstreams, onAdd, onRemove }: DnsListProps) => (
{upstreams.map(upstream => (
(
))}
isAddressValid(address, upstreams)}
+ isValid={(address: string) => isAddressValid(address, upstreams)}
upstreams={upstreams}
/>
);
-DnsList.propTypes = {
- upstreams: PropTypes.arrayOf(PropTypes.string).isRequired,
- onAdd: PropTypes.func.isRequired,
- onRemove: PropTypes.func.isRequired
-};
-
export default DnsList;
diff --git a/src/components/settings/DnsListItem.js b/src/components/settings/DnsListItem.tsx
similarity index 76%
rename from src/components/settings/DnsListItem.js
rename to src/components/settings/DnsListItem.tsx
index fecf7b2..d99ae9d 100644
--- a/src/components/settings/DnsListItem.js
+++ b/src/components/settings/DnsListItem.tsx
@@ -9,10 +9,14 @@
* Please see LICENSE file for your rights under this license. */
import React from "react";
-import PropTypes from "prop-types";
import { Button, ListGroupItem } from "reactstrap";
-const DnsListItem = ({ address, onRemove }) => (
+export interface DnsListItemProps {
+ address: string,
+ onRemove: () => void;
+}
+
+const DnsListItem = ({ address, onRemove }: DnsListItemProps) => (
{address}
);
-DnsListItem.propTypes = {
- address: PropTypes.string.isRequired,
- onRemove: PropTypes.func.isRequired
-};
-
export default DnsListItem;
diff --git a/src/components/settings/DnsListNewItem.js b/src/components/settings/DnsListNewItem.tsx
similarity index 76%
rename from src/components/settings/DnsListNewItem.js
rename to src/components/settings/DnsListNewItem.tsx
index 3f4621a..54e3a92 100644
--- a/src/components/settings/DnsListNewItem.js
+++ b/src/components/settings/DnsListNewItem.tsx
@@ -8,30 +8,39 @@
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
-import React, { Component } from "react";
-import PropTypes from "prop-types";
+import React, { Component, RefObject } from "react";
import { Button, InputGroup, InputGroupAddon, ListGroupItem } from "reactstrap";
import { Typeahead } from "react-bootstrap-typeahead";
-import { withNamespaces } from "react-i18next";
-import { preconfiguredUpstreamOptions } from "./preconfiguredUpstreams";
+import { WithNamespaces, withNamespaces } from "react-i18next";
+import { PreconfiguredUpstreamOption, preconfiguredUpstreamOptions } from "./preconfiguredUpstreams";
+
+export interface DnsListNewItemProps {
+ onAdd: (address: string) => void;
+ isValid: (address: string) => boolean;
+ upstreams: Array;
+}
+
+export interface DnsListNewItemState {
+ address: string;
+ selected: Array;
+}
/**
* A component to add upstream DNS servers. The servers are either selected from
* a list of preconfigured upstreams, or custom servers can be input.
*/
-class DnsListNewItem extends Component {
- static propTypes = {
- onAdd: PropTypes.func.isRequired,
- isValid: PropTypes.func.isRequired,
- upstreams: PropTypes.arrayOf(PropTypes.string).isRequired
- };
-
- state = {
+class DnsListNewItem extends Component<
+ DnsListNewItemProps & WithNamespaces,
+ DnsListNewItemState
+> {
+ state: DnsListNewItemState = {
address: "",
selected: []
};
- constructor(props) {
+ private readonly typeahead: RefObject;
+
+ constructor(props: DnsListNewItemProps & WithNamespaces) {
super(props);
this.typeahead = React.createRef();
}
diff --git a/src/components/settings/DnsOptionSettings.js b/src/components/settings/DnsOptionSettings.tsx
similarity index 83%
rename from src/components/settings/DnsOptionSettings.js
rename to src/components/settings/DnsOptionSettings.tsx
index 7faa201..60a0bab 100644
--- a/src/components/settings/DnsOptionSettings.js
+++ b/src/components/settings/DnsOptionSettings.tsx
@@ -11,8 +11,26 @@
import React, { Fragment } from "react";
import PropTypes from "prop-types";
import { Col, FormGroup, Input, Label } from "reactstrap";
+import i18next from "i18next";
-const DnsOptionSettings = ({ settings, onUpdate, t }) => (
+export interface DnsOptionsObject {
+ fqdnRequired: boolean;
+ bogusPriv: boolean;
+ dnssec: boolean;
+ listeningType: string;
+}
+
+export interface DnsOptionSettingsProps {
+ settings: DnsOptionsObject;
+ onUpdate: (settings: DnsOptionsObject) => void;
+ t: i18next.TranslationFunction;
+}
+
+const DnsOptionSettings = ({
+ settings,
+ onUpdate,
+ t
+}: DnsOptionSettingsProps) => (