diff --git a/src/components/Badge/Badge.scss b/src/components/Badge/Badge.scss index f31ec355..05de1bf4 100644 --- a/src/components/Badge/Badge.scss +++ b/src/components/Badge/Badge.scss @@ -6,9 +6,9 @@ $white_color: linear-gradient(#e6e9e9, #f2f3f4); $success_color: $gradient-color-blue-5; $warning_color: $gradient-color-yellow; $danger_color: var(--du-badge-danger, #ec3f3f); -$blue_color: var(--du-badge-blue, #115BFF); -$purple_color: var(--du-badge-purple, #722FE4); -$light_blue_color: var(--du-badge-light-blue, #0DC2E7); +$blue_color: var(--du-badge-blue, #115bff); +$purple_color: var(--du-badge-purple, #722fe4); +$light_blue_color: var(--du-badge-light-blue, #0dc2e7); $gold_color: $gradient-color-gold; $gold_color_border: $color_yellow; $green_color: $gradient-color-green-4; @@ -168,7 +168,7 @@ $green_color_border: $color-green-6; } &--bordered { - border-style: solid; + border: solid 1px; background: none; &.deriv-badge__color--general { @@ -242,7 +242,6 @@ $green_color_border: $color-green-6; color: $font-color-gray; } } - } &--bold-text { @@ -265,10 +264,8 @@ $green_color_border: $color-green-6; border-radius: 24px; } } - } - @include desktop { .deriv-badge { display: inline-flex; @@ -424,7 +421,7 @@ $green_color_border: $color-green-6; } &--bordered { - border-style: solid; + border: solid 1px; background: none; &.deriv-badge__color--general { @@ -498,7 +495,6 @@ $green_color_border: $color-green-6; color: $font-color-gray; } } - } &--bold-text { @@ -521,6 +517,5 @@ $green_color_border: $color-green-6; border-radius: 24px; } } - } } diff --git a/src/components/SectionMessage/SectionMessage.scss b/src/components/SectionMessage/SectionMessage.scss new file mode 100644 index 00000000..608e57f8 --- /dev/null +++ b/src/components/SectionMessage/SectionMessage.scss @@ -0,0 +1,72 @@ +$error-bgColor: $color-status-error-1; +$error-icColor: $color-red-11; +$warning-bgColor: $color-status-warning-1; +$warning-icColor: $color-yellow-4; +$success-bgColor: $color-status-success; +$success-icColor: $color-green-9; +$info-bgColor: $color-status-information-1; +$info-icColor: $color-blue-11; +$description-color: $alpha-color-black-8; +$general-color: $color-gray-2; + +.deriv-section-message { + width: 100%; + display: inline-flex; + align-items: center; + column-gap: 8px; + border-radius: 16px; + padding: 16px; + + &__title { + margin-bottom: 8px; + } + + &__description { + color: $description-color; + } + + &__cta-section { + margin-top: 16px; + } + + &__icon { + display: flex; + align-self: flex-start; + + &--info { + fill: $info-icColor; + } + + &--success { + fill: $success-icColor; + } + + &--warning { + fill: $warning-icColor; + } + + &--error { + fill: $error-icColor; + } + } + + &--info { + background-color: $info-bgColor; + } + + &--success { + background-color: $success-bgColor; + } + + &--warning { + background-color: $warning-bgColor; + } + + &--error { + background-color: $error-bgColor; + } + + &--general { + background-color: $general-color; + } +} diff --git a/src/components/SectionMessage/__test__/SectionMessage.spec.tsx b/src/components/SectionMessage/__test__/SectionMessage.spec.tsx new file mode 100644 index 00000000..e4ceb99b --- /dev/null +++ b/src/components/SectionMessage/__test__/SectionMessage.spec.tsx @@ -0,0 +1,59 @@ +import React from "react"; +import { render } from "@testing-library/react"; +import { SectionMessage } from ".."; + +describe("SectionMessage Component", () => { + it("renders with all props", () => { + const ctaSection = ; + const { getByText, getByRole } = render( + + Warning Message + , + ); + expect(getByText("Warning Message")).toBeInTheDocument(); + expect(getByText("Title")).toBeInTheDocument(); + expect(getByRole("button", { name: "Click me" })).toBeInTheDocument(); + }); + + it("renders with specified variant", () => { + const { container } = render( + Error Message, + ); + expect(container.firstChild).toHaveClass( + "deriv-section-message--error", + ); + }); + + it("renders with predefined error icon", () => { + const { container } = render( + Message with Icon, + ); + expect( + container.querySelector(".deriv-section-message__icon"), + ).toBeInTheDocument(); + expect( + container.querySelector(".deriv-section-message__icon--error"), + ).toBeInTheDocument(); + }); + + it("renders with custom icon", () => { + const Img = icon; + + const { container, getByAltText } = render( + Message with Icon, + ); + expect(getByAltText("icon")).toBeInTheDocument(); + expect( + container.querySelector(".deriv-section-message__icon"), + ).toBeInTheDocument(); + }); + + it("renders with custom class name", () => { + const { container } = render( + + Custom Message + , + ); + expect(container.firstChild).toHaveClass("custom-class"); + }); +}); diff --git a/src/components/SectionMessage/index.tsx b/src/components/SectionMessage/index.tsx new file mode 100644 index 00000000..9830acf4 --- /dev/null +++ b/src/components/SectionMessage/index.tsx @@ -0,0 +1,96 @@ +import React from "react"; +import { + LabelPairedCircleCheckMdBoldIcon, + LabelPairedCircleExclamationMdBoldIcon, + LabelPairedCircleInfoMdBoldIcon, + LabelPairedTriangleExclamationMdBoldIcon, + IconTypes, +} from "@deriv/quill-icons"; +import clsx from "clsx"; +import { Text } from "../Text"; +import "./SectionMessage.scss"; + +type TVariant = "warning" | "info" | "success" | "error" | "general"; + +type SectionMessageProps = { + children: JSX.Element | string; + className?: string; + ctaSection?: JSX.Element; + icon?: JSX.Element; + title?: JSX.Element | string; + variant?: TVariant; +}; + +const VariantIcons: Record, IconTypes> = { + error: LabelPairedTriangleExclamationMdBoldIcon, + info: LabelPairedCircleInfoMdBoldIcon, + success: LabelPairedCircleCheckMdBoldIcon, + warning: LabelPairedCircleExclamationMdBoldIcon, +}; + +/** + * `SectionMessage` is a React component designed to display a styled section message. + * It includes an optional icon, title, description, and call-to-action (CTA) section. + * + * @component + * @param {Object} props - Component props. + * @param {JSX.Element|string} props.children - The main content or message of the section. + * @param {string} [props.className] - Optional additional class names for styling. + * @param {JSX.Element} [props.ctaSection] - Optional CTA section element, such as a button / button group or link. + * @param {JSX.Element} [props.icon] - Optional icon element. + * @param {JSX.Element|string} [props.title] - Optional title element or string to summarize the message. + * @param {TVariant} [props.variant="general"] - Optional variant to style the section with different themes: "warning", "info", "success", "error", or "general". + */ +export const SectionMessage = ({ + children, + className, + ctaSection, + icon, + title, + variant = "general", +}: SectionMessageProps) => { + const IconComponent = variant !== "general" && VariantIcons[variant]; + + return ( +
+ {(icon || IconComponent) && ( +
+ {IconComponent ? ( + + ) : ( + icon + )} +
+ )} +
+ {title && ( + + {title} + + )} +
+ {children} +
+ {ctaSection && ( +
+ {ctaSection} +
+ )} +
+
+ ); +}; diff --git a/src/main.ts b/src/main.ts index 81ab70de..e64bc16d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -14,6 +14,7 @@ export { Loader } from "./components/Loader"; export { Modal } from "./components/Modal"; export { PageLayout } from "./components/PageLayout"; export { PasswordInput } from "./components/PasswordInput"; +export { SectionMessage } from "./components/SectionMessage"; export { SideNote } from "./components/SideNote"; export { Tab, Tabs } from "./components/Tabs"; export { Table } from "./components/Table"; diff --git a/src/styles/abstracts/_variables.scss b/src/styles/abstracts/_variables.scss index 91377635..44cf7396 100644 --- a/src/styles/abstracts/_variables.scss +++ b/src/styles/abstracts/_variables.scss @@ -68,8 +68,9 @@ $color-blue-5: #dfeaff; $color-blue-6: #92b8ff; $color-blue-7: #182130; $color-blue-8: #e6f5ff; -$color-blue-9:#00C6EF; -$color-blue-10:#115BFF; +$color-blue-9: #00c6ef; +$color-blue-10: #115bff; +$color-blue-11: #0777c4; $color-brown: #664407; $color-green: #85acb0; $color-green-1: #4bb4b3; @@ -80,6 +81,7 @@ $color-green-5: #4bb4b329; $color-green-6: #17eabd; $color-green-7: #e8fdf8; $color-green-8: #cedddf; +$color-green-9: #007a22; $color-gray: #c2c2c2; $color-gray-1: #999999; $color-gray-2: #f2f3f4; @@ -95,9 +97,9 @@ $color-gray-11: #fafafa; $color-gray-12: #f5f7fa; $color-gray-13: #2e2e2e; $color-orange: #ff6444; -$color-orange-1:#ff9c13; +$color-orange-1: #ff9c13; $color-purple: #722fe4; -$color-purple-1:#8F4BFF; +$color-purple-1: #8f4bff; $color-red: #ff444f; $color-red-1: #ec3f3f; $color-red-2: #cc2e3d; @@ -110,18 +112,24 @@ $color-red-8: #661b20; $color-red-9: #b33037; $color-red-10: #ff444f; $color-red-11: #fce3e3; +$color-red-11: #c40000; $color-violet: #4a3871; $color-white: #ffffff; $color-yellow: #ffad3a; $color-yellow-1: #b3760d; $color-yellow-2: #ffa912; $color-yellow-3: rgba(255, 173, 58, 0.16); +$color-yellow-4: #c47d00; /* STATUS COLORS */ $color-status-warning: rgba(255, 173, 58, 0.16); +$color-status-warning-1: rgba(255, 156, 19, 0.08); $color-status-information: rgba(55, 124, 252, 0.16); +$color-status-information-1: rgba(44, 154, 255, 0.08); $color-status-announcement: rgba(75, 180, 179, 0.16); +$color-status-success: rgba(0, 136, 50, 0.08); $color-status-error: rgba(236, 63, 63, 0.16); +$color-status-error-1: rgba(230, 25, 14, 0.08); /* ALPHA COLORS */ $alpha-color-black-1: transparentize($color-black-7, 0.28); @@ -131,6 +139,7 @@ $alpha-color-black-4: transparentize($color-black-7, 0.84); $alpha-color-black-5: transparentize($color-black-7, 0.16); $alpha-color-black-6: transparentize($color-black-7, 0.36); $alpha-color-black-7: transparentize($color-black, 0.5); +$alpha-color-black-8: transparentize($color-black-7, 0.72); $alpha-color-blue-1: transparentize($color-blue, 0.84); $alpha-color-blue-2: transparentize($color-blue-3, 0.84); $alpha-color-blue-3: transparentize($color-blue, 0.92); diff --git a/stories/SectionMessage.stories.tsx b/stories/SectionMessage.stories.tsx new file mode 100644 index 00000000..0d9b709a --- /dev/null +++ b/stories/SectionMessage.stories.tsx @@ -0,0 +1,90 @@ +import { StoryObj, Meta } from "@storybook/react"; +import { SectionMessage } from "../src/main"; +import { Button } from "../src/components/Button"; + +const meta = { + title: "Components/SectionMessage", + component: SectionMessage, + parameters: { layout: "centered" }, + tags: ["autodocs"], + argTypes: { + children: { + control: { + type: "text", + }, + }, + className: { + disable: true, + }, + ctaSection: { + control: false, + description: + "A prop which can be passed with a single button or div containing a group of buttons for CTA.", + }, + title: { + control: { + type: "text", + }, + }, + variant: { + control: { + type: "select", + }, + defaultValue: "general", + options: ["warning", "info", "success", "error", "general"], + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + name: "Default Section Message with CTA", + args: { + children: "This is a default section message", + title: "Section message", + variant: "general", + ctaSection: ( + + ), + }, +}; + +export const Warning: Story = { + name: "Warning Section Message", + args: { + children: "This is a default inline message", + title: "Section message", + variant: "warning", + }, +}; + +export const Error: Story = { + name: "Error Section Message", + args: { + children: "This is a default inline message", + title: "Section message", + variant: "error", + }, +}; + +export const Info: Story = { + name: "Info Section Message", + args: { + children: "This is a default inline message", + title: "Section message", + variant: "info", + }, +}; + +export const Success: Story = { + name: "Success Section Message", + args: { + children: "This is a default inline message", + title: "Section message", + variant: "success", + }, +};