diff --git a/gradle/changelog/icon_for_status.yaml b/gradle/changelog/icon_for_status.yaml new file mode 100644 index 0000000000..07e86fbdd4 --- /dev/null +++ b/gradle/changelog/icon_for_status.yaml @@ -0,0 +1,2 @@ +- type: added + description: Icon for displaying a status like success diff --git a/scm-ui/ui-core/src/base/buttons/Button.tsx b/scm-ui/ui-core/src/base/buttons/Button.tsx index 625aaf4871..35d228d8b7 100644 --- a/scm-ui/ui-core/src/base/buttons/Button.tsx +++ b/scm-ui/ui-core/src/base/buttons/Button.tsx @@ -29,6 +29,7 @@ export const ButtonVariants = { SECONDARY: "secondary", TERTIARY: "tertiary", SIGNAL: "signal", + DANGER: "danger", INFO: "info", } as const; @@ -43,6 +44,7 @@ const createButtonClasses = (variant?: ButtonVariant, isLoading?: boolean) => "is-primary is-inverted": variant === "tertiary", "is-warning": variant === "signal", "is-info is-outlined": variant === "info", + "is-danger": variant === "danger", "is-loading": isLoading, }); diff --git a/scm-ui/ui-core/src/base/status/StatusIcon.stories.tsx b/scm-ui/ui-core/src/base/status/StatusIcon.stories.tsx new file mode 100644 index 0000000000..3a4eb304e4 --- /dev/null +++ b/scm-ui/ui-core/src/base/status/StatusIcon.stories.tsx @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2020 - present Cloudogu GmbH + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the Free + * Software Foundation, version 3. + * + * 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 Affero General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + +import React, { ComponentProps } from "react"; + +import StoryRouter from "storybook-react-router"; +import { StoryFn } from "@storybook/react"; +import { StatusIcon } from "./index"; +import { StatusIconSizeVariantList, StatusVariantList, StatusVariants } from "./StatusIcon"; + +// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +export default { + title: "Status", + component: null, + subcomponents: { + Button: StatusIcon, + }, + argTypes: { + variant: { + options: StatusVariantList, + control: { type: "select" }, + }, + iconSize: { + options: StatusIconSizeVariantList, + control: { type: "select" }, + }, + }, + decorators: [StoryRouter()], + parameters: { + storyshots: { disable: true }, + }, +}; + +// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args +const StatusIconTemplate: StoryFn> = (args) => ; + +export const Icon = StatusIconTemplate.bind({}); +Icon.args = { + variant: StatusVariants.SUCCESS, + invert: false, + iconSize: "lg", +}; + +export const IconWithTitle = StatusIconTemplate.bind({}); +IconWithTitle.args = { + variant: StatusVariants.SUCCESS, + invert: false, + iconSize: "lg", + children: "Lorem Ipsum", +}; diff --git a/scm-ui/ui-core/src/base/status/StatusIcon.tsx b/scm-ui/ui-core/src/base/status/StatusIcon.tsx new file mode 100644 index 0000000000..86763559d7 --- /dev/null +++ b/scm-ui/ui-core/src/base/status/StatusIcon.tsx @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2020 - present Cloudogu GmbH + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the Free + * Software Foundation, version 3. + * + * 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 Affero General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + +import React from "react"; +import classNames from "classnames"; +import { Icon } from "../buttons"; + +export const StatusVariants = { + IN_PROGRESS: "in progress", + SUCCESS: "success", + WARNING: "warning", + DANGER: "danger", + UNDEFINED: "undefined", +}; + +export const StatusVariantList = Object.values(StatusVariants); + +export type StatusVariant = typeof StatusVariants[keyof typeof StatusVariants]; + +export const StatusIconSizeVariants = { + SMALL: "xs", + MEDIUM: "lg", + LARGE: "2x", +}; + +export const StatusIconSizeVariantList = Object.values(StatusIconSizeVariants); + +export type StatusIconSizeVariant = typeof StatusIconSizeVariants[keyof typeof StatusIconSizeVariants]; + +type IconProps = React.HTMLProps & { + variant: StatusVariant; + color?: string; + iconSize?: StatusIconSizeVariant; + invert?: boolean; +}; + +/** + * + * @beta + * @since 3.9.0 + */ +const StatusIcon = React.forwardRef( + ({ color, className, iconSize = StatusIconSizeVariants.MEDIUM, variant, invert = false, children }, ref) => { + const icon = classNames({ + "exclamation-triangle": variant === StatusVariants.DANGER, + "check-circle": variant === StatusVariants.SUCCESS, + "hourglass-start": variant === StatusVariants.IN_PROGRESS, + "circle-notch": variant === StatusVariants.UNDEFINED, + }); + if (!color) { + if (invert) { + color = classNames({ + "icon-color-inverted": variant === StatusVariants.DANGER || StatusVariants.SUCCESS, + "icon-warning-inverted": variant === StatusVariants.WARNING, + "icon-color-inverted-secondary": + variant === StatusVariants.IN_PROGRESS || variant === StatusVariants.UNDEFINED, + }); + } else { + color = classNames({ + "has-text-danger": variant === StatusVariants.DANGER, + "has-text-success": variant === StatusVariants.SUCCESS, + "has-text-warning": variant === StatusVariants.WARNING, + "icon-color-secondary": variant === StatusVariants.IN_PROGRESS || variant === StatusVariants.UNDEFINED, + }); + } + } + + return ( +
+ {variant === "warning" ? ( + {`${icon}`} + ) : ( + {`${icon} ${color} fa-${iconSize}`} + )} + {children && {children}} +
+ ); + } +); + +type WarningIconProps = React.HTMLProps & { + color?: string; + iconSize?: string; +}; + +const WarningIcon = React.forwardRef(({ color, className, iconSize }, ref) => { + return ( + + ); +}); + +export default StatusIcon; diff --git a/scm-ui/ui-core/src/base/status/index.ts b/scm-ui/ui-core/src/base/status/index.ts new file mode 100644 index 0000000000..44a83cede5 --- /dev/null +++ b/scm-ui/ui-core/src/base/status/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2020 - present Cloudogu GmbH + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the Free + * Software Foundation, version 3. + * + * 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 Affero General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + +export { default as StatusIcon, StatusVariant, StatusIconSizeVariant } from "./StatusIcon"; diff --git a/scm-ui/ui-styles/src/dark.scss b/scm-ui/ui-styles/src/dark.scss index bb3d1336a7..27dbf5ecbc 100644 --- a/scm-ui/ui-styles/src/dark.scss +++ b/scm-ui/ui-styles/src/dark.scss @@ -314,3 +314,21 @@ input[type="date"].input::-webkit-calendar-picker-indicator { .popover-close:focus { background: $grey-darker !important; } + +.icon-color-inverted { + color: $white; +} + +.icon-color-inverted-secondary { + color: $white; +} + +.icon-warning-inverted { + color: #583708; +} + +.icon-color-secondary { + color: $grey; +} + + diff --git a/scm-ui/ui-styles/src/highcontrast.scss b/scm-ui/ui-styles/src/highcontrast.scss index 9fd268828c..06b065e8cf 100644 --- a/scm-ui/ui-styles/src/highcontrast.scss +++ b/scm-ui/ui-styles/src/highcontrast.scss @@ -337,3 +337,19 @@ input[type="date"].input::-webkit-calendar-picker-indicator { .popover-close:focus { background: $grey-light !important; } + +.icon-color-inverted { + color: $scheme-main; +} + +.icon-color-inverted-secondary { + color: $white; +} + +.icon-warning-inverted { + color: $scheme-main; +} + +.icon-color-secondary { + color: $white; +} diff --git a/scm-ui/ui-styles/src/light.scss b/scm-ui/ui-styles/src/light.scss index 416241fd66..b50252c188 100644 --- a/scm-ui/ui-styles/src/light.scss +++ b/scm-ui/ui-styles/src/light.scss @@ -90,3 +90,19 @@ footer.footer { .has-hover-visible:hover { color: $grey-darker !important; } + +.icon-color-inverted { + color: $scheme-main; +} + +.icon-color-inverted-secondary { + color: $white; +} + +.icon-warning-inverted { + color: $scheme-main; +} + +.icon-color-secondary { + color: $dark; +}