diff --git a/pkg/shell/active-pages-modal.jsx b/pkg/shell/active-pages-modal.tsx
similarity index 96%
rename from pkg/shell/active-pages-modal.jsx
rename to pkg/shell/active-pages-modal.tsx
index 665497801614..396bbbece989 100644
--- a/pkg/shell/active-pages-modal.jsx
+++ b/pkg/shell/active-pages-modal.tsx
@@ -17,6 +17,8 @@
* along with Cockpit; If not, see .
*/
+// @cockpit-ts-relaxed
+
import cockpit from "cockpit";
import React, { useState } from "react";
@@ -27,9 +29,11 @@ import { Split, SplitItem } from "@patternfly/react-core/dist/esm/layouts/Split/
import { Modal } from "@patternfly/react-core/dist/esm/components/Modal/index.js";
import { useInit } from "hooks";
+import { ShellState } from "./state";
+
const _ = cockpit.gettext;
-export const ActivePagesDialog = ({ dialogResult, state }) => {
+export const ActivePagesDialog = ({ dialogResult, state } : { dialogResult, state: ShellState }) => {
function get_pages() {
const result = [];
for (const frame of Object.values(state.frames)) {
diff --git a/pkg/shell/failures.jsx b/pkg/shell/failures.tsx
similarity index 91%
rename from pkg/shell/failures.jsx
rename to pkg/shell/failures.tsx
index d45f93c72dfd..8c9992b15931 100644
--- a/pkg/shell/failures.jsx
+++ b/pkg/shell/failures.tsx
@@ -17,6 +17,8 @@
* along with Cockpit; If not, see .
*/
+// @cockpit-ts-relaxed
+
import cockpit from "cockpit";
import React from "react";
@@ -26,7 +28,7 @@ import { Page, PageSection, PageSectionVariants } from "@patternfly/react-core/d
import { Stack } from "@patternfly/react-core/dist/esm/layouts/Stack/index.js";
import { ExclamationCircleIcon } from "@patternfly/react-icons";
-import { EmptyStatePanel } from "cockpit-components-empty-state.jsx";
+import { EmptyStatePanel } from "cockpit-components-empty-state";
import { codes } from "./hosts_dialog.jsx";
@@ -61,20 +63,29 @@ export const EarlyFailure = () => {
};
const EarlyFailureReady = ({
- loading,
+ loading = false,
title,
paragraph,
- reconnect,
- troubleshoot,
- onTroubleshoot,
+ reconnect = false,
+ troubleshoot = false,
+ onTroubleshoot = () => {},
watchdog_problem,
- onReconnect
+ onReconnect = () => {},
+} : {
+ loading?: boolean,
+ title: string,
+ paragraph: string,
+ reconnect?: boolean,
+ troubleshoot?: boolean,
+ onTroubleshoot?: () => void,
+ watchdog_problem?: string,
+ onReconnect?: () => void,
}) => {
return (
-
diff --git a/pkg/shell/hosts.jsx b/pkg/shell/hosts.tsx
similarity index 83%
rename from pkg/shell/hosts.jsx
rename to pkg/shell/hosts.tsx
index 5a501a7aa9c5..5c835c3f5f0a 100644
--- a/pkg/shell/hosts.jsx
+++ b/pkg/shell/hosts.tsx
@@ -1,8 +1,28 @@
+/*
+ * This file is part of Cockpit.
+ *
+ * Copyright (C) 2024 Red Hat, Inc.
+ *
+ * Cockpit is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * Cockpit is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Cockpit; If not, see .
+ */
+
+// @cockpit-ts-relaxed
+
import cockpit from "cockpit";
import React from 'react';
import ReactDOM from "react-dom";
-import PropTypes from 'prop-types';
import { Button } from "@patternfly/react-core/dist/esm/components/Button";
import {
CaretDownIcon,
@@ -19,23 +39,30 @@ import { encode_location } from "./util.jsx";
import { split_connection_string } from "./machines/machines";
import { add_host, edit_host, connect_host } from "./hosts_dialog.jsx";
+import { ShellState } from "./state";
+
const _ = cockpit.gettext;
class HostsSelector extends React.Component {
- constructor() {
- super();
+ el: HTMLDivElement;
+ props: { children };
+
+ constructor(props) {
+ super(props);
+ this.props = props;
+
this.el = document.createElement("div");
this.el.className = "view-hosts";
}
componentDidMount() {
const hosts_sel = document.getElementById("nav-hosts");
- hosts_sel.appendChild(this.el);
+ hosts_sel?.appendChild(this.el);
}
componentWillUnmount() {
const hosts_sel = document.getElementById("nav-hosts");
- hosts_sel.removeChild(this.el);
+ hosts_sel?.removeChild(this.el);
}
render() {
@@ -63,10 +90,16 @@ export const CockpitCurrentHost = ({ current_user, machine }) => {
);
};
+interface CockpitHostsState { opened, editing, current_user, current_key }
+
// full host switcher
export class CockpitHosts extends React.Component {
+ props: { selector, host_modal_state, state: ShellState };
+ state: CockpitHostsState;
+
constructor(props) {
super(props);
+ this.props = props;
this.state = {
opened: false,
@@ -91,7 +124,7 @@ export class CockpitHosts extends React.Component {
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.state.current_machine.key !== prevState.current_key) {
- document.getElementById(nextProps.selector).classList.toggle("interact", false);
+ document.getElementById(nextProps.selector)?.classList.toggle("interact", false);
return {
current_key: nextProps.state.current_machine.key,
opened: false,
@@ -102,9 +135,9 @@ export class CockpitHosts extends React.Component {
}
toggleMenu() {
- document.getElementById(this.props.selector).classList.toggle("interact", !this.state.opened);
+ document.getElementById(this.props.selector)?.classList.toggle("interact", !this.state.opened);
- this.setState(s => {
+ this.setState((s: CockpitHostsState) => {
return (
{
opened: !s.opened,
@@ -118,7 +151,7 @@ export class CockpitHosts extends React.Component {
await add_host(this.props.host_modal_state, this.props.state);
}
- async onHostEdit(event, machine) {
+ async onHostEdit(_event, machine) {
await edit_host(this.props.host_modal_state, this.props.state, machine);
}
@@ -133,7 +166,7 @@ export class CockpitHosts extends React.Component {
}
onEditHosts() {
- this.setState(s => { return { editing: !s.editing } });
+ this.setState((s: CockpitHostsState) => { return { editing: !s.editing } });
}
onRemove(event, machine) {
@@ -202,8 +235,8 @@ export class CockpitHosts extends React.Component {
>}
/>;
- const label = current_machine.label || "";
- const user = current_machine.user || this.state.current_user;
+ const label = current_machine?.label || "";
+ const user = current_machine?.user || this.state.current_user;
const add_host_action = ;
@@ -238,7 +271,7 @@ export class CockpitHosts extends React.Component {
selector={this.props.selector}
groups={groups}
item_render={render}
- sorting={(a, b) => true}
+ sorting={(_a, _b) => true}
filtering={this.filterHosts}
current={label}
jump={() => console.error("internal error: jump not supported in hosts selector")}
@@ -254,9 +287,3 @@ export class CockpitHosts extends React.Component {
);
}
}
-
-CockpitHosts.propTypes = {
- state: PropTypes.object.isRequired,
- host_modal_state: PropTypes.object.isRequired,
- selector: PropTypes.string.isRequired,
-};
diff --git a/pkg/shell/hosts_dialog.jsx b/pkg/shell/hosts_dialog.jsx
index aa2f7f22eb4c..e9d34d1aaf55 100644
--- a/pkg/shell/hosts_dialog.jsx
+++ b/pkg/shell/hosts_dialog.jsx
@@ -74,8 +74,7 @@ export const HostModalState = () => {
close_modal,
};
- cockpit.event_target(self);
- return self;
+ return cockpit.event_target(self);
};
/* Activate and jump to a newly added machine, identified by its
diff --git a/pkg/shell/shell.jsx b/pkg/shell/shell.tsx
similarity index 91%
rename from pkg/shell/shell.jsx
rename to pkg/shell/shell.tsx
index 1e1e74fd7b4e..c41f8ef8adea 100644
--- a/pkg/shell/shell.jsx
+++ b/pkg/shell/shell.tsx
@@ -17,6 +17,8 @@
* along with Cockpit; If not, see .
*/
+// @cockpit-ts-relaxed
+
import cockpit from "cockpit";
import React, { useEffect } from 'react';
@@ -47,7 +49,7 @@ const SkipLink = ({ focus_id, children }) => {
{
- document.getElementById(focus_id).focus();
+ document.getElementById(focus_id)?.focus();
ev.preventDefault();
}}>
{children}
@@ -86,6 +88,8 @@ const Shell = () => {
if (!ready)
return null;
+ cockpit.assert(current_machine && current_manifest_item);
+
const title_parts = [];
if (current_manifest_item.label)
title_parts.push(current_manifest_item.label);
@@ -112,7 +116,7 @@ const Shell = () => {
style={
{
'--ct-color-host-accent': (current_machine.address == "localhost" ? undefined : current_machine.color)
- }
+ } as React.CSSProperties
}>
{_("Skip to content")}
@@ -155,6 +159,10 @@ const Shell = () => {
);
};
+interface ShellWindow extends Window {
+ features?: object;
+}
+
function init() {
cockpit.translate();
@@ -165,17 +173,17 @@ function init() {
window.name = "cockpit1";
/* Tell the pages about our features. */
- window.features = {
+ (window as ShellWindow).features = {
navbar_is_for_current_machine: true
};
function follow(arg) {
/* A promise of some sort */
if (arguments.length == 1 && typeof arg.then == "function") {
- arg.then(function() { console.log.apply(console, arguments) },
- function() { console.error.apply(console, arguments) });
+ arg.then(function(...args) { console.log(...args) },
+ function(...args) { console.error(...args) });
if (typeof arg.stream == "function")
- arg.stream(function() { console.log.apply(console, arguments) });
+ arg.stream(function(...args) { console.log(...args) });
}
}
@@ -190,7 +198,7 @@ function init() {
}
});
- const root = createRoot(document.getElementById("shell"));
+ const root = createRoot(document.getElementById("shell")!);
root.render();
}
diff --git a/pkg/shell/superuser.jsx b/pkg/shell/superuser.tsx
similarity index 93%
rename from pkg/shell/superuser.jsx
rename to pkg/shell/superuser.tsx
index 58dd7428e857..aa9911248ec3 100644
--- a/pkg/shell/superuser.jsx
+++ b/pkg/shell/superuser.tsx
@@ -17,6 +17,8 @@
* along with Cockpit; If not, see .
*/
+// @cockpit-ts-relaxed
+
import cockpit from "cockpit";
import React, { useState } from "react";
import { useObject, useInit, useEvent } from "hooks";
@@ -48,14 +50,14 @@ const UnlockDialog = ({ proxy, host }) => {
const D = useDialogs();
useInit(init, [proxy, host]);
- const [methods, setMethods] = useState(null);
- const [method, setMethod] = useState(false);
+ const [methods, setMethods] = useState(null);
+ const [method, setMethod] = useState(false);
const [busy, setBusy] = useState(false);
const [cancel, setCancel] = useState(() => D.close);
- const [prompt, setPrompt] = useState(null);
- const [message, setMessage] = useState(null);
+ const [prompt, setPrompt] = useState<{ message: string, prompt: string, echo: boolean } | null>(null);
+ const [message, setMessage] = useState(null);
const [error, setError] = useState(null);
- const [errorVariant, setErrorVariant] = useState(null);
+ const [errorVariant, setErrorVariant] = useState<"danger" | "warning" | null>(null);
const [value, setValue] = useState("");
function start(method) {
@@ -67,7 +69,7 @@ const UnlockDialog = ({ proxy, host }) => {
let did_prompt = false;
- const onprompt = (event, message, prompt, def, echo, error) => {
+ const onprompt = (_event, message, prompt, def, echo, error) => {
setBusy(false);
setPrompt({
message: sudo_polish(message),
@@ -134,7 +136,7 @@ const UnlockDialog = ({ proxy, host }) => {
const validated = errorVariant == "danger" ? "error" : errorVariant;
let title = null;
- let title_icon = null;
+ let title_icon: null | "danger" = null;
let body = null;
let footer = null;
@@ -230,7 +232,7 @@ const UnlockDialog = ({ proxy, host }) => {
variant="medium"
onClose={cancel}
title={title}
- titleIconVariant={title_icon}
+ titleIconVariant={title_icon || undefined}
footer={footer}>
{body}
@@ -283,7 +285,7 @@ const LockDialog = ({ proxy, host }) => {
);
};
-const SuperuserDialogs = ({ superuser_proxy, host, create_trigger }) => {
+const SuperuserDialogs = ({ superuser_proxy, host = undefined, create_trigger }) => {
const D = useDialogs();
useEvent(superuser_proxy, "changed",
() => {
@@ -338,7 +340,10 @@ export const SuperuserIndicator = ({ proxy, host }) => {
};
export const SuperuserButton = () => {
- const proxy = useObject(() => cockpit.dbus(null, { bus: "internal" }).proxy("cockpit.Superuser", "/superuser"));
+ const proxy = useObject(
+ () => cockpit.dbus(null, { bus: "internal" }).proxy("cockpit.Superuser", "/superuser"),
+ null,
+ []);
const create_trigger = (unlocked, onclick) =>