Green: positive actions, such as approved, completed, success…
diff --git a/apps/website/screens/components/button/usage/ButtonUsagePage.tsx b/apps/website/screens/components/button/usage/ButtonUsagePage.tsx
index dc640b6b33..1b8caf5085 100644
--- a/apps/website/screens/components/button/usage/ButtonUsagePage.tsx
+++ b/apps/website/screens/components/button/usage/ButtonUsagePage.tsx
@@ -14,6 +14,8 @@ import variants from "./examples/variants";
import icons from "./examples/iconUsage";
import HeaderDescriptionCell from "@/common/HeaderDescriptionCell";
import Code from "@/common/Code";
+import Image from "@/common/Image";
+import semanticButtons from "./images/semantic_buttons.png";
const sections = [
{
@@ -141,7 +143,7 @@ const sections = [
Neutral action with no specific context. Typically used for general actions. Shown in the brand's
- primary color. Use for neutral actions such as "Submit", "Save" or "Continue.”
+ primary color. Use for neutral actions such as "Submit", "Save" or "Continue.”.
@@ -152,18 +154,18 @@ const sections = [
Indicates a destructive action or highlights a critical issue. Styled in red. Use for actions like
- "Delete", "Remove" or "Cancel Subscription.”
+ "Delete", "Remove" or "Cancel Subscription.”.
-
- Warning
+
+ Info
- Alerts the user to potential issues or actions that need caution. Styled in orange. Use for actions like
- "Warning" or "Attention Needed.”
+ Provides additional information or context. Shown in blue, the brand's secondary color. Use for actions
+ like "More Info", "Details" or "Learn More.”.
@@ -174,22 +176,23 @@ const sections = [
Represents a positive action or confirms the completion of a task. Styled in green. Use for actions like
- "Confirm", "Complete" or "Approve.”
+ "Confirm", "Complete" or "Approve.”.
-
- Info
+
+ Warning
- Provides additional information or context. Shown in blue, the brand's secondary color. Use for actions
- like "More Info", "Details" or "Learn More.”
+ Alerts the user to potential issues or actions that need caution. Styled in orange. Use for actions like
+ "Warning" or "Attention Needed.”.
+
>
),
},
diff --git a/apps/website/screens/components/button/usage/images/semantic_buttons.png b/apps/website/screens/components/button/usage/images/semantic_buttons.png
new file mode 100644
index 0000000000..102018d9b6
Binary files /dev/null and b/apps/website/screens/components/button/usage/images/semantic_buttons.png differ
diff --git a/apps/website/screens/components/checkbox/code/CheckboxCodePage.tsx b/apps/website/screens/components/checkbox/code/CheckboxCodePage.tsx
index 192b45bec3..092be7e356 100644
--- a/apps/website/screens/components/checkbox/code/CheckboxCodePage.tsx
+++ b/apps/website/screens/components/checkbox/code/CheckboxCodePage.tsx
@@ -159,7 +159,7 @@ const sections = [
ref
- {"React.Ref "}
+ {"React.Ref"}
Reference to the component.
-
diff --git a/apps/website/screens/components/date-input/code/DateInputCodePage.tsx b/apps/website/screens/components/date-input/code/DateInputCodePage.tsx
index 83889dc134..2d99122108 100644
--- a/apps/website/screens/components/date-input/code/DateInputCodePage.tsx
+++ b/apps/website/screens/components/date-input/code/DateInputCodePage.tsx
@@ -231,7 +231,7 @@ const sections = [
ref
- {"React.Ref "}
+ {"React.Ref"}
Reference to the component.
-
diff --git a/apps/website/screens/components/file-input/code/FileInputCodePage.tsx b/apps/website/screens/components/file-input/code/FileInputCodePage.tsx
index 702450aaa7..1d8a5e857e 100644
--- a/apps/website/screens/components/file-input/code/FileInputCodePage.tsx
+++ b/apps/website/screens/components/file-input/code/FileInputCodePage.tsx
@@ -207,7 +207,7 @@ const sections = [
ref
- {"React.Ref "}
+ {"React.Ref"}
Reference to the component.
-
diff --git a/apps/website/screens/components/number-input/code/NumberInputCodePage.tsx b/apps/website/screens/components/number-input/code/NumberInputCodePage.tsx
index 9c8fabf10c..3c2ab4bd7e 100644
--- a/apps/website/screens/components/number-input/code/NumberInputCodePage.tsx
+++ b/apps/website/screens/components/number-input/code/NumberInputCodePage.tsx
@@ -252,7 +252,7 @@ const sections = [
ref
- {"React.Ref "}
+ {"React.Ref"}
Reference to the component.
-
diff --git a/apps/website/screens/components/password-input/code/PasswordInputCodePage.tsx b/apps/website/screens/components/password-input/code/PasswordInputCodePage.tsx
index ca0a1bfc71..7ff1ec01ad 100644
--- a/apps/website/screens/components/password-input/code/PasswordInputCodePage.tsx
+++ b/apps/website/screens/components/password-input/code/PasswordInputCodePage.tsx
@@ -199,7 +199,7 @@ const sections = [
ref
- {"React.Ref "}
+ {"React.Ref"}
Reference to the component.
-
diff --git a/apps/website/screens/components/radio-group/code/RadioGroupCodePage.tsx b/apps/website/screens/components/radio-group/code/RadioGroupCodePage.tsx
index 17935dd19d..489b3f49db 100644
--- a/apps/website/screens/components/radio-group/code/RadioGroupCodePage.tsx
+++ b/apps/website/screens/components/radio-group/code/RadioGroupCodePage.tsx
@@ -201,7 +201,7 @@ const sections = [
ref
- {"React.Ref "}
+ {"React.Ref"}
Reference to the component.
-
diff --git a/apps/website/screens/components/select/code/SelectCodePage.tsx b/apps/website/screens/components/select/code/SelectCodePage.tsx
index 1432e30b23..5d24722ee7 100644
--- a/apps/website/screens/components/select/code/SelectCodePage.tsx
+++ b/apps/website/screens/components/select/code/SelectCodePage.tsx
@@ -9,9 +9,15 @@ import uncontrolled from "./examples/uncontrolled";
import errorHandling from "./examples/errorHandling";
import groups from "./examples/groupedOptions";
import icons from "./examples/icons";
-import TableCode from "@/common/TableCode";
+import TableCode, { ExtendedTableCode } from "@/common/TableCode";
import StatusBadge from "@/common/StatusBadge";
+const optionsType = `{
+ label: string;
+ value: string;
+ icon: string | Icon;
+}`;
+
const sections = [
{
title: "Props",
@@ -74,11 +80,15 @@ const sections = [
-
- {
- "({ label: string, value: string, icon: (string | React.ReactNode & React.SVGProps ) })[] | ({ label: string, options: Option[] })[]"
- }
-
+ {"Option[] | ({ label: string, options: Option[] })[]"}
+
+ being Option the following type:
+
+ {optionsType}
+
+ and Icon:
+
+ {`React.ReactNode & React.SVGProps`}
An array of objects representing the selectable options. Each object has the following properties
@@ -253,7 +263,7 @@ const sections = [
ref
- {"React.Ref "}
+ {"React.Ref"}
Reference to the component.
-
diff --git a/apps/website/screens/components/slider/code/SliderCodePage.tsx b/apps/website/screens/components/slider/code/SliderCodePage.tsx
index e161f7af3d..c832c7ee2b 100644
--- a/apps/website/screens/components/slider/code/SliderCodePage.tsx
+++ b/apps/website/screens/components/slider/code/SliderCodePage.tsx
@@ -208,7 +208,7 @@ const sections = [
ref
- {"React.Ref "}
+ {"React.Ref"}
Reference to the component.
-
diff --git a/apps/website/screens/components/switch/code/SwitchCodePage.tsx b/apps/website/screens/components/switch/code/SwitchCodePage.tsx
index 7aa879615e..317584fb43 100644
--- a/apps/website/screens/components/switch/code/SwitchCodePage.tsx
+++ b/apps/website/screens/components/switch/code/SwitchCodePage.tsx
@@ -147,7 +147,7 @@ const sections = [
ref
- {"React.Ref "}
+ {"React.Ref"}
Reference to the component.
-
diff --git a/apps/website/screens/components/text-input/code/TextInputCodePage.tsx b/apps/website/screens/components/text-input/code/TextInputCodePage.tsx
index abdc299711..81f377ddc5 100644
--- a/apps/website/screens/components/text-input/code/TextInputCodePage.tsx
+++ b/apps/website/screens/components/text-input/code/TextInputCodePage.tsx
@@ -325,7 +325,7 @@ const sections = [
ref
- {"React.Ref "}
+ {"React.Ref"}
Reference to the component.
-
diff --git a/apps/website/screens/components/textarea/code/TextareaCodePage.tsx b/apps/website/screens/components/textarea/code/TextareaCodePage.tsx
index 782b46f432..9a95f7a0f9 100644
--- a/apps/website/screens/components/textarea/code/TextareaCodePage.tsx
+++ b/apps/website/screens/components/textarea/code/TextareaCodePage.tsx
@@ -272,7 +272,7 @@ const sections = [
ref
- {"React.Ref "}
+ {"React.Ref"}
Reference to the component.
-
diff --git a/apps/website/screens/components/toast/ToastPageLayout.tsx b/apps/website/screens/components/toast/ToastPageLayout.tsx
new file mode 100644
index 0000000000..d55e1e9a78
--- /dev/null
+++ b/apps/website/screens/components/toast/ToastPageLayout.tsx
@@ -0,0 +1,31 @@
+import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react";
+import PageHeading from "@/common/PageHeading";
+import TabsPageHeading from "@/common/TabsPageLayout";
+import ComponentHeading from "@/common/ComponentHeading";
+
+const ToastPageHeading = ({ children }: { children: React.ReactNode }) => {
+ const tabs = [
+ { label: "Code", path: "/components/toast" },
+ { label: "Usage", path: "/components/toast/usage" },
+ { label: "Specifications", path: "/components/toast/specifications" },
+ ];
+
+ return (
+
+
+
+
+
+ The toast component is a lightweight notification element that appears temporarily to provide feedback or
+ updates to the user. It is commonly used to communicate non-critical information, such as success messages,
+ warning alerts, or brief updates.
+
+
+
+
+ {children}
+
+ );
+};
+
+export default ToastPageHeading;
diff --git a/apps/website/screens/components/toast/code/ToastCodePage.tsx b/apps/website/screens/components/toast/code/ToastCodePage.tsx
new file mode 100644
index 0000000000..fd6d07711e
--- /dev/null
+++ b/apps/website/screens/components/toast/code/ToastCodePage.tsx
@@ -0,0 +1,304 @@
+import { DxcFlex, DxcLink, DxcParagraph, DxcTable, DxcToastsQueue } from "@dxc-technology/halstack-react";
+import QuickNavContainerLayout from "@/common/QuickNavContainerLayout";
+import QuickNavContainer from "@/common/QuickNavContainer";
+import DocFooter from "@/common/DocFooter";
+import TableCode, { ExtendedTableCode } from "@/common/TableCode";
+import StatusBadge from "@/common/StatusBadge";
+import Example from "@/common/example/Example";
+import basic from "./examples/basicUsage";
+import semantic from "./examples/semantic";
+import loading from "./examples/loading";
+import Code from "@/common/Code";
+
+const actionTypeString = `{
+ icon: string |
+ (React.ReactNode
+ & React.SVGProps);
+ label: string;
+ onClick: () => void;
+}`;
+
+const sections = [
+ {
+ title: "Toasts queue",
+ content: A component to be rendered at the top level of the application. ,
+ subSections: [
+ {
+ title: "Props",
+ content: (
+
+
+
+ Name
+ Type
+ Description
+ Default
+
+
+
+
+ duration
+
+ number
+
+
+ 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.
+
+
+ 3000
+
+
+
+
+ ),
+ },
+ ],
+ },
+ {
+ title: "useToast",
+ content: (
+ <>
+
+ A hook to queue toasts from any part of your application contained inside the Toast queue. It returns an
+ object with five methods, each explained below:
+
+
+
+
+ Method
+ Type
+ Description
+
+
+
+
+ default
+
+ {`(toast: Default) => void`}
+
+ Shows a toast with no implicit semantic meaning.
+
+
+ info
+
+ {`(toast: Semantic) => void`}
+
+ Shows a toast with an information semantic.
+
+
+ loading
+
+ {`(toast: Loading) => (() => void)`}
+
+
+ Shows a loading status toast. Visually and semantically, it is the same as an information toast, but
+ with the difference that it never disappears from the screen. Its removal will always depend on the
+ user, thanks to the function returned by this method.
+
+
+
+ success
+
+ {`(toast: Semantic) => void`}
+
+ Shows a toast with a success semantic.
+
+
+ warning
+
+ {`(toast: Semantic) => void`}
+
+ Shows a toast with a warning semantic.
+
+
+
+
+ Each method has a different argument type, which are detailed in the following sections.
+
+ >
+ ),
+ subSections: [
+ {
+ title: "Default",
+ content: (
+
+
+
+ Name
+ Type
+ Description
+ Default
+
+
+
+
+ action
+
+ {actionTypeString}
+
+ Tertiary button which performs a custom action, specified by the user.
+ -
+
+
+ icon
+
+ string | {"(React.ReactNode & React.SVGProps )"}
+
+
+
+ Material Symbol
+ {" "}
+ name or SVG element as the icon that will be placed next to the panel label. When using Material
+ Symbols, replace spaces with underscores. By default they are outlined if you want it to be filled
+ prefix the symbol name with "filled_" .
+
+ -
+
+
+
+
+
+ message
+
+
+
+ string
+
+ Message to be displayed as a toast.
+ -
+
+
+
+ ),
+ },
+ {
+ title: "Loading",
+ content: (
+
+
+
+ Name
+ Type
+ Description
+ Default
+
+
+
+
+ action
+
+ {actionTypeString}
+
+ Tertiary button which performs a custom action, specified by the user.
+ -
+
+
+
+
+
+ message
+
+
+
+ string
+
+ Message to be displayed as a toast.
+ -
+
+
+
+ ),
+ },
+ {
+ title: "Semantic",
+ content: (
+
+
+
+ Name
+ Type
+ Description
+ Default
+
+
+
+
+ action
+
+ {actionTypeString}
+
+ Tertiary button which performs a custom action, specified by the user.
+ -
+
+
+ hideSemanticIcon
+
+ boolean
+
+ Flag that allows to hide the semantic icon of the toast.
+
+ false
+
+
+
+
+
+
+ message
+
+
+
+ string
+
+ Message to be displayed as a toast.
+ -
+
+
+
+ ),
+ },
+ ],
+ },
+ {
+ title: "Examples",
+ subSections: [
+ {
+ title: "Basic usage",
+ content: ,
+ },
+ {
+ title: "Semantic toasts",
+ content: ,
+ },
+ {
+ title: "Loading toast",
+ content: (
+ <>
+
+ A loading toast is a toast that will never disappear from the screen. Its removal will always depend on
+ the user, thanks to the function returned by the loading method. This allows users to have
+ full control over the status of the process.
+
+
+ >
+ ),
+ },
+ ],
+ },
+];
+
+const TextareaCodePage = () => {
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+export default TextareaCodePage;
diff --git a/apps/website/screens/components/toast/code/examples/basicUsage.ts b/apps/website/screens/components/toast/code/examples/basicUsage.ts
new file mode 100644
index 0000000000..3f7475a915
--- /dev/null
+++ b/apps/website/screens/components/toast/code/examples/basicUsage.ts
@@ -0,0 +1,24 @@
+import { DxcButton, DxcInset, useToast } from "@dxc-technology/halstack-react";
+
+const code = `() => {
+ const toast = useToast();
+
+ return (
+
+ {
+ toast.default({ message: "This is a basic message." });
+ }}
+ />
+
+ );
+}`;
+
+const scope = {
+ DxcButton,
+ DxcInset,
+ useToast
+};
+
+export default { code, scope };
diff --git a/apps/website/screens/components/toast/code/examples/loading.ts b/apps/website/screens/components/toast/code/examples/loading.ts
new file mode 100644
index 0000000000..09cf668688
--- /dev/null
+++ b/apps/website/screens/components/toast/code/examples/loading.ts
@@ -0,0 +1,31 @@
+import { DxcButton, DxcInset, useToast } from "@dxc-technology/halstack-react";
+
+const code = `() => {
+ const toast = useToast();
+
+ const loadProcess = () => {
+ const loadingToast = toast.loading({ message: "Loading process..." });
+
+ setTimeout(() => {
+ loadingToast();
+ toast.success({ message: "Process finished successfully." });
+ }, 6000);
+ };
+
+ return (
+
+
+
+ );
+}`;
+
+const scope = {
+ DxcButton,
+ DxcInset,
+ useToast,
+};
+
+export default { code, scope };
diff --git a/apps/website/screens/components/toast/code/examples/semantic.ts b/apps/website/screens/components/toast/code/examples/semantic.ts
new file mode 100644
index 0000000000..0eecef4f00
--- /dev/null
+++ b/apps/website/screens/components/toast/code/examples/semantic.ts
@@ -0,0 +1,44 @@
+import { DxcButton, DxcFlex, DxcInset, useToast } from "@dxc-technology/halstack-react";
+
+const code = `() => {
+ const toast = useToast();
+
+ const action = { label: "Action", onClick: () => {} };
+
+ return (
+
+
+ {
+ toast.info({ message: "This is a information message.", action });
+ }}
+ />
+ {
+ toast.success({ message: "This is a success message.", action });
+ }}
+ />
+ {
+ toast.warning({ message: "This is a warning message.", action });
+ }}
+ />
+
+
+ );
+}`;
+
+const scope = {
+ DxcButton,
+ DxcFlex,
+ DxcInset,
+ useToast,
+};
+
+export default { code, scope };
diff --git a/apps/website/screens/components/toast/specs/ToastSpecsPage.tsx b/apps/website/screens/components/toast/specs/ToastSpecsPage.tsx
new file mode 100644
index 0000000000..93cda09b8d
--- /dev/null
+++ b/apps/website/screens/components/toast/specs/ToastSpecsPage.tsx
@@ -0,0 +1,51 @@
+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: (
+
+
+
+ ),
+ },
+ {
+ title: "Anatomy",
+ content: (
+ <>
+
+
+ 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
new file mode 100644
index 0000000000..5f6b66262d
Binary files /dev/null and b/apps/website/screens/components/toast/specs/images/toast_anatomy.png 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
new file mode 100644
index 0000000000..8ac5c2a006
Binary files /dev/null and b/apps/website/screens/components/toast/specs/images/toast_specs.png differ
diff --git a/apps/website/screens/components/toast/usage/ToastUsagePage.tsx b/apps/website/screens/components/toast/usage/ToastUsagePage.tsx
new file mode 100644
index 0000000000..5130034b2e
--- /dev/null
+++ b/apps/website/screens/components/toast/usage/ToastUsagePage.tsx
@@ -0,0 +1,188 @@
+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.)
+
+
+
+
+
+ >
+ ),
+ },
+ {
+ 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.
+
+
+
+
+ >
+ ),
+ },
+ {
+ 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.
+
+
+
+ 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
new file mode 100644
index 0000000000..a99cdd72e6
Binary files /dev/null and b/apps/website/screens/components/toast/usage/images/loading_toast.png 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
new file mode 100644
index 0000000000..1d6823278a
Binary files /dev/null and b/apps/website/screens/components/toast/usage/images/semantic_toasts.png 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
new file mode 100644
index 0000000000..3d34099ff3
Binary files /dev/null and b/apps/website/screens/components/toast/usage/images/toasts_positioning.png differ
diff --git a/apps/website/screens/overview/introduction/IntroductionPage.tsx b/apps/website/screens/overview/introduction/IntroductionPage.tsx
index b69e451d84..6023edd5d4 100644
--- a/apps/website/screens/overview/introduction/IntroductionPage.tsx
+++ b/apps/website/screens/overview/introduction/IntroductionPage.tsx
@@ -228,4 +228,4 @@ const Introduction = () => {
);
};
-export default Introduction;
+export default Introduction;
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 25f0880981..941ed2e993 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,7 +23,6 @@
"@cloudscape-design/components": "^3.0.706",
"@dxc-technology/halstack-react": "*",
"@radix-ui/react-popover": "^1.0.7",
- "@types/styled-components": "5.1.29",
"cross-env": "^7.0.3",
"next": "14.2.10",
"raw-loader": "^4.0.2",
@@ -42,6 +41,7 @@
"@types/react": "^18",
"@types/react-color": "^3.0.6",
"@types/react-dom": "^18",
+ "@types/styled-components": "5.1.29",
"eslint": "^8",
"eslint-config-next": "14.2.4",
"typescript": "^5"
@@ -2148,9 +2148,9 @@
}
},
"node_modules/@cloudscape-design/collection-hooks": {
- "version": "1.0.52",
- "resolved": "https://registry.npmjs.org/@cloudscape-design/collection-hooks/-/collection-hooks-1.0.52.tgz",
- "integrity": "sha512-7HcMpyAaMBP4gnVnEAdShL6RqfKAs1b1KttAZml2sxI+tmrWY50DkbUH1gP4SuxRX/+b7eiA3kBvFm8HmlkWZA==",
+ "version": "1.0.53",
+ "resolved": "https://registry.npmjs.org/@cloudscape-design/collection-hooks/-/collection-hooks-1.0.53.tgz",
+ "integrity": "sha512-3EvwMN+t9AeoG4Lzu6LtTet/3OwM2hIegeYBA6QGRycMZ3VWKhI7y8GMj0xEGJY4+9Yx10jyjhPcC6ER1s0/UQ==",
"license": "Apache-2.0",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
@@ -2167,9 +2167,9 @@
}
},
"node_modules/@cloudscape-design/components": {
- "version": "3.0.756",
- "resolved": "https://registry.npmjs.org/@cloudscape-design/components/-/components-3.0.756.tgz",
- "integrity": "sha512-EwG1cAXHtgyQ2/XEDvjDkxjZJR2cHJmyIDTZqUWls2ARHWa22+D8L1890yaxsn2Mb72qOKXh5rHZDDLllKKnHg==",
+ "version": "3.0.758",
+ "resolved": "https://registry.npmjs.org/@cloudscape-design/components/-/components-3.0.758.tgz",
+ "integrity": "sha512-XuQG5DW1ukeMdyckxWfqcUxcmUpwlcrpQbbu4GRsd2YPDi2pqzKSxofshbs8NWzJc4jK5U84CTF2qx2GjSdGCg==",
"license": "Apache-2.0",
"dependencies": {
"@cloudscape-design/collection-hooks": "^1.0.0",
@@ -2198,9 +2198,9 @@
}
},
"node_modules/@cloudscape-design/test-utils-core": {
- "version": "1.0.41",
- "resolved": "https://registry.npmjs.org/@cloudscape-design/test-utils-core/-/test-utils-core-1.0.41.tgz",
- "integrity": "sha512-CXvkRndyX+uCkuCNT/D4IUFvio7KhKK8Gfm2ISdk92H89ybku07ff4oK5R7elMfAQiX0DR/snb8g72b+e1XIYw==",
+ "version": "1.0.42",
+ "resolved": "https://registry.npmjs.org/@cloudscape-design/test-utils-core/-/test-utils-core-1.0.42.tgz",
+ "integrity": "sha512-vNSODT+6Kp8LSI56ddorC7hLkts2O+AH3v6DYfbwhx/giHs4rDtKPNGcQ6abyrY2DLIByWgFdbHpx2hNoQgCPw==",
"license": "Apache-2.0",
"dependencies": {
"css-selector-tokenizer": "^0.8.0",
@@ -5225,16 +5225,16 @@
}
},
"node_modules/@rollup/pluginutils/node_modules/@types/estree": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
- "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"dev": true,
"license": "MIT"
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.21.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.3.tgz",
- "integrity": "sha512-MmKSfaB9GX+zXl6E8z4koOr/xU63AMVleLEa64v7R0QF/ZloMs5vcD1sHgM64GXXS1csaJutG+ddtzcueI/BLg==",
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.0.tgz",
+ "integrity": "sha512-/IZQvg6ZR0tAkEi4tdXOraQoWeJy9gbQ/cx4I7k9dJaCk9qrXEcdouxRVz5kZXt5C2bQ9pILoAA+KB4C/d3pfw==",
"cpu": [
"arm"
],
@@ -5246,9 +5246,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.21.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.3.tgz",
- "integrity": "sha512-zrt8ecH07PE3sB4jPOggweBjJMzI1JG5xI2DIsUbkA+7K+Gkjys6eV7i9pOenNSDJH3eOr/jLb/PzqtmdwDq5g==",
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.0.tgz",
+ "integrity": "sha512-ETHi4bxrYnvOtXeM7d4V4kZWixib2jddFacJjsOjwbgYSRsyXYtZHC4ht134OsslPIcnkqT+TKV4eU8rNBKyyQ==",
"cpu": [
"arm64"
],
@@ -5260,9 +5260,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.21.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.3.tgz",
- "integrity": "sha512-P0UxIOrKNBFTQaXTxOH4RxuEBVCgEA5UTNV6Yz7z9QHnUJ7eLX9reOd/NYMO3+XZO2cco19mXTxDMXxit4R/eQ==",
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.0.tgz",
+ "integrity": "sha512-ZWgARzhSKE+gVUX7QWaECoRQsPwaD8ZR0Oxb3aUpzdErTvlEadfQpORPXkKSdKbFci9v8MJfkTtoEHnnW9Ulng==",
"cpu": [
"arm64"
],
@@ -5274,9 +5274,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.21.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.3.tgz",
- "integrity": "sha512-L1M0vKGO5ASKntqtsFEjTq/fD91vAqnzeaF6sfNAy55aD+Hi2pBI5DKwCO+UNDQHWsDViJLqshxOahXyLSh3EA==",
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.0.tgz",
+ "integrity": "sha512-h0ZAtOfHyio8Az6cwIGS+nHUfRMWBDO5jXB8PQCARVF6Na/G6XS2SFxDl8Oem+S5ZsHQgtsI7RT4JQnI1qrlaw==",
"cpu": [
"x64"
],
@@ -5288,9 +5288,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.21.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.3.tgz",
- "integrity": "sha512-btVgIsCjuYFKUjopPoWiDqmoUXQDiW2A4C3Mtmp5vACm7/GnyuprqIDPNczeyR5W8rTXEbkmrJux7cJmD99D2g==",
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.0.tgz",
+ "integrity": "sha512-9pxQJSPwFsVi0ttOmqLY4JJ9pg9t1gKhK0JDbV1yUEETSx55fdyCjt39eBQ54OQCzAF0nVGO6LfEH1KnCPvelA==",
"cpu": [
"arm"
],
@@ -5302,9 +5302,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.21.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.3.tgz",
- "integrity": "sha512-zmjbSphplZlau6ZTkxd3+NMtE4UKVy7U4aVFMmHcgO5CUbw17ZP6QCgyxhzGaU/wFFdTfiojjbLG3/0p9HhAqA==",
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.0.tgz",
+ "integrity": "sha512-YJ5Ku5BmNJZb58A4qSEo3JlIG4d3G2lWyBi13ABlXzO41SsdnUKi3HQHe83VpwBVG4jHFTW65jOQb8qyoR+qzg==",
"cpu": [
"arm"
],
@@ -5316,9 +5316,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.21.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.3.tgz",
- "integrity": "sha512-nSZfcZtAnQPRZmUkUQwZq2OjQciR6tEoJaZVFvLHsj0MF6QhNMg0fQ6mUOsiCUpTqxTx0/O6gX0V/nYc7LrgPw==",
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.0.tgz",
+ "integrity": "sha512-U4G4u7f+QCqHlVg1Nlx+qapZy+QoG+NV6ux+upo/T7arNGwKvKP2kmGM4W5QTbdewWFgudQxi3kDNST9GT1/mg==",
"cpu": [
"arm64"
],
@@ -5330,9 +5330,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.21.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.3.tgz",
- "integrity": "sha512-MnvSPGO8KJXIMGlQDYfvYS3IosFN2rKsvxRpPO2l2cum+Z3exiExLwVU+GExL96pn8IP+GdH8Tz70EpBhO0sIQ==",
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.0.tgz",
+ "integrity": "sha512-aQpNlKmx3amwkA3a5J6nlXSahE1ijl0L9KuIjVOUhfOh7uw2S4piR3mtpxpRtbnK809SBtyPsM9q15CPTsY7HQ==",
"cpu": [
"arm64"
],
@@ -5344,9 +5344,9 @@
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
- "version": "4.21.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.3.tgz",
- "integrity": "sha512-+W+p/9QNDr2vE2AXU0qIy0qQE75E8RTwTwgqS2G5CRQ11vzq0tbnfBd6brWhS9bCRjAjepJe2fvvkvS3dno+iw==",
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.0.tgz",
+ "integrity": "sha512-9fx6Zj/7vve/Fp4iexUFRKb5+RjLCff6YTRQl4CoDhdMfDoobWmhAxQWV3NfShMzQk1Q/iCnageFyGfqnsmeqQ==",
"cpu": [
"ppc64"
],
@@ -5358,9 +5358,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.21.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.3.tgz",
- "integrity": "sha512-yXH6K6KfqGXaxHrtr+Uoy+JpNlUlI46BKVyonGiaD74ravdnF9BUNC+vV+SIuB96hUMGShhKV693rF9QDfO6nQ==",
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.0.tgz",
+ "integrity": "sha512-VWQiCcN7zBgZYLjndIEh5tamtnKg5TGxyZPWcN9zBtXBwfcGSZ5cHSdQZfQH/GB4uRxk0D3VYbOEe/chJhPGLQ==",
"cpu": [
"riscv64"
],
@@ -5372,9 +5372,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.21.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.3.tgz",
- "integrity": "sha512-R8cwY9wcnApN/KDYWTH4gV/ypvy9yZUHlbJvfaiXSB48JO3KpwSpjOGqO4jnGkLDSk1hgjYkTbTt6Q7uvPf8eg==",
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.0.tgz",
+ "integrity": "sha512-EHmPnPWvyYqncObwqrosb/CpH3GOjE76vWVs0g4hWsDRUVhg61hBmlVg5TPXqF+g+PvIbqkC7i3h8wbn4Gp2Fg==",
"cpu": [
"s390x"
],
@@ -5386,9 +5386,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.21.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.3.tgz",
- "integrity": "sha512-kZPbX/NOPh0vhS5sI+dR8L1bU2cSO9FgxwM8r7wHzGydzfSjLRCFAT87GR5U9scj2rhzN3JPYVC7NoBbl4FZ0g==",
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.0.tgz",
+ "integrity": "sha512-tsSWy3YQzmpjDKnQ1Vcpy3p9Z+kMFbSIesCdMNgLizDWFhrLZIoN21JSq01g+MZMDFF+Y1+4zxgrlqPjid5ohg==",
"cpu": [
"x64"
],
@@ -5400,9 +5400,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.21.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.3.tgz",
- "integrity": "sha512-S0Yq+xA1VEH66uiMNhijsWAafffydd2X5b77eLHfRmfLsRSpbiAWiRHV6DEpz6aOToPsgid7TI9rGd6zB1rhbg==",
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.0.tgz",
+ "integrity": "sha512-anr1Y11uPOQrpuU8XOikY5lH4Qu94oS6j0xrulHk3NkLDq19MlX8Ng/pVipjxBJ9a2l3+F39REZYyWQFkZ4/fw==",
"cpu": [
"x64"
],
@@ -5414,9 +5414,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.21.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.3.tgz",
- "integrity": "sha512-9isNzeL34yquCPyerog+IMCNxKR8XYmGd0tHSV+OVx0TmE0aJOo9uw4fZfUuk2qxobP5sug6vNdZR6u7Mw7Q+Q==",
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.0.tgz",
+ "integrity": "sha512-7LB+Bh+Ut7cfmO0m244/asvtIGQr5pG5Rvjz/l1Rnz1kDzM02pSX9jPaS0p+90H5I1x4d1FkCew+B7MOnoatNw==",
"cpu": [
"arm64"
],
@@ -5428,9 +5428,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.21.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.3.tgz",
- "integrity": "sha512-nMIdKnfZfzn1Vsk+RuOvl43ONTZXoAPUUxgcU0tXooqg4YrAqzfKzVenqqk2g5efWh46/D28cKFrOzDSW28gTA==",
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.0.tgz",
+ "integrity": "sha512-+3qZ4rer7t/QsC5JwMpcvCVPRcJt1cJrYS/TMJZzXIJbxWFQEVhrIc26IhB+5Z9fT9umfVc+Es2mOZgl+7jdJQ==",
"cpu": [
"ia32"
],
@@ -5442,9 +5442,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.21.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.3.tgz",
- "integrity": "sha512-fOvu7PCQjAj4eWDEuD8Xz5gpzFqXzGlxHZozHP4b9Jxv9APtdxL6STqztDzMLuRXEc4UpXGGhx029Xgm91QBeA==",
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.0.tgz",
+ "integrity": "sha512-YdicNOSJONVx/vuPkgPTyRoAPx3GbknBZRCOUkK84FJ/YTfs/F0vl/YsMscrB6Y177d+yDRcj+JWMPMCgshwrA==",
"cpu": [
"x64"
],
@@ -5978,9 +5978,9 @@
"license": "MIT"
},
"node_modules/@storybook/icons": {
- "version": "1.2.10",
- "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-1.2.10.tgz",
- "integrity": "sha512-310apKdDcjbbX2VSLWPwhEwAgjxTzVagrwucVZIdGPErwiAppX8KvBuWZgPo+rQLVrtH8S+pw1dbUwjcE6d7og==",
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-1.2.12.tgz",
+ "integrity": "sha512-UxgyK5W3/UV4VrI3dl6ajGfHM4aOqMAkFLWe2KibeQudLf6NJpDrDMSHwZj+3iKC4jFU7dkKbbtH2h/al4sW3Q==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6660,108 +6660,31 @@
}
},
"node_modules/@testing-library/react": {
- "version": "13.4.0",
- "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz",
- "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==",
+ "version": "16.0.1",
+ "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz",
+ "integrity": "sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.12.5",
- "@testing-library/dom": "^8.5.0",
- "@types/react-dom": "^18.0.0"
+ "@babel/runtime": "^7.12.5"
},
"engines": {
- "node": ">=12"
+ "node": ">=18"
},
"peerDependencies": {
+ "@testing-library/dom": "^10.0.0",
+ "@types/react": "^18.0.0",
+ "@types/react-dom": "^18.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
- }
- },
- "node_modules/@testing-library/react/node_modules/@testing-library/dom": {
- "version": "8.20.1",
- "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz",
- "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/code-frame": "^7.10.4",
- "@babel/runtime": "^7.12.5",
- "@types/aria-query": "^5.0.1",
- "aria-query": "5.1.3",
- "chalk": "^4.1.0",
- "dom-accessibility-api": "^0.5.9",
- "lz-string": "^1.5.0",
- "pretty-format": "^27.0.2"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@testing-library/react/node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
},
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/@testing-library/react/node_modules/aria-query": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz",
- "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "deep-equal": "^2.0.5"
- }
- },
- "node_modules/@testing-library/react/node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/@testing-library/react/node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@testing-library/react/node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
}
},
"node_modules/@testing-library/user-event": {
@@ -7135,6 +7058,7 @@
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz",
"integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@types/react": "*",
@@ -7466,6 +7390,7 @@
"version": "5.1.29",
"resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.29.tgz",
"integrity": "sha512-5h/ah9PAblggQ6Laa4peplT4iY5ddA8qM1LMD4HzwToUWs3hftfy0fayeRgbtH1JZUdw5CCaowmz7Lnb8SjIxQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@types/hoist-non-react-statics": "*",
@@ -8045,9 +7970,9 @@
}
},
"node_modules/@vitest/expect/node_modules/@types/estree": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
- "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"dev": true,
"license": "MIT"
},
@@ -9612,9 +9537,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001660",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz",
- "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==",
+ "version": "1.0.30001662",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001662.tgz",
+ "integrity": "sha512-sgMUVwLmGseH8ZIrm1d51UbrhqMCH3jvS7gF/M6byuHOnKyLOBL7W8yz5V02OHwgLGA36o/AFhWzzh4uc5aqTA==",
"funding": [
{
"type": "opencollective",
@@ -22083,9 +22008,9 @@
}
},
"node_modules/rollup": {
- "version": "4.21.3",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.3.tgz",
- "integrity": "sha512-7sqRtBNnEbcBtMeRVc6VRsJMmpI+JU1z9VTvW8D4gXIYQFz0aLcsE6rRkyghZkLfEgUZgVvOG7A5CVz/VW5GIA==",
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.0.tgz",
+ "integrity": "sha512-W21MUIFPZ4+O2Je/EU+GP3iz7PH4pVPUXSbEZdatQnxo29+3rsUjgrJmzuAZU24z7yRAnFN6ukxeAhZh/c7hzg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -22099,22 +22024,22 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.21.3",
- "@rollup/rollup-android-arm64": "4.21.3",
- "@rollup/rollup-darwin-arm64": "4.21.3",
- "@rollup/rollup-darwin-x64": "4.21.3",
- "@rollup/rollup-linux-arm-gnueabihf": "4.21.3",
- "@rollup/rollup-linux-arm-musleabihf": "4.21.3",
- "@rollup/rollup-linux-arm64-gnu": "4.21.3",
- "@rollup/rollup-linux-arm64-musl": "4.21.3",
- "@rollup/rollup-linux-powerpc64le-gnu": "4.21.3",
- "@rollup/rollup-linux-riscv64-gnu": "4.21.3",
- "@rollup/rollup-linux-s390x-gnu": "4.21.3",
- "@rollup/rollup-linux-x64-gnu": "4.21.3",
- "@rollup/rollup-linux-x64-musl": "4.21.3",
- "@rollup/rollup-win32-arm64-msvc": "4.21.3",
- "@rollup/rollup-win32-ia32-msvc": "4.21.3",
- "@rollup/rollup-win32-x64-msvc": "4.21.3",
+ "@rollup/rollup-android-arm-eabi": "4.22.0",
+ "@rollup/rollup-android-arm64": "4.22.0",
+ "@rollup/rollup-darwin-arm64": "4.22.0",
+ "@rollup/rollup-darwin-x64": "4.22.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.22.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.22.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.22.0",
+ "@rollup/rollup-linux-arm64-musl": "4.22.0",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.22.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.22.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.22.0",
+ "@rollup/rollup-linux-x64-gnu": "4.22.0",
+ "@rollup/rollup-linux-x64-musl": "4.22.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.22.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.22.0",
+ "@rollup/rollup-win32-x64-msvc": "4.22.0",
"fsevents": "~2.3.2"
}
},
@@ -25672,9 +25597,9 @@
"license": "MIT"
},
"node_modules/webpack/node_modules/@types/estree": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
- "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"license": "MIT",
"peer": true
},
@@ -26160,12 +26085,13 @@
"@storybook/addon-essentials": "^8.1.10",
"@storybook/addon-interactions": "^8.1.10",
"@storybook/addon-links": "^8.1.10",
+ "@storybook/addon-viewport": "^8.2.9",
"@storybook/blocks": "^8.1.10",
"@storybook/react": "^8.1.10",
"@storybook/react-vite": "^8.1.10",
"@storybook/test": "^8.1.10",
"@storybook/test-runner": "^0.18.2",
- "@testing-library/react": "^13.0.0",
+ "@testing-library/react": "^16.0.1",
"@testing-library/user-event": "^13.0.0",
"@turbo/gen": "^1.12.4",
"@types/eslint": "^8.56.5",
@@ -26190,8 +26116,8 @@
"typescript": "^5.3.3"
},
"peerDependencies": {
- "react": "^18.x",
- "react-dom": "^18.x",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
"styled-components": "^5.0.1"
}
},
diff --git a/packages/lib/.storybook/main.ts b/packages/lib/.storybook/main.ts
index ff5fe0b9cd..53feea3d29 100644
--- a/packages/lib/.storybook/main.ts
+++ b/packages/lib/.storybook/main.ts
@@ -6,6 +6,7 @@ const config: StorybookConfig = {
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
+ '@storybook/addon-viewport',
"storybook-addon-pseudo-states",
"@storybook/addon-a11y",
"@chromatic-com/storybook",
diff --git a/packages/lib/package.json b/packages/lib/package.json
index 4734de3d9d..b54f81a18e 100644
--- a/packages/lib/package.json
+++ b/packages/lib/package.json
@@ -29,8 +29,8 @@
"test:watch": "jest --env=jsdom --config=./jest.config.js --watch"
},
"peerDependencies": {
- "react": "^18.x",
- "react-dom": "^18.x",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
"styled-components": "^5.0.1"
},
"dependencies": {
@@ -57,12 +57,13 @@
"@storybook/addon-essentials": "^8.1.10",
"@storybook/addon-interactions": "^8.1.10",
"@storybook/addon-links": "^8.1.10",
+ "@storybook/addon-viewport": "^8.2.9",
"@storybook/blocks": "^8.1.10",
"@storybook/react": "^8.1.10",
"@storybook/react-vite": "^8.1.10",
"@storybook/test": "^8.1.10",
"@storybook/test-runner": "^0.18.2",
- "@testing-library/react": "^13.0.0",
+ "@testing-library/react": "^16.0.1",
"@testing-library/user-event": "^13.0.0",
"@turbo/gen": "^1.12.4",
"@types/eslint": "^8.56.5",
diff --git a/packages/lib/src/common/variables.ts b/packages/lib/src/common/variables.ts
index cd81bf93f9..9ddb1e0a73 100644
--- a/packages/lib/src/common/variables.ts
+++ b/packages/lib/src/common/variables.ts
@@ -1500,14 +1500,6 @@ export const responsiveSizes = {
};
export const defaultTranslatedComponentLabels = {
- formFields: {
- optionalLabel: "(Optional)",
- requiredSelectionErrorMessage: "This field is required. Please, choose an option.",
- requiredValueErrorMessage: "This field is required. Please, enter a value.",
- formatRequestedErrorMessage: "Please match the format requested.",
- lengthErrorMessage: (minLength?: number, maxLength?: number) => `Min length ${minLength}, max length ${maxLength}.`,
- logoAlternativeText: "Logo",
- },
applicationLayout: {
visibilityToggleTitle: "Toggle sidenav visibility",
},
@@ -1517,6 +1509,25 @@ export const defaultTranslatedComponentLabels = {
warningTitleText: "warning",
errorTitleText: "error",
},
+ calendar: {
+ daysShort: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
+ months: [
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December",
+ ],
+ previousMonthTitle: "Previous month",
+ nextMonthTitle: "Next month",
+ },
dateInput: {
invalidDateErrorMessage: "Invalid date.",
},
@@ -1536,6 +1547,14 @@ export const defaultTranslatedComponentLabels = {
footer: {
copyrightText: (year: number) => `© DXC Technology ${year}. All rights reserved.`,
},
+ formFields: {
+ optionalLabel: "(Optional)",
+ requiredSelectionErrorMessage: "This field is required. Please, choose an option.",
+ requiredValueErrorMessage: "This field is required. Please, enter a value.",
+ formatRequestedErrorMessage: "Please match the format requested.",
+ lengthErrorMessage: (minLength?: number, maxLength?: number) => `Min length ${minLength}, max length ${maxLength}.`,
+ logoAlternativeText: "Logo",
+ },
header: {
closeIcon: "Close menu",
hamburguerTitle: "Menu",
@@ -1577,24 +1596,8 @@ export const defaultTranslatedComponentLabels = {
searchingMessage: "Searching...",
fetchingDataErrorMessage: "Error fetching data",
},
- calendar: {
- daysShort: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
- months: [
- "January",
- "February",
- "March",
- "April",
- "May",
- "June",
- "July",
- "August",
- "September",
- "October",
- "November",
- "December",
- ],
- previousMonthTitle: "Previous month",
- nextMonthTitle: "Next month",
+ toast: {
+ clearToastActionTitle: "Clear toast",
},
};
diff --git a/packages/lib/src/index.ts b/packages/lib/src/index.ts
index 9a720a5017..3c712db26a 100644
--- a/packages/lib/src/index.ts
+++ b/packages/lib/src/index.ts
@@ -44,11 +44,14 @@ import DxcTabs from "./tabs/Tabs";
import DxcTag from "./tag/Tag";
import DxcTextarea from "./textarea/Textarea";
import DxcTextInput from "./text-input/TextInput";
+import DxcToastsQueue from "./toast/ToastsQueue";
import DxcToggleGroup from "./toggle-group/ToggleGroup";
import DxcTooltip from "./tooltip/Tooltip";
import DxcTypography from "./typography/Typography";
import DxcWizard from "./wizard/Wizard";
+import useToast from "./toast/useToast";
+
import HalstackContext, { HalstackProvider, HalstackLanguageContext } from "./HalstackContext";
export {
@@ -98,6 +101,7 @@ export {
DxcTag,
DxcTextarea,
DxcTextInput,
+ DxcToastsQueue,
DxcToggleGroup,
DxcTooltip,
DxcTypography,
@@ -105,4 +109,5 @@ export {
HalstackContext,
HalstackLanguageContext,
HalstackProvider,
+ useToast as useToast,
};
diff --git a/packages/lib/src/toast/Toast.accessibility.test.tsx b/packages/lib/src/toast/Toast.accessibility.test.tsx
new file mode 100644
index 0000000000..a95bc3a4f1
--- /dev/null
+++ b/packages/lib/src/toast/Toast.accessibility.test.tsx
@@ -0,0 +1,70 @@
+import { render } from "@testing-library/react";
+import { axe } from "../../test/accessibility/axe-helper.js";
+import DxcToast from "./Toast";
+import DxcToastsQueue from "./ToastsQueue";
+import useToast from "./useToast";
+import DxcButton from "../button/Button";
+import userEvent from "@testing-library/user-event";
+
+const actionIcon = {
+ label: "Action",
+ onClick: () => {
+ console.log("Action clicked");
+ },
+ icon: "restart_alt",
+};
+
+const ToastPage = () => {
+ const toast = useToast();
+ return (
+ {
+ toast.default({ message: "This is a simple placed toast." });
+ }}
+ />
+ );
+};
+const TestExample = () => (
+
+
+
+);
+
+describe("Toast component accessibility tests", () => {
+ it("Toast queue should not have accessibility issues", async () => {
+ const { container } = render( );
+ const results = await axe(container);
+ const button = container.querySelector("button");
+ userEvent.click(button);
+ expect(results).toHaveNoViolations();
+ });
+ it("Should not have basic accessibility issues", async () => {
+ const { container } = render(
+ {}}
+ icon="rocket"
+ action={actionIcon}
+ />
+ );
+ const results = await axe(container);
+ expect(results).toHaveNoViolations();
+ });
+ it("Should not have accessibility issues when loading", async () => {
+ const { container } = render(
+ {}}
+ action={actionIcon}
+ loading
+ />
+ );
+ const results = await axe(container);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/lib/src/toast/Toast.stories.tsx b/packages/lib/src/toast/Toast.stories.tsx
new file mode 100644
index 0000000000..7e3d59d6c7
--- /dev/null
+++ b/packages/lib/src/toast/Toast.stories.tsx
@@ -0,0 +1,263 @@
+import { userEvent, within } from "@storybook/test";
+import ExampleContainer from "../../.storybook/components/ExampleContainer";
+import Title from "../../.storybook/components/Title";
+import DxcButton from "../button/Button";
+import DxcFlex from "../flex/Flex";
+import DxcToast from "./Toast";
+import DxcToastsQueue from "./ToastsQueue";
+import useToast from "./useToast";
+import { INITIAL_VIEWPORTS } from "@storybook/addon-viewport";
+
+export default {
+ title: "Toast",
+ component: DxcToast,
+};
+
+const action = {
+ label: "Action",
+ onClick: () => {
+ console.log("Action clicked");
+ },
+};
+const actionIcon = {
+ label: "Action",
+ onClick: () => {
+ console.log("Action clicked");
+ },
+ icon: "restart_alt",
+};
+const onClear = () => {};
+
+export const Chromatic = () => (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+);
+
+const Screens = () => {
+ const toast = useToast();
+
+ return (
+
+
+
+ {
+ toast.default({ message: "This is a simple placed toast." });
+ }}
+ />
+ {
+ toast.info({
+ message:
+ "This is a very long label for a Toast. Please, always try to avoid this king of messages, be brief and concise.",
+ action: actionIcon,
+ });
+ }}
+ />
+ {
+ toast.success({
+ message:
+ "This is another very long label for a Toast. Please, always try to avoid this king of messages, be brief and concise.",
+ action: action,
+ });
+ }}
+ />
+
+
+ );
+};
+const ToastsQueue = () => (
+
+
+
+);
+
+const playFunc = async ({ canvasElement }) => {
+ const canvas = within(canvasElement);
+ await userEvent.click(canvas.getByText("Show default toast"));
+ await userEvent.click(canvas.getByText("Show info toast"));
+ await userEvent.click(canvas.getByText("Show success toast"));
+};
+
+export const FullScreenToast = ToastsQueue.bind({});
+FullScreenToast.play = playFunc;
+
+export const MobileScreenToast = ToastsQueue.bind({});
+MobileScreenToast.parameters = {
+ viewport: {
+ viewports: INITIAL_VIEWPORTS,
+ defaultViewport: "iphonex",
+ },
+};
+MobileScreenToast.play = playFunc;
diff --git a/packages/lib/src/toast/Toast.test.tsx b/packages/lib/src/toast/Toast.test.tsx
new file mode 100644
index 0000000000..c483e471af
--- /dev/null
+++ b/packages/lib/src/toast/Toast.test.tsx
@@ -0,0 +1,226 @@
+import userEvent from "@testing-library/user-event";
+import DxcButton from "../button/Button";
+import DxcToastsQueue from "./ToastsQueue";
+import useToast from "./useToast";
+import { render, waitFor } from "@testing-library/react";
+import { act } from "@testing-library/react";
+
+const ToastPage = ({ onClick }: { onClick?: () => void }) => {
+ const toast = useToast();
+
+ const loadingFunc = () => {
+ const removeLoadingToast = toast.loading({ message: "Loading process..." });
+
+ setTimeout(() => {
+ removeLoadingToast();
+ toast.success({ message: "The process ended successfully." });
+ }, 5000);
+ };
+
+ return (
+ <>
+ {
+ toast.info({ message: "This is an information toast." });
+ }}
+ />
+ {
+ toast.loading({ message: "Loading..." });
+ }}
+ />
+ {
+ onClick
+ ? toast.default({ message: "This is a simple toast.", action: { label: "Action", onClick } })
+ : toast.default({ message: "This is a simple toast." });
+ }}
+ />
+
+ >
+ );
+};
+
+describe("Toast component tests", () => {
+ test("Renders the component", async () => {
+ const { getByText } = render(
+
+
+
+ );
+ const button = getByText("Show toast");
+ userEvent.click(button);
+ await waitFor(() => {
+ expect(getByText("This is a simple toast.")).toBeTruthy();
+ });
+ });
+ test("Toast disappears after the specified duration", async () => {
+ jest.useFakeTimers();
+ const { getByText, queryByText } = render(
+
+
+
+ );
+ const button = getByText("Show toast");
+ userEvent.click(button);
+
+ act(() => {
+ jest.advanceTimersByTime(4249);
+ });
+ expect(getByText("This is a simple toast.")).toBeTruthy();
+
+ act(() => {
+ jest.advanceTimersByTime(1);
+ });
+ expect(queryByText("This is a simple toast.")).toBeFalsy();
+
+ jest.useRealTimers();
+ });
+ test("If duration > 5000, the toast disappears at 5000ms", async () => {
+ jest.useFakeTimers();
+ const { getByText, queryByText } = render(
+
+
+
+ );
+ const button = getByText("Show toast");
+ userEvent.click(button);
+
+ act(() => {
+ jest.advanceTimersByTime(5001);
+ });
+ expect(queryByText("This is a simple toast.")).toBeFalsy();
+
+ jest.useRealTimers();
+ });
+ test("If duration < 3000, the toast disappears at 3000ms", async () => {
+ jest.useFakeTimers();
+ const { getByText, queryByText } = render(
+
+
+
+ );
+ const button = getByText("Show toast");
+ userEvent.click(button);
+
+ act(() => {
+ jest.advanceTimersByTime(3001);
+ });
+ expect(queryByText("This is a simple toast.")).toBeFalsy();
+
+ jest.useRealTimers();
+ });
+ test("Clear action removes the toast", async () => {
+ const { getByText, getByLabelText, queryByText } = render(
+
+
+
+ );
+ const button = getByText("Show toast");
+ userEvent.click(button);
+ const clearButton = getByLabelText("Clear toast");
+ userEvent.click(clearButton);
+ await waitFor(() => {
+ expect(queryByText("This is a simple toast.")).toBeFalsy();
+ });
+ });
+ test("Action button executes the onClick function", () => {
+ const onClick = jest.fn();
+ const { getByText } = render(
+
+
+
+ );
+ const button = getByText("Show toast");
+ userEvent.click(button);
+ const actionButton = getByText("Action");
+ userEvent.click(actionButton);
+ expect(onClick).toHaveBeenCalled();
+ });
+ test("Toast queue can only accumulate 5 toasts at the same time", async () => {
+ const { getByText, getAllByText } = render(
+
+
+
+ );
+ const button = getByText("Show toast");
+ for (let i = 0; i < 6; i++) {
+ userEvent.click(button);
+ }
+ await waitFor(() => {
+ expect(getAllByText("This is a simple toast.").length).toBe(5);
+ });
+ });
+ test("Toast queue removes the older toast when more than 5 toast accumulate", async () => {
+ const { getByText, getAllByText, queryByText } = render(
+
+
+
+ );
+ const infoBtn = getByText("Show info toast");
+ const defaultBtn = getByText("Show toast");
+
+ userEvent.click(infoBtn);
+ waitFor(() => {
+ expect(getByText("This is an information toast.")).toBeTruthy();
+ });
+ for (let i = 0; i < 6; i++) {
+ userEvent.click(defaultBtn);
+ }
+ await waitFor(() => {
+ expect(queryByText("This is an information toast.")).toBeFalsy();
+ expect(getAllByText("This is a simple toast.").length).toBe(5);
+ });
+ });
+ test("Loading toast is never removed automatically", async () => {
+ jest.useFakeTimers();
+ const { getByText } = render(
+
+
+
+ );
+ const button = getByText("Show loading toast");
+ userEvent.click(button);
+ act(() => {
+ jest.advanceTimersByTime(10000); // over 5000ms
+ });
+ expect(getByText("Loading...")).toBeTruthy();
+ jest.useRealTimers();
+ });
+ test("Loading toast can be cleared", async () => {
+ const { getByLabelText, getByText, queryByText } = render(
+
+
+
+ );
+ const button = getByText("Show loading toast");
+ userEvent.click(button);
+ const clearButton = getByLabelText("Clear toast");
+ userEvent.click(clearButton);
+ await waitFor(() => {
+ expect(queryByText("Loading...")).toBeFalsy();
+ });
+ });
+ test("Loading toast can be removed programmatically", async () => {
+ jest.useFakeTimers();
+ const { getByText, queryByText } = render(
+
+
+
+ );
+ const button = getByText("Load process");
+ userEvent.click(button);
+ await waitFor(() => {
+ expect(getByText("Loading process...")).toBeTruthy();
+ });
+ act(() => {
+ jest.advanceTimersByTime(5000);
+ });
+ expect(queryByText("Loading process...")).toBeFalsy();
+ expect(getByText("The process ended successfully.")).toBeTruthy();
+ jest.useRealTimers();
+ });
+});
diff --git a/packages/lib/src/toast/Toast.tsx b/packages/lib/src/toast/Toast.tsx
new file mode 100644
index 0000000000..bd56963947
--- /dev/null
+++ b/packages/lib/src/toast/Toast.tsx
@@ -0,0 +1,193 @@
+import { memo, 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 useTranslatedLabels from "../useTranslatedLabels";
+import { responsiveSizes } from "../common/variables";
+
+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 ContentContainer = styled.div<{ loading: ToastPropsType["loading"] }>`
+ display: flex;
+ align-items: center;
+ gap: ${CoreTokens.spacing_8};
+ overflow: hidden;
+
+ ${({ loading }) => !loading && `font-size: ${CoreTokens.type_scale_05}`};
+ > svg {
+ width: 24px;
+ height: 24px;
+ }
+`;
+
+const Message = styled.span`
+ color: ${CoreTokens.color_black};
+ font-family: ${CoreTokens.type_sans};
+ font-size: ${CoreTokens.type_scale_02};
+ font-weight: ${CoreTokens.type_semibold};
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+`;
+
+const fadeInUp = keyframes`
+ 0% {
+ transform: translateY(100%);
+ opacity: 0;
+ }
+ 100% {
+ transform: translateY(0);
+ opacity: 1;
+ }
+`;
+
+const fadeOutDown = keyframes`
+ 0% {
+ transform: translateY(0);
+ opacity: 1;
+ }
+ 100% {
+ transform: translateY(100%);
+ opacity: 0;
+ }
+`;
+
+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);
+ display: inline-flex;
+ justify-content: space-between;
+ gap: ${CoreTokens.spacing_24};
+ padding: ${CoreTokens.spacing_8} ${CoreTokens.spacing_12};
+ background-color: ${({ semantic }) => getSemantic(semantic).secondaryColor};
+ color: ${({ semantic }) => getSemantic(semantic).primaryColor};
+ animation: ${({ isClosing }) => (isClosing ? fadeOutDown : fadeInUp)} 0.3s ease forwards;
+
+ @media (max-width: ${responsiveSizes.medium}rem) {
+ max-width: 100%;
+ }
+`;
+
+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,
+ hideSemanticIcon,
+ icon,
+ loading,
+ message,
+ onClear,
+ semantic,
+}: ToastPropsType) => {
+ const [isClosing, setIsClosing] = useState(false);
+ const translatedLabels = useTranslatedLabels();
+
+ const clearClosingAnimationTimer = useTimeout(
+ () => {
+ setIsClosing(true);
+ },
+ loading ? null : duration - 300
+ );
+
+ const clearTimer = useTimeout(
+ () => {
+ onClear();
+ },
+ loading ? null : duration
+ );
+
+ return (
+
+
+
+ {message}
+
+
+ {action && (
+
+ )}
+ {
+ if (!loading) {
+ clearClosingAnimationTimer();
+ clearTimer();
+ }
+ setIsClosing(true);
+ setTimeout(() => {
+ onClear();
+ }, 300);
+ }}
+ />
+
+
+ );
+};
+
+export default memo(DxcToast);
diff --git a/packages/lib/src/toast/ToastsQueue.tsx b/packages/lib/src/toast/ToastsQueue.tsx
new file mode 100644
index 0000000000..a117bc4e5b
--- /dev/null
+++ b/packages/lib/src/toast/ToastsQueue.tsx
@@ -0,0 +1,84 @@
+import { createContext, useCallback, useEffect, useMemo, useState } from "react";
+import { createPortal } from "react-dom";
+import styled from "styled-components";
+import CoreTokens from "../common/coreTokens";
+import DxcToast from "./Toast";
+import { QueuedToast, Semantic, ToastContextType, ToastsQueuePropsType, ToastType } from "./types";
+import { responsiveSizes } from "../common/variables";
+
+export const ToastContext = createContext(null);
+
+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 ToastsQueue = styled.section`
+ box-sizing: border-box;
+ position: fixed;
+ bottom: 0;
+ right: 0;
+ z-index: 2147483647;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ gap: ${CoreTokens.spacing_8};
+ padding: ${CoreTokens.spacing_24};
+
+ @media (max-width: ${responsiveSizes.medium}rem) {
+ align-items: center;
+ width: 100%;
+ }
+`;
+
+const 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]);
+
+ const add = useCallback(
+ (toast: ToastType, semantic: Semantic) => {
+ const id = generateUniqueToastId(toasts);
+ setToasts((prevToasts) => [...prevToasts, { id, semantic, ...toast }].slice(-5));
+ return () => remove(id);
+ },
+ [duration]
+ );
+
+ const remove = useCallback((id: string) => {
+ setToasts((prevToasts) => prevToasts.filter((toast) => toast.id !== id));
+ }, []);
+
+ useEffect(() => {
+ setIsMounted(true);
+ }, []);
+
+ return (
+
+ {isMounted &&
+ createPortal(
+
+ {toasts.map((t) => (
+ {
+ remove(t.id);
+ }}
+ {...t}
+ />
+ ))}
+ ,
+ document.body
+ )}
+ {children}
+
+ );
+};
+
+export default DxcToastsQueue;
diff --git a/packages/lib/src/toast/types.ts b/packages/lib/src/toast/types.ts
new file mode 100644
index 0000000000..51d474d46d
--- /dev/null
+++ b/packages/lib/src/toast/types.ts
@@ -0,0 +1,58 @@
+type SVG = React.ReactNode & React.SVGProps;
+type Action = {
+ icon?: string | SVG;
+ label: string;
+ onClick: () => void;
+};
+
+type CommonProps = {
+ action?: Action;
+ message: string;
+};
+type DefaultToast = CommonProps & {
+ icon?: string | SVG;
+};
+type LoadingToast = CommonProps & {
+ loading: boolean;
+};
+type SemanticToast = CommonProps & {
+ hideSemanticIcon?: boolean;
+};
+type ToastType = DefaultToast | LoadingToast | SemanticToast;
+
+type Semantic = "default" | "info" | "success" | "warning";
+
+type QueuedToast = ToastType & {
+ id: string;
+ semantic: Semantic;
+};
+
+type ToastContextType = {
+ add: (toast: ToastType, semantic: Semantic) => () => void;
+};
+
+type ToastPropsType = {
+ action?: Action;
+ duration: number;
+ icon?: string | SVG;
+ loading?: boolean;
+ message: string;
+ onClear: () => void;
+ semantic: Semantic;
+ hideSemanticIcon?: boolean;
+};
+
+type ToastsQueuePropsType = { duration?: number; children: React.ReactNode };
+
+export default ToastPropsType;
+export type {
+ CommonProps,
+ DefaultToast,
+ LoadingToast,
+ QueuedToast,
+ Semantic,
+ SemanticToast,
+ ToastContextType,
+ ToastsQueuePropsType,
+ ToastType,
+};
diff --git a/packages/lib/src/toast/useToast.tsx b/packages/lib/src/toast/useToast.tsx
new file mode 100644
index 0000000000..ff1b1febae
--- /dev/null
+++ b/packages/lib/src/toast/useToast.tsx
@@ -0,0 +1,27 @@
+import { useContext } from "react";
+import { ToastContext } from "./ToastsQueue";
+import { ToastType, DefaultToast, Semantic, SemanticToast, LoadingToast } from "./types";
+
+const useToast = () => {
+ const { add } = useContext(ToastContext);
+
+ const show = (toast: T, semantic: Semantic) => add(toast, semantic);
+
+ return {
+ default: (toast: DefaultToast) => {
+ show(toast, "default");
+ },
+ info: (toast: SemanticToast) => {
+ show(toast, "info");
+ },
+ loading: (toast: Omit) => show({ ...toast, loading: true }, "info"),
+ success: (toast: SemanticToast) => {
+ show(toast, "success");
+ },
+ warning: (toast: SemanticToast) => {
+ show(toast, "warning");
+ },
+ };
+};
+
+export default useToast;
diff --git a/packages/lib/src/utils/useTimeout.tsx b/packages/lib/src/utils/useTimeout.tsx
new file mode 100644
index 0000000000..da42f6c415
--- /dev/null
+++ b/packages/lib/src/utils/useTimeout.tsx
@@ -0,0 +1,32 @@
+import { useRef, useCallback, useEffect } from "react";
+
+type UseTimeoutType = (callback: () => void, delay?: number) => () => void;
+
+/**
+ * Custom hook to handle setTimeout in a declarative way.
+ * Inspired by Dan Abramov's article: https://overreacted.io/making-setinterval-declarative-with-react-hooks/
+ *
+ * @param callback Function to be executed after the delay
+ * @param delay Time in milliseconds to wait before executing the callback
+ * @returns Function to clear the timeout
+ */
+const useTimeout: UseTimeoutType = (callback, delay) => {
+ const savedCallback = useRef<() => void>();
+ const timerRef = useRef>();
+ const clearTimerCallback = useCallback(() => clearTimeout(timerRef.current), []);
+
+ useEffect(() => {
+ savedCallback.current = callback;
+ }, [callback]);
+
+ useEffect(() => {
+ if (delay != null) {
+ timerRef.current = setTimeout(savedCallback.current, delay);
+ return clearTimerCallback;
+ }
+ }, [delay, clearTimerCallback]);
+
+ return clearTimerCallback;
+};
+
+export default useTimeout;
diff --git a/packages/lib/tsconfig.json b/packages/lib/tsconfig.json
index 00fd4ab5dc..ca7788ca2a 100644
--- a/packages/lib/tsconfig.json
+++ b/packages/lib/tsconfig.json
@@ -1,6 +1,7 @@
{
"extends": "@dxc-technology/typescript-config/react-library.json",
"compilerOptions": {
+ "jsx": "react-jsx",
"outDir": "dist",
"strict": false,
"target": "es5",