diff --git a/apps/website/pages/components/toast/code.tsx b/apps/website/pages/components/toast/code.tsx new file mode 100644 index 0000000000..3041466e25 --- /dev/null +++ b/apps/website/pages/components/toast/code.tsx @@ -0,0 +1,17 @@ +import Head from "next/head"; +import type { ReactElement } from "react"; +import ToastPageLayout from "screens/components/toast/ToastPageLayout"; +import ToastCodePage from "screens/components/toast/code/ToastCodePage"; + +const Code = () => ( + <> + + Toast code — Halstack Design System + + + +); + +Code.getLayout = (page: ReactElement) => {page}; + +export default Code; diff --git a/apps/website/pages/components/toast/index.tsx b/apps/website/pages/components/toast/index.tsx index 34cdb10fe3..b4e1acb5fd 100644 --- a/apps/website/pages/components/toast/index.tsx +++ b/apps/website/pages/components/toast/index.tsx @@ -1,21 +1,17 @@ import Head from "next/head"; import type { ReactElement } from "react"; -import ToastCodePage from "screens/components/toast/code/ToastCodePage"; import ToastPageLayout from "screens/components/toast/ToastPageLayout"; +import ToastOverviewPage from "screens/components/toast/overview/ToastOverviewPage"; -const Index = () => { - return ( - <> - - Toast — Halstack Design System - - - - ); -}; +const Index = () => ( + <> + + Toast — Halstack Design System + + + +); -Index.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; +Index.getLayout = (page: ReactElement) => {page}; export default Index; diff --git a/apps/website/pages/components/toast/specifications.tsx b/apps/website/pages/components/toast/specifications.tsx deleted file mode 100644 index 758c8863fa..0000000000 --- a/apps/website/pages/components/toast/specifications.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import ToastSpecsPage from "screens/components/toast/specs/ToastSpecsPage"; -import ToastPageLayout from "screens/components/toast/ToastPageLayout"; - -const Specifications = () => { - return ( - <> - - Toast Specs — Halstack Design System - - - - ); -}; - -Specifications.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Specifications; diff --git a/apps/website/pages/components/toast/usage.tsx b/apps/website/pages/components/toast/usage.tsx deleted file mode 100644 index 30c1252cdb..0000000000 --- a/apps/website/pages/components/toast/usage.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import ToastPageLayout from "screens/components/toast/ToastPageLayout"; -import ToastUsagePage from "screens/components/toast/usage/ToastUsagePage"; - -const Usage = () => { - return ( - <> - - Toast Usage — Halstack Design System - - - - ); -}; - -Usage.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Usage; diff --git a/apps/website/screens/components/spinner/code/SpinnerCodePage.tsx b/apps/website/screens/components/spinner/code/SpinnerCodePage.tsx index 9d65c905fb..01a5bdc2d7 100644 --- a/apps/website/screens/components/spinner/code/SpinnerCodePage.tsx +++ b/apps/website/screens/components/spinner/code/SpinnerCodePage.tsx @@ -35,6 +35,19 @@ const sections = [ 'Spinner' + + inheritColor + + boolean + + + If true, the color is inherited from the closest parent with a defined color. This allows users to adapt + the spinner to the semantic color of the use case in which it is used. + + + false + + label diff --git a/apps/website/screens/components/toast/ToastPageLayout.tsx b/apps/website/screens/components/toast/ToastPageLayout.tsx index f636cf1b6e..f1598a4291 100644 --- a/apps/website/screens/components/toast/ToastPageLayout.tsx +++ b/apps/website/screens/components/toast/ToastPageLayout.tsx @@ -6,9 +6,8 @@ import { ReactNode } from "react"; const ToastPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ - { label: "Code", path: "/components/toast" }, - { label: "Usage", path: "/components/toast/usage" }, - { label: "Specifications", path: "/components/toast/specifications" }, + { label: "Overview", path: "/components/toast" }, + { label: "Code", path: "/components/toast/code" }, ]; return ( @@ -21,7 +20,7 @@ const ToastPageHeading = ({ children }: { children: ReactNode }) => { updates to the user. It is commonly used to communicate non-critical information, such as success messages, warning alerts, or brief updates. - + {children} diff --git a/apps/website/screens/components/toast/code/ToastCodePage.tsx b/apps/website/screens/components/toast/code/ToastCodePage.tsx index 3ac8256f14..b6333cd129 100644 --- a/apps/website/screens/components/toast/code/ToastCodePage.tsx +++ b/apps/website/screens/components/toast/code/ToastCodePage.tsx @@ -36,6 +36,21 @@ const sections = [ + + + + + children + + + + ReactNode + + + Tree of components from which the useToast hook can be triggered. + + - + duration @@ -49,14 +64,6 @@ const sections = [ 3000 - - children - - ReactNode - - Tree of components from which the useToast hook can be triggered. - - - ), @@ -296,15 +303,13 @@ const sections = [ }, ]; -const ToastCodePage = () => { - return ( - - - - - - - ); -}; +const ToastCodePage = () => ( + + + + + + +); export default ToastCodePage; diff --git a/apps/website/screens/components/toast/overview/ToastOverviewPage.tsx b/apps/website/screens/components/toast/overview/ToastOverviewPage.tsx new file mode 100644 index 0000000000..acb8c27824 --- /dev/null +++ b/apps/website/screens/components/toast/overview/ToastOverviewPage.tsx @@ -0,0 +1,250 @@ +import { DxcBulletedList, DxcFlex, DxcParagraph, DxcTable, DxcTypography } from "@dxc-technology/halstack-react"; +import QuickNavContainer from "@/common/QuickNavContainer"; +import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; +import DocFooter from "@/common/DocFooter"; +import HeaderDescriptionCell from "@/common/HeaderDescriptionCell"; +import Figure from "@/common/Figure"; +import Image from "@/common/Image"; +import semanticToasts from "./images/semantic_toasts.png"; +import loadingToast from "./images/loading_toast.png"; +import toastsPositioning from "./images/toasts_stacking.png"; +import anatomy from "./images/toast_anatomy.png"; + +const sections = [ + { + title: "Introduction", + content: ( + + The toast component provides brief, non-intrusive notifications to users, appearing{" "} + temporarily on the screen without disrupting their workflow. Typically used for system + messages, confirmations, or alerts, toasts help communicate important feedback in response to user actions. They + automatically disappear after a set duration but can also include manual dismissal options. By maintaining + visibility without demanding immediate interaction, toasts enhance user experience while keeping the interface + clean and efficient. + + ), + }, + { + title: "Anatomy", + content: ( + <> + Toast's anatomy + + + Container: the structural wrapper that holds all elements of the toast, defining its size, + background, and layout while ensuring proper visibility within the interface. + + + Icon: an optional icon indicating the type of message (e.g., success, error, warning, + info). + + + Text message: a short text message providing the main content of the toast. + + + Action: an optional button with quick and straightforward action like "Undo" or "View" + depending on the context. + + + Close action: to allow manual dismissal of the toast. + + + + ), + }, + { + title: "Using toasts", + subSections: [ + { + title: "Loading status toast", + content: ( + <> + + A loading toast provides users with real-time feedback during an ongoing process. Instead + of a static icon, a spinner is displayed to visually indicate that the process is still in progress. This + toast remains visible until the process is complete, ensuring users are aware that the system is working. + Once the task is finished, the loading toast will automatically disappear, and a follow-up toast will + appear in the queue to confirm the outcome of the process. + +
+ Common loading process represented with toasts +
+ + ), + }, + + { + title: "Position and order on screen", + content: ( + <> + + Toasts should be positioned in a way that ensures they are easily noticeable without obstructing the main + content or interrupting the user's workflow. + + + + Bottom-Right: Toasts are aligned to the bottom-right corner of the screen. + + + Bottom-Center: On small devices, toasts are positioned in the bottom-center of the + screen. + + + + This position allows users to receive notifications without interfering with primary tasks or content. + + Positioning of some example toasts on the screen + + Toasts should appear and disappear in a specific order to ensure clarity and consistency in user + notifications: + + + + Order of appearance: Toasts appear in the order they are triggered. This means the + newest toast will appear at the bottom of the stack. This ensures users see the most recent notification + last, making it easier to track the sequence of events. + + + Order of disappearance: Toasts disappear in the same order they appeared. This means + the oldest toast will disappear first, maintaining a First In, First Out (FIFO) system. This order helps + maintain a logical flow and ensures users have enough time to read each notification. + + + + ), + }, + { + title: "Managing multiple toasts", + content: ( + <> + + When multiple toasts appear on the screen simultaneously, it's important to manage their display to ensure + they don't overlap and that each one remains visible and readable. + + Key practices to ensure they remain effective and user-friendly: + + + Stacking: Toasts are displayed in a vertical stack. New toasts are added to the stack + in a consistent location (at the bottom). + + + Offset spacing: Small gap between toasts to visually separate them (8px) + + + Limit: Only 5 toast max. should be displayed at the same time. + + + Sequential display: Display toasts one after another rather than all at once. + + + Timing: Set a uniform duration for each toast to stay visible (3-5 seconds). + + + + ), + }, + ], + }, + { + title: "Semantic toasts", + content: ( + <> + Toasts can be categorized based on their purpose: + + + + Semantic + Description + + + + + + + Default + + + Used for neutral messages or general notifications. (i.e.. Settings have been updated.) + + + + + Info + + + + Displays general information or updates. (i.e. New message received. Check inbox. - New update + available. Download now.) + + + + + + Success + + + + Indicates successful completion of an action. (i.e. Operation successful. Changes saved. - Profile + updated successfully.) + + + + + + Warning + + + + Provides cautionary advice without blocking actions. (i.e. Unstable connection. Proceed with caution.) + + + + + Semantic toasts based on their purpose + + ), + }, + { + title: "Best practices", + content: ( + + + Keep messages concise and clear: ensure toasts are easy to read at a glance by using short, + direct language that conveys the message effectively. + + + Avoid obstructing main content: position toasts in the bottom-right corner of the screen to + keep them visible without interfering with the user's workflow. + + + Prevent notification overload: display no more than five toasts at a time to maintain clarity + and avoid overwhelming users with excessive messages. If additional notifications are needed, consider + queueing them. + + + Ensure consistency: use a uniform visual style, placement, and behavior for all toasts across + the application to create a seamless user experience. + + + Prioritize urgency and relevance: reserve toasts for important, time-sensitive information + and avoid using them for non-critical updates that could be delivered through other means. + + + Allow for manual dismissal when necessary: while toasts should disappear automatically after + a set duration, providing a close button can improve accessibility and user control. + + + ), + }, +]; + +const TextareaOverviewPage = () => ( + + + + + + +); + +export default TextareaOverviewPage; diff --git a/apps/website/screens/components/toast/overview/images/loading_toast.png b/apps/website/screens/components/toast/overview/images/loading_toast.png new file mode 100644 index 0000000000..05d3990ae7 Binary files /dev/null and b/apps/website/screens/components/toast/overview/images/loading_toast.png differ diff --git a/apps/website/screens/components/toast/overview/images/semantic_toasts.png b/apps/website/screens/components/toast/overview/images/semantic_toasts.png new file mode 100644 index 0000000000..1321c30856 Binary files /dev/null and b/apps/website/screens/components/toast/overview/images/semantic_toasts.png differ diff --git a/apps/website/screens/components/toast/overview/images/toast_anatomy.png b/apps/website/screens/components/toast/overview/images/toast_anatomy.png new file mode 100644 index 0000000000..2c4b85ce26 Binary files /dev/null and b/apps/website/screens/components/toast/overview/images/toast_anatomy.png differ diff --git a/apps/website/screens/components/toast/overview/images/toasts_stacking.png b/apps/website/screens/components/toast/overview/images/toasts_stacking.png new file mode 100644 index 0000000000..ac9f06a413 Binary files /dev/null and b/apps/website/screens/components/toast/overview/images/toasts_stacking.png differ diff --git a/apps/website/screens/components/toast/specs/ToastSpecsPage.tsx b/apps/website/screens/components/toast/specs/ToastSpecsPage.tsx deleted file mode 100644 index e34c41e41c..0000000000 --- a/apps/website/screens/components/toast/specs/ToastSpecsPage.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { DxcBulletedList, DxcFlex, DxcParagraph } from "@dxc-technology/halstack-react"; -import QuickNavContainer from "@/common/QuickNavContainer"; -import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; -import DocFooter from "@/common/DocFooter"; -import Figure from "@/common/Figure"; -import Image from "@/common/Image"; -import specs from "./images/toast_specs.png"; -import anatomy from "./images/toast_anatomy.png"; - -const sections = [ - { - title: "Specifications", - content: ( -
- Toast design specifications -
- ), - }, - { - title: "Anatomy", - content: ( - <> - Toast anatomy - - Container - Icon - Text message - Action - Close action - - - ), - }, - { - title: "Design tokens", - content: This component currently has no design tokens., - }, -]; - -const TextareaSpecsPage = () => { - return ( - - - - - - - ); -}; - -export default TextareaSpecsPage; diff --git a/apps/website/screens/components/toast/specs/images/toast_anatomy.png b/apps/website/screens/components/toast/specs/images/toast_anatomy.png deleted file mode 100644 index b22b195bc6..0000000000 Binary files a/apps/website/screens/components/toast/specs/images/toast_anatomy.png and /dev/null differ diff --git a/apps/website/screens/components/toast/specs/images/toast_specs.png b/apps/website/screens/components/toast/specs/images/toast_specs.png deleted file mode 100644 index 7666001d8d..0000000000 Binary files a/apps/website/screens/components/toast/specs/images/toast_specs.png and /dev/null differ diff --git a/apps/website/screens/components/toast/usage/ToastUsagePage.tsx b/apps/website/screens/components/toast/usage/ToastUsagePage.tsx deleted file mode 100644 index b6de1ae8d5..0000000000 --- a/apps/website/screens/components/toast/usage/ToastUsagePage.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import { DxcBulletedList, DxcFlex, DxcParagraph, DxcTable, DxcTypography } from "@dxc-technology/halstack-react"; -import QuickNavContainer from "@/common/QuickNavContainer"; -import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; -import DocFooter from "@/common/DocFooter"; -import HeaderDescriptionCell from "@/common/HeaderDescriptionCell"; -import Figure from "@/common/Figure"; -import Image from "@/common/Image"; -import semanticToasts from "./images/semantic_toasts.png"; -import loadingToast from "./images/loading_toast.png"; -import toastsPositioning from "./images/toasts_positioning.png"; - -const sections = [ - { - title: "Usage", - content: ( - - Keep messages concise and clear to ensure quick readability. - - Position toasts in the bottom-right to avoid obstructing main content. - - - Display no more than 5 toasts simultaneously to avoid overwhelming users. - - - Maintain a consistent visual style and placement for all toasts across the application. - - - ), - }, - { - title: "Semantic toasts", - content: ( - <> - Toasts can be categorized based on their purpose: - - - - Semantic - Description - - - - - - - Default - - - Used for neutral messages or general notifications. (ie. Settings have been updated.) - - - - - Info - - - - Displays general information or updates. (ie. New message received. Check inbox. - New update available. - Download now.) - - - - - - Warning - - - - Indicates successful completion of an action. (ie. Operation successful. Changes saved. - Profile - updated successfully.) - - - - - - Success - - - - Provides cautionary advice without blocking actions. (ie. Unstable connection. Proceed with caution.) - - - - - Semantic toasts based on their purpose - - ), - }, - { - title: "Loading status toast", - content: ( - <> - - A loading toast provides users with real-time feedback during an ongoing process. Instead of a static icon, a - spinner is displayed to visually indicate that the process is still in progress. This toast remains visible - until the process is complete, ensuring users are aware that the system is working. Once the task is finished, - the loading toast will automatically disappear, and a follow-up toast will appear in the queue to confirm the - outcome of the process. - -
- Common loading process represented with toasts -
- - ), - }, - { - title: "Position and order on screen", - content: ( - <> - - Toasts should be positioned in a way that ensures they are easily noticeable without obstructing the main - content or interrupting the user's workflow. - - - - Bottom-Right: Toasts are aligned to the bottom-right corner of the screen. - - - Bottom-Center: On small devices, toasts are positioned in the bottom-center of the screen. - - - - This positions allows users to receive notifications without them interfering with primary tasks or content. - - Positioning of some example toasts on the screen - - Toasts should appear and disappear in a specific order to ensure clarity and consistency in user - notifications: - - - - Order of appearance: Toasts appear in the order they are triggered. This means the newest - toast will appear at the bottom of the stack. This ensures users see the most recent notification last, - making it easier to track the sequence of events. - - - Order of disappearance: Toasts disappear in the same order they appeared. This means the - oldest toast will disappear first, maintaining a First In, First Out (FIFO) system. This order helps - maintain a logical flow and ensures users have enough time to read each notification. - - - - ), - }, - { - title: "Managing multiple toasts", - content: ( - <> - - When multiple toasts appear on the screen simultaneously, it's important to manage their display to ensure - they don't overlap and that each one remains visible and readable. - - Key practices to ensure they remain effective and user-friendly: - - - Stacking: Toasts are displayed in a vertical stack. New toasts are added to the stack in a - consistent location (at the bottom). - - - Offset spacing: Small gap between toasts to visually separate them (8px) - - - Limit: Only 5 toast max. should be displayed at the same time. - - - Sequential display: Display toasts one after another rather than all at once. - - - Timing: Set a uniform duration for each toast to stay visible (3-5 seconds). - - - - ), - }, -]; - -const TextareaUsagePage = () => { - return ( - - - - - - - ); -}; - -export default TextareaUsagePage; diff --git a/apps/website/screens/components/toast/usage/images/loading_toast.png b/apps/website/screens/components/toast/usage/images/loading_toast.png deleted file mode 100644 index 022b16f8ac..0000000000 Binary files a/apps/website/screens/components/toast/usage/images/loading_toast.png and /dev/null differ diff --git a/apps/website/screens/components/toast/usage/images/semantic_toasts.png b/apps/website/screens/components/toast/usage/images/semantic_toasts.png deleted file mode 100644 index 71053efd4c..0000000000 Binary files a/apps/website/screens/components/toast/usage/images/semantic_toasts.png and /dev/null differ diff --git a/apps/website/screens/components/toast/usage/images/toasts_positioning.png b/apps/website/screens/components/toast/usage/images/toasts_positioning.png deleted file mode 100644 index 3d34099ff3..0000000000 Binary files a/apps/website/screens/components/toast/usage/images/toasts_positioning.png and /dev/null differ diff --git a/packages/lib/src/spinner/Spinner.tsx b/packages/lib/src/spinner/Spinner.tsx index d2bfb1fb63..b6b04891e4 100644 --- a/packages/lib/src/spinner/Spinner.tsx +++ b/packages/lib/src/spinner/Spinner.tsx @@ -139,6 +139,7 @@ const determinateValue = (value: SpinnerPropsType["value"], strokeDashArray: num const CircleSpinner = styled.circle<{ determined: boolean; + inheritColor: SpinnerPropsType["inheritColor"]; value: SpinnerPropsType["value"]; }>` fill: transparent; @@ -150,7 +151,12 @@ const CircleSpinner = styled.circle<{ : props.mode !== "small" ? "1.4s ease-in-out infinite both svg-circle-large" : "1.4s ease-in-out infinite both svg-circle-small"}; - stroke: ${({ mode }) => (mode === "overlay" ? "var(--color-fg-primary-medium)" : "var(--color-fg-primary-strong)")}; + stroke: ${({ inheritColor, mode }) => + inheritColor + ? "currentColor" + : mode === "overlay" + ? "var(--color-fg-primary-medium)" + : "var(--color-fg-primary-strong)"}; transform-origin: ${({ determined }) => (!determined ? "50% 50%" : "")}; stroke-dasharray: ${({ mode }) => (mode !== "small" ? "409" : "38")}; stroke-width: ${({ mode }) => (mode !== "small" ? "8.5px" : "2px")}; @@ -180,7 +186,15 @@ const Labels = styled.div<{ mode: SpinnerPropsType["mode"] }>` } `; -const DxcSpinner = ({ ariaLabel = "Spinner", label, margin, mode = "large", showValue, value }: SpinnerPropsType) => { +const DxcSpinner = ({ + ariaLabel = "Spinner", + inheritColor = false, + label, + margin, + mode = "large", + showValue, + value, +}: SpinnerPropsType) => { const labelId = useId(); const determined = useMemo(() => value != null && value >= 0 && value <= 100, [value]); const [hasTooltip, setHasTooltip] = useState(false); @@ -215,6 +229,7 @@ const DxcSpinner = ({ ariaLabel = "Spinner", label, margin, mode = "large", show cx={mode === "small" ? "8" : "70"} cy={mode === "small" ? "8" : "70"} determined={determined} + inheritColor={inheritColor} mode={mode} r={mode === "small" ? "6" : "65"} value={value} diff --git a/packages/lib/src/spinner/types.ts b/packages/lib/src/spinner/types.ts index 40ccf47c11..d10d8f7c3c 100644 --- a/packages/lib/src/spinner/types.ts +++ b/packages/lib/src/spinner/types.ts @@ -2,31 +2,36 @@ import { Margin, Space } from "../common/utils"; type Props = { /** - * Text to be placed inside the spinner. + * Specifies a string to be used as the name for the spinner element when no `label` is provided or the `mode` is set to small. */ - label?: string; + ariaLabel?: string; /** - * The value of the progress indicator. If it's received the - * component is determinate, otherwise is indeterminate. + * If true, the color is inherited from the closest parent with a defined color. This allows users to adapt the spinner + * to the semantic color of the use case in which it is used. */ - value?: number; + inheritColor?: boolean; /** - * If true, the value is displayed inside the spinner.. + * Text to be placed inside the spinner. */ - showValue?: boolean; + label?: string; + /** + * Size of the margin to be applied to the component ('xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge'). + * You can pass an object with 'top', 'bottom', 'left' and 'right' properties in order to specify different margin sizes. + */ + margin?: Space | Margin; /** * Available modes of the spinner. */ mode?: "large" | "small" | "overlay"; /** - * Size of the margin to be applied to the component ('xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge'). - * You can pass an object with 'top', 'bottom', 'left' and 'right' properties in order to specify different margin sizes. + * If true, the value is displayed inside the spinner.. */ - margin?: Space | Margin; + showValue?: boolean; /** - * Specifies a string to be used as the name for the spinner element when no `label` is provided or the `mode` is set to small. + * The value of the progress indicator. If it's received the + * component is determinate, otherwise is indeterminate. */ - ariaLabel?: string; + value?: number; }; export default Props; diff --git a/packages/lib/src/toast/Toast.accessibility.test.tsx b/packages/lib/src/toast/Toast.accessibility.test.tsx index 6633a8dfa3..d89d603fee 100644 --- a/packages/lib/src/toast/Toast.accessibility.test.tsx +++ b/packages/lib/src/toast/Toast.accessibility.test.tsx @@ -25,6 +25,7 @@ const ToastPage = () => { /> ); }; + const TestExample = () => ( diff --git a/packages/lib/src/toast/Toast.tsx b/packages/lib/src/toast/Toast.tsx index f1b27c24cd..9d26f2cecc 100644 --- a/packages/lib/src/toast/Toast.tsx +++ b/packages/lib/src/toast/Toast.tsx @@ -1,16 +1,14 @@ import { memo, useContext, useState } from "react"; import styled, { keyframes } from "styled-components"; -import CoreTokens from "../common/coreTokens"; import DxcActionIcon from "../action-icon/ActionIcon"; import DxcButton from "../button/Button"; import DxcFlex from "../flex/Flex"; -import DxcIcon from "../icon/Icon"; -import DxcSpinner from "../spinner/Spinner"; -import { HalstackProvider } from "../HalstackContext"; import ToastPropsType from "./types"; import useTimeout from "../utils/useTimeout"; import { HalstackLanguageContext } from "../HalstackContext"; import { responsiveSizes } from "../common/variables"; +import getSemantic from "./utils"; +import ToastIcon from "./ToastIcon"; const fadeInUp = keyframes` 0% { @@ -34,43 +32,19 @@ const fadeOutDown = keyframes` } `; -const getSemantic = (semantic: ToastPropsType["semantic"]) => { - switch (semantic) { - case "info": - return { - primaryColor: CoreTokens.color_blue_700, - secondaryColor: CoreTokens.color_blue_100, - icon: "filled_info", - }; - case "success": - return { - primaryColor: CoreTokens.color_green_700, - secondaryColor: CoreTokens.color_green_100, - icon: "filled_check_circle", - }; - case "warning": - return { - primaryColor: CoreTokens.color_orange_700, - secondaryColor: CoreTokens.color_orange_100, - icon: "filled_warning", - }; - default: - return { primaryColor: CoreTokens.color_purple_700, secondaryColor: CoreTokens.color_purple_100, icon: "" }; - } -}; - const Toast = styled.output<{ semantic: ToastPropsType["semantic"]; isClosing: boolean }>` box-sizing: border-box; min-width: 200px; max-width: 600px; width: fit-content; - border-radius: ${CoreTokens.border_radius_medium}; - border-left: ${CoreTokens.border_width_2} solid ${({ semantic }) => getSemantic(semantic).primaryColor}; - box-shadow: 0px 2px 2px 0px rgba(181, 181, 181, 0.4); + border-left: var(--border-width-m) var(--border-style-default) ${({ semantic }) => getSemantic(semantic).primaryColor}; + border-radius: var(--border-radius-s); + box-shadow: var(--shadow-low-x-position) var(--shadow-low-y-position) var(--shadow-low-blur) var(--shadow-low-spread) + var(--shadow-dark); display: inline-flex; + gap: var(--spacing-gap-l); justify-content: space-between; - gap: ${CoreTokens.spacing_24}; - padding: ${CoreTokens.spacing_8} ${CoreTokens.spacing_12}; + padding: var(--spacing-padding-xs) var(--spacing-padding-s); background-color: ${({ semantic }) => getSemantic(semantic).secondaryColor}; animation: ${({ isClosing }) => (isClosing ? fadeOutDown : fadeInUp)} 0.3s ease forwards; @@ -82,51 +56,26 @@ const Toast = styled.output<{ semantic: ToastPropsType["semantic"]; isClosing: b const ContentContainer = styled.div<{ loading: ToastPropsType["loading"]; semantic: ToastPropsType["semantic"] }>` display: flex; align-items: center; - gap: ${CoreTokens.spacing_8}; - overflow: hidden; + gap: var(--spacing-gap-s); color: ${({ semantic }) => getSemantic(semantic).primaryColor}; - - ${({ loading }) => !loading && `font-size: ${CoreTokens.type_scale_05}`}; + overflow: hidden; + ${({ loading }) => !loading && `font-size: var(--height-s);`} > svg { + height: var(--height-s); width: 24px; - height: 24px; } `; const Message = styled.span` - color: ${CoreTokens.color_grey_900}; - font-family: ${CoreTokens.type_sans}; - font-size: ${CoreTokens.type_scale_02}; - font-weight: ${CoreTokens.type_semibold}; + color: var(--color-fg-neutral-dark); + font-family: var(--typography-font-family); + font-size: var(--typography-label-m); + font-weight: var(--typography-label-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; `; -const spinnerTheme = { - spinner: { - accentColor: getSemantic("info").primaryColor, - }, -}; - -const ToastIcon = memo( - ({ - icon, - hideSemanticIcon, - loading, - semantic, - }: Pick) => { - if (semantic === "default") return typeof icon === "string" ? : icon; - else if (semantic === "info" && loading) - return ( - - - - ); - else return !hideSemanticIcon && ; - } -); - const DxcToast = ({ action, duration, @@ -155,12 +104,12 @@ const DxcToast = ({ ); return ( - + {message} - + {action && ( ) => { + if (semantic === "default") return typeof icon === "string" ? : icon; + else if (semantic === "info" && loading) return ; + else return !hideSemanticIcon && ; + } +); + +export default ToastIcon; diff --git a/packages/lib/src/toast/ToastsQueue.tsx b/packages/lib/src/toast/ToastsQueue.tsx index 5b12882bd0..a4ee128520 100644 --- a/packages/lib/src/toast/ToastsQueue.tsx +++ b/packages/lib/src/toast/ToastsQueue.tsx @@ -6,6 +6,7 @@ import DxcToast from "./Toast"; import { QueuedToast, Semantic, ToastsQueuePropsType, ToastType } from "./types"; import { responsiveSizes } from "../common/variables"; import ToastContext from "./ToastContext"; +import { generateUniqueToastId } from "./utils"; const ToastsQueue = styled.section` box-sizing: border-box; @@ -25,17 +26,7 @@ const ToastsQueue = styled.section` } `; -const generateUniqueToastId = (toasts: QueuedToast[]) => { - let id = ""; - let exists = true; - while (exists) { - id = `${performance.now()}-${Math.random().toString(36).slice(2, 9)}`; - exists = toasts.some((toast) => toast.id === id); - } - return id; -}; - -const DxcToastsQueue = ({ children, duration = 3000 }: ToastsQueuePropsType) => { +export default function DxcToastsQueue({ children, duration = 3000 }: ToastsQueuePropsType) { const [toasts, setToasts] = useState([]); const [isMounted, setIsMounted] = useState(false); // Next.js SSR mounting issue const adjustedDuration = useMemo(() => (duration > 5000 ? 5000 : duration < 3000 ? 3000 : duration), [duration]); @@ -78,6 +69,4 @@ const DxcToastsQueue = ({ children, duration = 3000 }: ToastsQueuePropsType) => {children} ); -}; - -export default DxcToastsQueue; +} diff --git a/packages/lib/src/toast/types.ts b/packages/lib/src/toast/types.ts index 73f84bce0e..4fe4a92bbf 100644 --- a/packages/lib/src/toast/types.ts +++ b/packages/lib/src/toast/types.ts @@ -65,16 +65,16 @@ type ToastPropsType = { hideSemanticIcon?: boolean; }; -type ToastsQueuePropsType = { +type ToastsQueuePropsType = { + /** + * Tree of components from which the useToast hook can be triggered. + */ + children: ReactNode; /** * Duration in milliseconds before a toast automatically hides itself. * The range goes from 3000ms to 5000ms, any other value will not be taken into consideration. */ duration?: number; - /** - * Tree of components from which the useToast hook can be triggered. - */ - children: ReactNode; }; export default ToastPropsType; diff --git a/packages/lib/src/toast/useToast.tsx b/packages/lib/src/toast/useToast.tsx index 28f5819c2d..de5f5e1eac 100644 --- a/packages/lib/src/toast/useToast.tsx +++ b/packages/lib/src/toast/useToast.tsx @@ -2,7 +2,7 @@ import { useContext, useMemo } from "react"; import ToastContext from "./ToastContext"; import { DefaultToast, SemanticToast, LoadingToast } from "./types"; -const useToast = () => { +export default function useToast() { const add = useContext(ToastContext); const toast = useMemo( @@ -15,8 +15,6 @@ const useToast = () => { }), [add] ); - + return toast; -}; - -export default useToast; +} diff --git a/packages/lib/src/toast/utils.ts b/packages/lib/src/toast/utils.ts new file mode 100644 index 0000000000..6718ee6344 --- /dev/null +++ b/packages/lib/src/toast/utils.ts @@ -0,0 +1,40 @@ +import ToastPropsType, { QueuedToast } from "./types"; + +export default function getSemantic(semantic: ToastPropsType["semantic"]) { + switch (semantic) { + case "default": + return { + primaryColor: "var(--border-color-primary-stronger)", + secondaryColor: "var(--color-bg-primary-lighter)", + icon: "", + }; + case "info": + return { + primaryColor: "var(--border-color-secondary-strong)", + secondaryColor: "var(--color-bg-secondary-lighter)", + icon: "filled_info", + }; + case "success": + return { + primaryColor: "var(--border-color-success-medium)", + secondaryColor: "var(--color-bg-success-lighter)", + icon: "filled_check_circle", + }; + case "warning": + return { + primaryColor: "var(--border-color-warning-medium)", + secondaryColor: "var(--color-bg-warning-lighter)", + icon: "filled_warning", + }; + } +} + +export function generateUniqueToastId(toasts: QueuedToast[]) { + let id = ""; + let exists = true; + while (exists) { + id = `${performance.now()}-${Math.random().toString(36).slice(2, 9)}`; + exists = toasts.some((toast) => toast.id === id); + } + return id; +};