Skip to content

Commit

Permalink
webui: provide an About screen
Browse files Browse the repository at this point in the history
  • Loading branch information
acruzgon committed Jul 19, 2023
1 parent dda9ace commit f133b48
Show file tree
Hide file tree
Showing 7 changed files with 282 additions and 1 deletion.
100 changes: 100 additions & 0 deletions ui/webui/src/components/AnacondaAboutModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright (C) 2023 Red Hat, Inc.
*
* This program 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.
*
* This program 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 This program; If not, see <http://www.gnu.org/licenses/>.
*/
import cockpit from "cockpit";

import React, { useState, useEffect } from "react";
import {
AboutModal,
Button,
Flex,
TextContent,
TextList,
TextListItem,
Stack,
StackItem
} from "@patternfly/react-core";
import { ExternalLinkAltIcon } from "@patternfly/react-icons";

import { getAnacondaVersion } from "../helpers/product.js";
import { LanguageContext } from "./Common.jsx";

import "./AnacondaAboutModal.scss";

const _ = cockpit.gettext;

const AboutModalVersions = ({ anacondaVersion }) => {
const arr = [["Anaconda", anacondaVersion]];

return (
<TextContent>
<TextList id="about-modal-content" component="dl">
{arr.map((item) => (
<React.Fragment key={item[0]}>
<TextListItem component="dt">{item[0]}</TextListItem>
<TextListItem component="dd">{item[1]}</TextListItem>
</React.Fragment>
))}
</TextList>
</TextContent>
);
};

const ProductName = ({ prettyName }) => {
return (
<Stack hasGutter>
<StackItem id="about-modal-title">{prettyName}</StackItem>
<StackItem id="about-modal-subtitle" className="subtitle">{_("Powered by Anaconda")}</StackItem>
</Stack>
);
};

export const AnacondaAboutModal = ({ isModalOpen, setIsAboutModalOpen }) => {
const [anacondaVersion, setAnacondaVersion] = useState("");
const { prettyName } = React.useContext(LanguageContext);

const toggleModal = () => {
setIsAboutModalOpen(!isModalOpen);
};

useEffect(() => {
getAnacondaVersion().then(content => setAnacondaVersion(content));
}, []);

return (
<AboutModal
noAboutModalBoxContentContainer
isOpen={isModalOpen}
onClose={toggleModal}
trademark="Anaconda © 2023 Red Hat, Inc."
productName={<ProductName prettyName={prettyName} />}
>
<Flex id="about-modal-flex2" direction={{ default: "column" }} justifyContent={{ default: "justifyContentSpaceBetween" }}>
<AboutModalVersions anacondaVersion={anacondaVersion} />
<Button
isInline
id="anaconda-page-button"
variant="link"
icon={<ExternalLinkAltIcon />}
href="https://github.com/rhinstaller/anaconda"
target="_blank"
component="a">
{_("Anaconda project page")}
</Button>
</Flex>
</AboutModal>
);
};
48 changes: 48 additions & 0 deletions ui/webui/src/components/AnacondaAboutModal.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
@import "global-variables";

.pf-c-about-modal-box__strapline {
padding-top: 0;
}

/* Scale down the about box to be a little smaller */
@media only screen and (min-width: 992px) {
.pf-c-about-modal-box {
--pf-c-about-modal-box--lg--MaxWidth: 62rem;
--pf-c-about-modal-box--lg--MaxHeight: 38rem;
grid-template-rows: 3rem max-content auto;
grid-template-columns: 1fr 0fr;
}
}

/* Hide the logo and side graphic */
.pf-c-about-modal-box__brand,
.pf-c-about-modal-box__hero {
display: none;
}

/* Make the close button more obvious, since we hide the side graphic*/
.pf-c-about-modal-box__close .pf-c-button.pf-m-plain {
--pf-c-about-modal-box__close--c-button--BackgroundColor: var(--pf-global--BackgroundColor--dark-200);
position: absolute;
}
.pf-c-about-modal-box__close .pf-c-button.pf-m-plain:hover {
--pf-c-about-modal-box__close--c-button--BackgroundColor: var(--pf-global--BackgroundColor--dark-400);
}

.pf-c-about-modal-box {
.subtitle {
font-size: var(--pf-c-title--m-2xl--FontSize);
}

.pf-c-about-modal-box__body {
height: 100%;

> .pf-l-flex {
height: 100%;
}

#about-modal-content {
font-size: large;
}
}
}
2 changes: 2 additions & 0 deletions ui/webui/src/components/AnacondaHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
TextContent, Text, TextVariants
} from "@patternfly/react-core";
import { InfoCircleIcon } from "@patternfly/react-icons";
import { HeaderKebab } from "./HeaderKebab";

const _ = cockpit.gettext;

Expand Down Expand Up @@ -66,6 +67,7 @@ export const AnacondaHeader = ({ beta, title }) => {
<Text component="h1">{title}</Text>
</TextContent>
{betanag}
<HeaderKebab />
</Flex>
</PageSection>
);
Expand Down
72 changes: 72 additions & 0 deletions ui/webui/src/components/HeaderKebab.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright (C) 2023 Red Hat, Inc.
*
* This program 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.
*
* This program 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 This program; If not, see <http://www.gnu.org/licenses/>.
*/
import cockpit from "cockpit";

import React, { useState } from "react";
import { Dropdown, DropdownItem, DropdownPosition, KebabToggle } from "@patternfly/react-core";
import { AnacondaAboutModal } from "./AnacondaAboutModal";

const _ = cockpit.gettext;

export const HeaderKebab = () => {
const [isOpen, setIsOpen] = useState(false);
const [isAboutModalOpen, setIsAboutModalOpen] = useState(false);

const onToggle = isOpen => {
setIsOpen(isOpen);
};
const onFocus = () => {
const element = document.getElementById("toggle-kebab");
element.focus();
};
const onSelect = () => {
setIsOpen(false);
onFocus();
};

const handleAboutModal = () => {
setIsAboutModalOpen(true);
};

const dropdownItems = [
<DropdownItem id="about-modal-dropdown-item" key="separated link" onClick={handleAboutModal}>
{_("About")}
</DropdownItem>,
];
return (
<>
<Dropdown
position={DropdownPosition.right}
onSelect={onSelect}
toggle={
<KebabToggle
id="toggle-kebab"
onToggle={onToggle}
/>
}
isOpen={isOpen}
isPlain
dropdownItems={dropdownItems}
/>
{isAboutModalOpen &&
<AnacondaAboutModal
isModalOpen={isAboutModalOpen}
setIsAboutModalOpen={setIsAboutModalOpen}
/>}
</>
);
};
2 changes: 1 addition & 1 deletion ui/webui/src/components/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export const Application = () => {

return (
<WithDialogs>
<LanguageContext.Provider value={{ language, setLanguage }}>
<LanguageContext.Provider value={{ language, setLanguage, prettyName }}>
<HelpDrawer
isExpanded={isHelpExpanded}
setIsExpanded={setIsHelpExpanded}
Expand Down
23 changes: 23 additions & 0 deletions ui/webui/src/helpers/product.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (C) 2023 Red Hat, Inc.
*
* This program 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.
*
* This program 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 This program; If not, see <http://www.gnu.org/licenses/>.
*/
import cockpit from "cockpit";

export const getAnacondaVersion = () => {
return cockpit
.spawn(["anaconda", "--version"])
.then((content) => content.split(" ").slice(-1)[0].replace("\n", ""));
};
36 changes: 36 additions & 0 deletions ui/webui/test/check-basic
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,42 @@ class TestBasic(anacondalib.VirtInstallMachineCase):
# Back button should be disabled on the first screen
b.wait_visible(f"#installation-back-btn:not([aria-disabled={True}]")

def testAboutModal(self):
b = self.browser
m = self.machine
i = Installer(b, m)

i.open()

# To obtain pretty name and validate
pretty_name = m.execute("cat /etc/os-release | grep PRETTY_NAME | cut -d '\"' -f 2 | tr -d '\n'")

# Click on the kebab
b.click("#toggle-kebab")

#Click on the "About" item from the dropdown
b.click("#about-modal-dropdown-item")

# Expect pretty_name to be shown in modal title
b.wait_in_text("#about-modal-title", pretty_name)

# Expect "Powered by Anaconda" to be shown in modal subtitle
b.wait_in_text("#about-modal-subtitle", "Powered by Anaconda")

# Expect About modal body to be shown
b.wait_visible("#about-modal-content")
b.wait_in_text("#about-modal-content dt", "Anaconda")

# Expect button link for Anaconda project page to be shown and click on it
b.wait_in_text("#anaconda-page-button", "Anaconda project page")
b.click("#anaconda-page-button")

# Expect trademark to be shown in modal content
b.wait_in_text(".pf-c-about-modal-box__strapline", "Anaconda © 2023 Red Hat, Inc.")

#Close about modal
b.click(".pf-c-button[aria-label='Close Dialog']")

class TestQuit(anacondalib.VirtInstallMachineCase):

def testQuitInstallation(self):
Expand Down

0 comments on commit f133b48

Please sign in to comment.