Move to PatternFly Elements and lit

PFE uses lit, so it makes sense to just use that for the main
application as well. Web components are pretty much React built into the
web platform, and lit adds some convenience around that.

This is mostly a demo -- for real Cockpit pages, PF Elements is still
missing too many components.
This commit is contained in:
Martin Pitt 2024-11-08 09:05:22 +01:00
parent 6506f469ac
commit 5aa20207b7
11 changed files with 51 additions and 73 deletions

View file

@ -4,12 +4,11 @@
"browser": true,
"es6": true
},
"extends": ["eslint:recommended", "standard", "standard-jsx", "standard-react"],
"extends": ["eslint:recommended", "standard"],
"parserOptions": {
"ecmaVersion": "2022",
"sourceType": "module"
},
"plugins": ["react", "react-hooks"],
"rules": {
"indent": ["error", 4,
{
@ -22,12 +21,8 @@
"no-var": "error",
"lines-between-class-members": ["error", "always", { "exceptAfterSingleLine": true }],
"prefer-promise-reject-errors": ["error", { "allowEmptyReject": true }],
"react/jsx-indent": ["error", 4],
"semi": ["error", "always", { "omitLastInOneLineBlock": true }],
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "error",
"camelcase": "off",
"comma-dangle": "off",
"curly": "off",
@ -35,10 +30,6 @@
"key-spacing": "off",
"no-console": "off",
"quotes": "off",
"react/jsx-curly-spacing": "off",
"react/jsx-indent-props": "off",
"react/jsx-no-useless-fragment": "error",
"react/prop-types": "off",
"space-before-function-paren": "off",
"standard/no-callback-literal": "off"
},

View file

@ -30,10 +30,6 @@ updates:
- "types*"
ignore:
# https://github.com/cockpit-project/cockpit/issues/21151
- dependency-name: "sass"
versions: [">=1.80.0", "2.x"]
# needs to be done in Cockpit first
- dependency-name: "@patternfly/*"
update-types: ["version-update:semver-major"]

View file

@ -18,8 +18,6 @@
"stylelint:fix": "stylelint --fix src/*{.css,scss}"
},
"devDependencies": {
"@types/react": "18.3.12",
"@types/react-dom": "18.3.1",
"@typescript-eslint/eslint-plugin": "8.15.0",
"argparse": "2.0.1",
"esbuild": "0.24.0",
@ -30,18 +28,14 @@
"eslint": "8.57.1",
"eslint-config-standard": "17.1.0",
"eslint-config-standard-jsx": "11.0.0",
"eslint-config-standard-react": "13.0.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "6.6.0",
"eslint-plugin-react": "7.37.2",
"eslint-plugin-react-hooks": "4.6.2",
"gettext-parser": "8.0.0",
"glob": "11.0.0",
"htmlparser": "1.7.7",
"jed": "1.1.1",
"qunit": "2.22.0",
"sass": "1.79.6",
"stylelint": "16.10.0",
"stylelint-config-recommended-scss": "14.0.0",
"stylelint-config-standard": "36.0.1",
@ -50,11 +44,7 @@
"typescript": "5.6.3"
},
"dependencies": {
"@patternfly/patternfly": "5.4.2",
"@patternfly/react-core": "5.4.8",
"@patternfly/react-icons": "5.4.2",
"@patternfly/react-styles": "5.4.1",
"react": "18.3.1",
"react-dom": "18.3.1"
"@patternfly/elements": "4.0.2",
"lit": "3.2.1"
}
}

View file

@ -57,7 +57,7 @@ appstream-util validate-relax --nonet %{buildroot}/%{_datadir}/metainfo/*
%files
%doc README.md
%license LICENSE dist/index.js.LEGAL.txt dist/index.css.LEGAL.txt
%license LICENSE dist/index.js.LEGAL.txt
%{_datadir}/cockpit/*
%{_datadir}/metainfo/*

View file

@ -1,5 +0,0 @@
@use "page.scss";
p {
font-weight: bold;
}

View file

@ -1,7 +1,7 @@
/*
* This file is part of Cockpit.
*
* Copyright (C) 2017 Red Hat, Inc.
* 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
@ -17,32 +17,36 @@
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useEffect, useState } from 'react';
import { Alert } from "@patternfly/react-core/dist/esm/components/Alert/index.js";
import { Card, CardBody, CardTitle } from "@patternfly/react-core/dist/esm/components/Card/index.js";
import { css, html, LitElement } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import '@patternfly/elements/pf-card/pf-card.js';
import cockpit from 'cockpit';
const _ = cockpit.gettext;
export const Application = () => {
const [hostname, setHostname] = useState(_("Unknown"));
@customElement('ct-application')
export class Application extends LitElement {
@state() private accessor hostname = _("Unknown");
useEffect(() => {
static readonly styles = css`
.running-on {
color: green;
}
`;
connectedCallback() {
super.connectedCallback();
const hostname = cockpit.file('/etc/hostname');
hostname.watch(content => setHostname(content?.trim() ?? ""));
return hostname.close;
}, []);
hostname.watch(content => { this.hostname = content?.trim() ?? "" });
}
return (
<Card>
<CardTitle>Starter Kit</CardTitle>
<CardBody>
<Alert
variant="info"
title={ cockpit.format(_("Running on $0"), hostname) }
/>
</CardBody>
</Card>
);
};
render() {
return html`
<pf-card>
<h1 slot="header">Starter Kit</h1>
<p class="running-on">${cockpit.format(_("Running on $0"), this.hostname)}</p>
</pf-card>`;
}
}

View file

@ -29,6 +29,6 @@ along with this package; If not, see <http://www.gnu.org/licenses/>.
</head>
<body>
<div id="app"></div>
<ct-application></ct-application>
</body>
</html>

View file

@ -1,7 +1,7 @@
/*
* This file is part of Cockpit.
*
* Copyright (C) 2017 Red Hat, Inc.
* 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
@ -17,16 +17,6 @@
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
*/
import React from 'react';
import { createRoot } from 'react-dom/client';
// import "cockpit-dark-theme"; // doesn't work for PFE
import "cockpit-dark-theme";
import { Application } from './app.jsx';
import "patternfly/patternfly-5-cockpit.scss";
import './app.scss';
document.addEventListener("DOMContentLoaded", () => {
createRoot(document.getElementById("app")!).render(<Application />);
});
import './ct-application.ts';

View file

@ -7,6 +7,19 @@ git init
rm -f bots # common local case: existing bots symlink
make bots test/common
# support running from clean git tree
if [ -e .git ]; then
# move package.json temporarily otherwise npm might try to install the dependencies from it
rm -f package-lock.json # otherwise the command below installs *everything*, argh
mv package.json .package.json
# only install a subset to save time/space
npm install query-selector-shadow-dom
mv .package.json package.json
else
# upstream tarballs ship test dependencies; print version for debugging
grep '"version"' node_modules/query-selector-shadow-dom/package.json
fi
# disable detection of affected tests; testing takes too long as there is no parallelization
mv .git dot-git

View file

@ -18,16 +18,16 @@ class TestApplication(testlib.MachineCase):
self.login_and_go("/starter-kit")
# verify expected heading
b.wait_text(".pf-v5-c-card__title", "Starter Kit")
b.wait_text("pf-card [slot='header']", "Starter Kit")
# verify expected host name
hostname = m.execute("cat /etc/hostname").strip()
b.wait_in_text(".pf-v5-c-alert__title", "Running on " + hostname)
b.wait_in_text("pf-card .running-on", "Running on " + hostname)
# change current hostname
self.write_file("/etc/hostname", "new-" + hostname)
# verify new hostname name
b.wait_in_text(".pf-v5-c-alert__title", "Running on new-" + hostname)
b.wait_in_text("pf-card .running-on", "Running on new-" + hostname)
# change language to German
b.switch_to_top()
@ -46,7 +46,7 @@ class TestApplication(testlib.MachineCase):
b.go("/starter-kit")
b.enter_page("/starter-kit")
# page label (from js) should be translated
b.wait_in_text(".pf-v5-c-alert__title", "Läuft auf")
b.wait_in_text("pf-card .running-on", "Läuft auf")
if __name__ == '__main__':

View file

@ -3,7 +3,6 @@
"allowJs": true,
"checkJs": true,
"exactOptionalPropertyTypes": true,
"jsx": "react",
"lib": [
"dom",
"es2020"