From 7ecaca0334431a782c8b76497edc83de083f0ca6 Mon Sep 17 00:00:00 2001 From: Martin Pitt Date: Fri, 8 Nov 2024 09:05:22 +0100 Subject: [PATCH] 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. Install query-selector-shadow-dom to enable testlib's shiny new support for traversing selectors through shadow DOMs from https://github.com/cockpit-project/cockpit/pull/21233 --- .eslintrc.json | 11 +---------- Makefile | 10 ++++++++-- package.json | 14 +++----------- src/app.scss | 7 ++++--- src/app.tsx | 42 ++++++++++++++++++++---------------------- src/index.html | 2 +- src/index.tsx | 15 ++++----------- test/check-application | 8 ++++---- tsconfig.json | 1 - 9 files changed, 45 insertions(+), 65 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index d499b3eb..08c37301 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -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" }, diff --git a/Makefile b/Makefile index ed6f3af0..2eb927f6 100644 --- a/Makefile +++ b/Makefile @@ -114,6 +114,12 @@ devel-uninstall: print-version: @echo "$(VERSION)" +# required for running integration tests +TEST_NPMS = \ + node_modules/query-selector-shadow-dom \ + $(NULL) + + dist: $(TARFILE) @ls -1 $(TARFILE) @@ -125,8 +131,8 @@ $(TARFILE): export NODE_ENV=production $(TARFILE): $(DIST_TEST) $(SPEC) if type appstream-util >/dev/null 2>&1; then appstream-util validate-relax --nonet *.metainfo.xml; fi tar --xz $(TAR_ARGS) -cf $(TARFILE) --transform 's,^,$(RPM_NAME)/,' \ - --exclude packaging/$(SPEC).in --exclude node_modules \ - $$(git ls-files) $(COCKPIT_REPO_FILES) $(NODE_MODULES_TEST) $(SPEC) dist/ + --exclude packaging/$(SPEC).in \ + $$(git ls-files) $(COCKPIT_REPO_FILES) $(NODE_MODULES_TEST) $(SPEC) $(TEST_NPMS) dist/ $(NODE_CACHE): $(NODE_MODULES_TEST) tar --xz $(TAR_ARGS) -cf $@ node_modules diff --git a/package.json b/package.json index 2169d295..2f328e2b 100644 --- a/package.json +++ b/package.json @@ -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.13.0", "argparse": "2.0.1", "esbuild": "0.24.0", @@ -30,16 +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", + "query-selector-shadow-dom": "1.0.1", "qunit": "2.22.0", "sass": "1.80.3", "stylelint": "16.10.0", @@ -50,11 +46,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" } } diff --git a/src/app.scss b/src/app.scss index 6d2c5d8b..803c34a9 100644 --- a/src/app.scss +++ b/src/app.scss @@ -1,5 +1,6 @@ -@use "page.scss"; +// requires PF5, needs counterpart for PFE +// @use "page.scss"; -p { - font-weight: bold; +.running-on { + color: green; } diff --git a/src/app.tsx b/src/app.tsx index 0d3b12fd..aa8973e0 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -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,30 @@ * along with Cockpit; If not, see . */ -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 { 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(() => { + 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 ( - - Starter Kit - - - - - ); -}; + render() { + return html` + +

Starter Kit

+

${cockpit.format(_("Running on $0"), this.hostname)}

+
`; + } +} diff --git a/src/index.html b/src/index.html index 2b06bb4f..b606d7f8 100644 --- a/src/index.html +++ b/src/index.html @@ -29,6 +29,6 @@ -
+ diff --git a/src/index.tsx b/src/index.tsx index 51254c0a..4499a05f 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -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,9 @@ * along with Cockpit; If not, see . */ -import React from 'react'; -import { createRoot } from 'react-dom/client'; +// import "cockpit-dark-theme"; // doesn't work for PFE -import "cockpit-dark-theme"; +import './app.jsx'; -import { Application } from './app.jsx'; - -import "patternfly/patternfly-5-cockpit.scss"; +// import "patternfly/patternfly-5-cockpit.scss"; // does not apply to PFE import './app.scss'; - -document.addEventListener("DOMContentLoaded", () => { - createRoot(document.getElementById("app")!).render(); -}); diff --git a/test/check-application b/test/check-application index 3e85d0f9..cc362e12 100755 --- a/test/check-application +++ b/test/check-application @@ -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__': diff --git a/tsconfig.json b/tsconfig.json index c4b1669d..e0e3c019 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,6 @@ "allowJs": true, "checkJs": true, "exactOptionalPropertyTypes": true, - "jsx": "react", "lib": [ "dom", "es2020"