Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions web/__mocks__/svg.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react';

export default () => (
// Simple SVG square based on a wikimedia example https://commons.wikimedia.org/wiki/SVG_examples
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="48" height="48">
<rect x="0" y="0" width="48" height="48" />
</svg>
);
1 change: 1 addition & 0 deletions web/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ module.exports = {
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
// moduleNameMapper: {},
moduleNameMapper: {
"\\.svg\\?component$": "<rootDir>/__mocks__/svg.js",
...pathsToModuleNameMapper(compilerOptions.paths, { prefix: "<rootDir>/" }),
"\\.(css|scss)$": "identity-obj-proxy",
},
Expand Down
737 changes: 650 additions & 87 deletions web/package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@babel/eslint-parser": "^7.13.14",
"@babel/preset-env": "^7.5.4",
"@babel/preset-react": "^7.0.0",
"@svgr/webpack": "^6.5.1",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^14.1.1",
Expand Down Expand Up @@ -75,12 +76,12 @@
"webpack-cli": "^5.0.0"
},
"dependencies": {
"@material-symbols/svg-400": "^0.4.2",
"@patternfly/patternfly": "^4.219.2",
"@patternfly/react-core": "^4.261.0",
"@patternfly/react-table": "^4.111.33",
"core-js": "^3.21.1",
"eos-ds": "^5.0.0",
"eos-icons-react": "^2.3.0",
"ipaddr.js": "^2.0.1",
"react": "17.0.2",
"react-dom": "17.0.2",
Expand Down
6 changes: 6 additions & 0 deletions web/package/cockpit-d-installer.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
-------------------------------------------------------------------
Fri Dec 30 11:56:46 UTC 2022 - David Diaz <dgonzalez@suse.com>

- Switch to Material Symbols icon set and refactor how icons
are imported (gh#yast/d-installer#383).

-------------------------------------------------------------------
Thu Dec 15 10:14:22 UTC 2022 - Knut Anderssen <kanderssen@suse.com>

Expand Down
11 changes: 3 additions & 8 deletions web/src/components/core/InstallationFinished.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,17 @@ import {
EmptyStateBody
} from "@patternfly/react-core";

import { Center, Title as SectionTitle, PageIcon, MainActions } from "@components/layout";
import { Icon, Center, Title as SectionTitle, PageIcon, MainActions } from "@components/layout";
import { useInstallerClient } from "@context/installer";

import {
EOS_TASK_ALT as InstallationFinishedIcon,
EOS_CHECK_CIRCLE as SectionIcon
} from "eos-icons-react";

function InstallationFinished() {
const client = useInstallerClient();
const onRebootAction = () => client.manager.rebootSystem();

return (
<>
<SectionTitle>Installation Finished</SectionTitle>
<PageIcon><SectionIcon /></PageIcon>
<PageIcon><Icon name="task_alt" /></PageIcon>
<MainActions>
<Button isLarge variant="primary" onClick={onRebootAction}>
Reboot
Expand All @@ -53,7 +48,7 @@ function InstallationFinished() {

<Center>
<EmptyState>
<EmptyStateIcon icon={InstallationFinishedIcon} className="success-icon" />
<EmptyStateIcon icon={() => <Icon name="check_circle" />} className="success-icon" />
<Title headingLevel="h2" size="4xl">
Congratulations!
</Title>
Expand Down
6 changes: 2 additions & 4 deletions web/src/components/core/InstallationProgress.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,14 @@
import React from "react";

import ProgressReport from "./ProgressReport";
import { Center, Title, PageIcon } from "@components/layout";
import { Center, Icon, Title, PageIcon } from "@components/layout";
import { Questions } from "@components/questions";

import { EOS_DOWNLOADING as Icon } from "eos-icons-react";

function InstallationProgress() {
return (
<>
<Title>Installing</Title>
<PageIcon><Icon /></PageIcon>
<PageIcon><Icon name="downloading" /></PageIcon>
<Center><ProgressReport /></Center>
<Questions />
</>
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/core/KebabMenu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import React, { useState } from "react";
import { Dropdown, DropdownToggle } from "@patternfly/react-core";
import { EOS_MORE_VERT as MenuIcon } from "eos-icons-react";
import { Icon } from "@components/layout";

export default function KebabMenu({ items, position = "right", id = Date.now(), ...props }) {
const [isOpen, setIsOpen] = useState(false);
Expand All @@ -33,7 +33,7 @@ export default function KebabMenu({ items, position = "right", id = Date.now(),
onSelect={toggle}
toggle={
<DropdownToggle id={`${id}-toggler`} className="toggler" toggleIndicator={null} onToggle={toggle}>
<MenuIcon />
<Icon name="more_vert" size="24" />
</DropdownToggle>
}
isPlain
Expand Down
19 changes: 10 additions & 9 deletions web/src/components/core/Section.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,16 @@ import {
Tooltip
} from "@patternfly/react-core";

import { classNames } from "@/utils";
import { Icon } from '@components/layout';
import { ValidationErrors } from "@components/core";

import { CogIcon } from '@patternfly/react-icons';
import { classNames } from "@/utils";

import "./section.scss";

const SettingsIcon = ({ ...props }) => <Icon name="settings" {...props} />;

/**
* Helper method for rendering section react-icons
* Helper method for rendering section icon
*
* @param {React.FunctionComponent|React.ComponentClass} icon
* @param {string} ariaLabel
Expand All @@ -53,11 +54,11 @@ import "./section.scss";
const renderIcon = (icon, ariaLabel, size = 32) => {
if (!icon) return null;

const Icon = icon;
const SectionIcon = icon;

return (
<figure aria-label={ariaLabel}>
<Icon size={size} />
<SectionIcon size={size} />
</figure>
);
};
Expand Down Expand Up @@ -104,7 +105,7 @@ const renderIcon = (icon, ariaLabel, size = 32) => {
* @param {boolean} [props.usingSeparator] - whether or not a thin border should be shown between title and content
* @param {React.FunctionComponent} [props.icon] - An icon for the section
* @param {import("@client/mixins").ValidationError[]} [props.errors] - Validation errors to be shown before the title
* @param {React.FunctionComponent|React.ComponentClass} [props.actionIcon=CogIcon] - An icon to be used for section actions
* @param {React.FunctionComponent|React.ComponentClass} [props.actionIcon=SettingsIcon] - An icon component to be used for section actions
* @param {React.ReactNode} [props.actionTooltip] - text to be shown as a tooltip when user hovers action icon, if present
* @param {React.MouseEventHandler} [props.onActionClick] - callback to be triggered when user clicks on action icon, if present
* @param {JSX.Element} [props.children] - the section content
Expand All @@ -116,7 +117,7 @@ export default function Section({
usingSeparator,
icon,
errors,
actionIcon = CogIcon,
actionIcon = SettingsIcon,
actionTooltip,
onActionClick,
children,
Expand All @@ -127,7 +128,7 @@ export default function Section({

const Action = () => (
<Button variant="plain" className="d-installer-section-action" isInline onClick={onActionClick}>
{renderIcon(actionIcon, `${title} section action icon`)}
{renderIcon(actionIcon, `${title} section action icon`, 16)}
</Button>
);

Expand Down
8 changes: 5 additions & 3 deletions web/src/components/core/ValidationErrors.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
Popover
} from "@patternfly/react-core";

import ExclamationTriangleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-triangle-icon';
import { Icon } from '@components/layout';

/**
* @param {import("@client/mixins").ValidationError[]} errors - Validation errors
Expand Down Expand Up @@ -63,17 +63,19 @@ const ValidationErrors = ({ title = "Errors", errors }) => {

if (!errors || errors.length === 0) return null;

const warningIcon = <Icon name="warning" size="16" />;

if (errors.length === 1) {
return (
<>
<div className="warning-text"><ExclamationTriangleIcon /> {errors[0].message}</div>
<div className="warning-text">{warningIcon} {errors[0].message}</div>
</>
);
} else {
return (
<>
<div className="warning-text">
<ExclamationTriangleIcon />
{ warningIcon }
<a href="#" onClick={() => setPopoverVisible(true)}>{`${errors.length} errors found`}</a>
<Popover
isVisible={popoverVisible}
Expand Down
12 changes: 4 additions & 8 deletions web/src/components/layout/DBusError.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,13 @@ import React from "react";
import { Button, Title, EmptyState, EmptyStateIcon, EmptyStateBody } from "@patternfly/react-core";

import {
Icon,
Center,
MainActions,
PageIcon,
Title as PageTitle,
} from "@components/layout";

import {
EOS_ANNOUNCEMENT as Icon,
EOS_ENDPOINTS_DISCONNECTED as DisconnectionIcon
} from "eos-icons-react";

// TODO: an example
const ReloadAction = () => (
<Button isLarge variant="primary" onClick={() => location.reload()}>
Expand All @@ -45,13 +41,13 @@ function DBusError() {
return (
<>
<PageTitle>D-Bus Error</PageTitle>
<PageIcon><Icon /></PageIcon>
<PageIcon><Icon name="problem" /></PageIcon>
<MainActions><ReloadAction /></MainActions>

<Center>
<EmptyState>
<EmptyStateIcon icon={DisconnectionIcon} />
<Title headingLevel="h4" size="lg">
<EmptyStateIcon icon={({ ...props }) => <Icon name="error" { ...props } />} />
<Title headingLevel="h2" size="4xl">
Cannot connect to D-Bus
</Title>
<EmptyStateBody>
Expand Down
104 changes: 104 additions & 0 deletions web/src/components/layout/Icon.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright (c) [2022] SUSE LLC
*
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as published
* by the Free Software Foundation.
*
* 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, contact SUSE LLC.
*
* To contact SUSE LLC about this file by physical or electronic mail, you may
* find current contact information at www.suse.com.
*/

import React from 'react';

// NOTE: "~icons" is an alias to use a shorter path to real icons location.
// Check the tsconfig.json file to see its value.
import Inventory from "~icons/inventory_2.svg?component";
import Translate from "~icons/translate.svg?component";
import SettingsEthernet from "~icons/settings_ethernet.svg?component";
import EditSquare from "~icons/edit_square.svg?component";
import Edit from "~icons/edit.svg?component";
import HardDrive from "~icons/hard_drive.svg?component";
import ManageAccounts from "~icons/manage_accounts.svg?component";
import HomeStorage from "~icons/home_storage.svg?component";
import Problem from "~icons/problem.svg?component";
import Error from "~icons/error.svg?component";
import CheckCircle from "~icons/check_circle.svg?component";
import TaskAlt from "~icons/task_alt.svg?component";
import Downloading from "~icons/downloading.svg?component";
import MoreVert from "~icons/more_vert.svg?component";
import Wifi from "~icons/wifi.svg?component";
import Lan from "~icons/lan.svg?component";
import Lock from "~icons/lock.svg?component";
import SignalCellularAlt from "~icons/signal_cellular_alt.svg?component";
import SettingsFill from "~icons/settings-fill.svg?component";
import SettingsApplications from "~icons/settings_applications.svg?component";
import Info from "~icons/info.svg?component";
import Delete from "~icons/delete.svg?component";
import Warning from "~icons/warning.svg?component";
import Loading from "./three-dots-loader-icon.svg?component";

const icons = {
check_circle: CheckCircle,
delete: Delete,
downloading: Downloading,
edit: Edit,
edit_square: EditSquare,
error: Error,
hard_drive: HardDrive,
home_storage: HomeStorage,
info: Info,
inventory_2: Inventory,
lan: Lan,
loading: Loading,
lock: Lock,
manage_accounts: ManageAccounts,
more_vert: MoreVert,
problem: Problem,
settings: SettingsFill,
settings_applications: SettingsApplications,
settings_ethernet: SettingsEthernet,
signal_cellular_alt: SignalCellularAlt,
task_alt: TaskAlt,
translate: Translate,
warning: Warning,
wifi: Wifi,
};

/**
* D-Installer Icon component
*
* If exists, it renders requested icon with given size.
*
* @todo: import icons dynamically if the list grows too much. See
* - https://stackoverflow.com/a/61472427
* - https://ryanhutzley.medium.com/dynamic-svg-imports-in-create-react-app-d6d411f6d6c6
*
* @todo: find how to render the "icon not found" warning only in _development_ mode
*
* @example
* <Icon name="warning" size="16" />
*
* @param {object} props - component props
* @param {string} props.name - desired icon
* @param {string|number} [props.size=32] - the icon width and height
* @param {object} [props.otherProps] other props sent to SVG icon
*
*/
export default function Icon({ name, size = 32, ...otherProps }) {
const IconComponent = icons[name];

return (IconComponent)
? <IconComponent width={size} height={size} {...otherProps} />
: <em>`icon ${name} not found!`</em>;
}
4 changes: 2 additions & 2 deletions web/src/components/layout/Layout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import React from "react";

import "./layout.scss";
import logo from "@assets/suse-horizontal-logo.svg";
import logoUrl from "@assets/suse-horizontal-logo.svg";
import { createTeleporter } from "react-teleporter";

const PageTitle = createTeleporter();
Expand Down Expand Up @@ -77,7 +77,7 @@ function Layout({ children }) {

<div className="layout__footer">
<div className="layout__footer-info-area">
<img src={logo} alt="Logo of SUSE" className="company-logo" />
<img src={logoUrl} alt="Logo of SUSE" className="company-logo" />
<FooterInfoArea.Target />
</div>
<FooterActions.Target
Expand Down
8 changes: 3 additions & 5 deletions web/src/components/layout/LoadingEnvironment.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,14 @@
import React from "react";
import { Title, EmptyState, EmptyStateIcon } from "@patternfly/react-core";

import { Center } from "@components/layout";

import { EOS_THREE_DOTS_LOADING_ANIMATED as LoadingIcon } from "eos-icons-react";
import { Icon, Center } from "@components/layout";

function LoadingEnvironment({ text = "Loading installation environment, please wait." }) {
return (
<Center>
<EmptyState>
<EmptyStateIcon icon={LoadingIcon} />
<Title headingLevel="h4" size="lg">
<EmptyStateIcon icon={({ ...props }) => <Icon name="loading" {...props} />} />
<Title headingLevel="h2" size="4xl">
{ text }
</Title>
</EmptyState>
Expand Down
1 change: 1 addition & 0 deletions web/src/components/layout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

export * from "./Layout";
export { default as Layout } from "./Layout";
export { default as Icon } from "./Icon";
export { default as Center } from "./Center";
export { default as DBusError } from "./DBusError";
export { default as LoadingEnvironment } from "./LoadingEnvironment";
Loading