diff --git a/apps/website/pages/components/alert/code.tsx b/apps/website/pages/components/alert/code.tsx new file mode 100644 index 0000000000..d0f75530b5 --- /dev/null +++ b/apps/website/pages/components/alert/code.tsx @@ -0,0 +1,19 @@ +import Head from "next/head"; +import type { ReactElement } from "react"; +import AlertPageLayout from "screens/components/alert/AlertPageLayout"; +import AlertCodePage from "screens/components/alert/code/AlertCodePage"; + +const Code = () => { + return ( + <> + + Alert Code — Halstack Design System + + + + ); +}; + +Code.getLayout = (page: ReactElement) => {page}; + +export default Code; diff --git a/apps/website/pages/components/alert/index.tsx b/apps/website/pages/components/alert/index.tsx index 8ababce366..a4781141d7 100644 --- a/apps/website/pages/components/alert/index.tsx +++ b/apps/website/pages/components/alert/index.tsx @@ -1,7 +1,7 @@ import Head from "next/head"; import type { ReactElement } from "react"; import AlertPageLayout from "screens/components/alert/AlertPageLayout"; -import AlertCodePage from "screens/components/alert/code/AlertCodePage"; +import AlertOverviewPage from "screens/components/alert/overview/AlertOverviewPage"; const Index = () => { return ( @@ -9,13 +9,11 @@ const Index = () => { Alert — 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/alert/specifications.tsx b/apps/website/pages/components/alert/specifications.tsx deleted file mode 100644 index afbc1da802..0000000000 --- a/apps/website/pages/components/alert/specifications.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import AlertPageLayout from "screens/components/alert/AlertPageLayout"; -import AlertSpecsPage from "screens/components/alert/specs/AlertSpecsPage"; - -const Specifications = () => { - return ( - <> - - Alert Specs — Halstack Design System - - - - ); -}; - -Specifications.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Specifications; diff --git a/apps/website/pages/components/alert/usage.tsx b/apps/website/pages/components/alert/usage.tsx deleted file mode 100644 index 2f28321624..0000000000 --- a/apps/website/pages/components/alert/usage.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import AlertPageLayout from "screens/components/alert/AlertPageLayout"; -import AlertUsagePage from "screens/components/alert/usage/AlertUsagePage"; - -const Usage = () => { - return ( - <> - - Alert Usage — Halstack Design System - - - - ); -}; - -Usage.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Usage; diff --git a/apps/website/screens/components/alert/AlertPageLayout.tsx b/apps/website/screens/components/alert/AlertPageLayout.tsx index bdaf827c25..12bfe4ac93 100644 --- a/apps/website/screens/components/alert/AlertPageLayout.tsx +++ b/apps/website/screens/components/alert/AlertPageLayout.tsx @@ -6,9 +6,8 @@ import { ReactNode } from "react"; const AlertPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ - { label: "Code", path: "/components/alert" }, - { label: "Usage", path: "/components/alert/usage" }, - { label: "Specifications", path: "/components/alert/specifications" }, + { label: "Overview", path: "/components/alert" }, + { label: "Code", path: "/components/alert/code" }, ]; return ( @@ -17,11 +16,9 @@ const AlertPageHeading = ({ children }: { children: ReactNode }) => { - Alert messages are meant to provide contextual feedback about important changes in the interface. They are - used to convey essential information to the user, ensuring that critical updates or warnings are immediately - noticeable. + Alert messages are meant to provide contextual feedback about important changes in the interface. - + {children} diff --git a/apps/website/screens/components/alert/code/AlertCodePage.tsx b/apps/website/screens/components/alert/code/AlertCodePage.tsx index 4fcb17c975..a7a26fb7f8 100644 --- a/apps/website/screens/components/alert/code/AlertCodePage.tsx +++ b/apps/website/screens/components/alert/code/AlertCodePage.tsx @@ -160,15 +160,13 @@ const sections = [ }, ]; -const AlertCodePage = () => { - return ( - - - - - - - ); -}; +const AlertCodePage = () => ( + + + + + + +); export default AlertCodePage; diff --git a/apps/website/screens/components/alert/usage/AlertUsagePage.tsx b/apps/website/screens/components/alert/overview/AlertOverviewPage.tsx similarity index 59% rename from apps/website/screens/components/alert/usage/AlertUsagePage.tsx rename to apps/website/screens/components/alert/overview/AlertOverviewPage.tsx index 764f6a3d37..700c296c96 100644 --- a/apps/website/screens/components/alert/usage/AlertUsagePage.tsx +++ b/apps/website/screens/components/alert/overview/AlertOverviewPage.tsx @@ -11,116 +11,168 @@ import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; import DocFooter from "@/common/DocFooter"; import HeaderDescriptionCell from "@/common/HeaderDescriptionCell"; import Image from "@/common/Image"; -import banner from "./images/alert_banner.png"; -import modal from "./images/alert_modal.png"; -import inline from "./images/alert_inline.png"; +import anatomy from "./images/alert_anatomy.png"; +import banner from "./examples/banner"; +import dialog from "./examples/dialog"; +import inline from "./examples/inline"; import Link from "next/link"; import Code from "@/common/Code"; +import Example from "@/common/example/Example"; const sections = [ { - title: "Usage", + title: "Introduction", + content: ( + <> + + Halstack's Alert component is designed to provide clear and accessible feedback to users, ensuring important + messages stand out within an interface. It supports different variants—info, success, warning, and + error—allowing teams to communicate various levels of urgency effectively. With options for dismissibility and + customizable content, it adapts to different use cases while maintaining consistency. Its structured design + enhances readability and ensures a non-intrusive yet noticeable presence. + + + ), + }, + { + title: "Anatomy", + content: ( + <> + Alert's anatomy + + + Container: the structural wrapper that holds all elements of the alert, defining its size, + background, and layout while ensuring proper visibility within the interface. + + + Icon: a visual indicator that reinforces the alert's purpose (e.g., info, success, warning, + error) and helps users quickly identify the type of message + + + Title: a short, bolded text that provides a concise summary of the alert's content, making + it easy for users to grasp its importance at a glance. + + + Description: the detailed text within the alert that provides context or additional + information about the message, ensuring clarity for the user. + + + Divider: a subtle separator that visually organizes the alert's content, enhancing + readability and distinguishing the pagination element from the closable action. + + + Pagination: a navigation element that allows users to browse multiple alerts within the + same container, useful for managing sequential or grouped notifications. + + + Actions: interactive buttons placed within the alert that enable users to take relevant + actions in response to the message, such as dismissing, acknowledging, or navigating to further details. + + + + ), + }, + { + title: "Key interactions and features", content: ( <> The key characteristics of our alert component are the following: - Visibility: alerts should be positioned in a place where it can attract the user's eye + Visibility: alerts should be positioned in a place where it can attract the user's eye without too much effort, as it will convey critical messages about the system or important features. - Concise: alert messages should use clear and straight-to-the-point language that conveys + Concise: alert messages should use clear and straight-to-the-point language that conveys the message without unnecessary information, avoiding ambiguity or irrelevant details that could distract or confuse the user. - Relevance: the content displayed on the alert must be directly relevant to the users task + Relevance: the content displayed on the alert must be directly relevant to the users task or system state. - Actionability: the component provides clear and straightforward actions for the user to + Actionability: the component provides clear and straightforward actions for the user to take in response to it, such as retrying, seeing details or dismissing the alert. - Persistence: alerts should remain visible long enough for users to read and understand + Persistence: alerts should remain visible long enough for users to read and understand them, but not so long as to become an annoyance. Allow users to dismiss alerts once they have acknowledged them. - Sequential display: only one alert must be visible at a time. If multiple alerts are + Sequential display: only one alert must be visible at a time. If multiple alerts are triggered, subsequent alerts wait until the current alert is dismissed before appearing. This behavior prevents alert stacking and ensures users address each alert individually. ), - subSections: [ - { - title: "Semantic alerts", - content: ( - <> - - Depending on the message that the alert is showcasing, we can find four different semantic meanings of the - component: - - - - - Semantic - Description - - - - - - - Information - - - - Informational messages are used exclusively to assist the user with directional or explanatory text - about a complex or seldom used process. - - - - - - Warning - - - - Alert or warning messages should be displayed when there is a potential obstacle in completing a - process as intended. - - - - - - Error - - - - Error messages convey a critical system problem that requires user and/or technical intervention to - correct. - - - - - - Success - - - - Success messages should be used to assure user that a system calculation or data submission was - completed correctly. - - - - - - ), - }, - ], + }, + { + title: "Semantic modes", + content: ( + <> + + Depending on the message that the alert is showcasing, we can find four different semantic meanings of the + component: + + + + + Type + Description + + + + + + + Information + + + + Informational messages are used exclusively to assist the user with directional or explanatory text + about a complex or seldom used process. + + + + + + Warning + + + + Alert or warning messages should be displayed when there is a potential obstacle in completing a process + as intended. + + + + + + Error + + + + Error messages convey a critical system problem that requires user and/or technical intervention to + correct. + + + + + + Success + + + + Success messages should be used to assure user that a system calculation or data submission was + completed correctly. + + + + + + ), }, { title: "Variants", @@ -135,15 +187,15 @@ const sections = [ ), subSections: [ { - title: "Banner alert", + title: "Alert banner", content: ( <> - Displayed always below the header of the site, the banner alert is used for critical system or + Displayed always bellow the header of the site, the Alert banner is used for critical system or application messages. These alerts are actionable, meaning the user can interact with the message to resolve the issue directly from the alert itself. However, closing the message is not considered an action and is mandatory, as the message remains visible and cannot be dismissed until addressed. Additionally, if - there are multiple critical system messages, the banner alert allows pagination between them, ensuring + there are multiple critical system messages, the Alert banner allows pagination between them, ensuring that users can manage and resolve all critical issues systematically. @@ -151,59 +203,59 @@ const sections = [ - System disruption notification: informing users about a critical system disruption that + System disruption notification: informing users about a critical system disruption that requires immediate attention, with an option to view more details or contact support. - Security breach alert: warning users of a potential security breach, with actions to + Security breach alert: warning users of a potential security breach, with actions to change passwords or review account activity. - Critical update required: notifying users that a crucial software update is available + Critical update required: notifying users that a crucial software update is available and needs to be installed to continue using the application safely. - Payment failure notification: informing users that a recent payment has failed and + Payment failure notification: informing users that a recent payment has failed and providing options to retry the payment or update billing information. - Banner alert + ), }, { - title: "Modal alert", + title: "Alert dialog", content: ( <> - The modal alert displays important information that demands immediate user attention. It is presented with - an overlay that blocks the rest of the interface, ensuring that the user addresses the alert before - proceeding. Similar to other variants, the modal alert allows up to two actions, excluding the mandatory + The Alert dialog displays important information that demands immediate user attention. It is presented + with an overlay that blocks the rest of the interface, ensuring that the user addresses the alert before + proceeding. Similar to other variants, the Dialog Alert allows up to two actions, excluding the mandatory close action. This variant ensures that critical information is not overlooked by requiring the user to - interact with the modal to continue using the application. + interact with the dialog to continue using the application. Use cases: - Data loss warning: informing users that unsaved changes will be lost if they proceed + Data loss warning: informing users that unsaved changes will be lost if they proceed with an action, with options to save changes or discard them. - Permission request: requesting user permission for access to sensitive data or device + Permission request: requesting user permission for access to sensitive data or devide features, with actions to grant or deny permission. - Policy renewal reminder: notifying users that a client's insurance policy is about to + Policy renewal reminder: notifying users that a client's insurance policy is about to expire, with options to renew the policy or contact support for assistance. - Important policy update: alerting users to important changes in the terms of service or + Important policy update: alerting users to important changes in the terms of service or privacy policy, requiring them to acknowledge the update before continuing. - Modal alert + ), }, @@ -212,40 +264,39 @@ const sections = [ content: ( <> - The inline alert is displayed in a non-modal manner and is typically used within other components (such as - within a card, table, or container). These alerts provide contextual feedback related to the component - they are embedded in, informing the user about the status of an action. Like other alert variants, Inline - alerts can include up to two actions, and the close action is optional. They also support pagination for - scenarios where multiple related alerts need to be managed. + The Inline alert is displayed in a non-modal manner and is typically used within other components (such as + within a card, table, or container). These alerts Alert provide contextual feedback related to the + component they are embedded in, informing the user about the status of an action. Like other alert + variants, Inline alerts can include up to two actions, and the close action is optional. They also support + pagination for scenarios where multiple related alerts need to be managed. Use cases: - Failed API: displaying an error message within a table indicating that there was an + Failed API: displaying an error message within a table indicating that there was an error fetching data from the API. - Pending documents to be uploaded: informing the user that some documents are pending to + Pending documents to be uploaded: informing the user that some documents are pending to be uploaded, with action to cancel the upload. - Unsaved policy changes: warning users that the changes applied to a certain policy will + Unsaved policy changes: warning users that the changes applied to a certain policy will be lost if they proceed to the next page, with actions to save changes or continue. - Document upload reminder: alerting users within a policy details page that required + Document upload reminder: alerting users within a policy details page that required documents are missing, with actions to upload the documents or get assistance. - Inline alert + ), }, ], }, - { title: "Best practices", subSections: [ @@ -269,10 +320,10 @@ const sections = [ Alerts cannot be displayed in a stacked manner. Make sure to prioritize alerts that compete at the same - level so the user will assess the most important one first. + level so the user will assess the most important one first - Only use alerts for critical messages that require immediate attention from the user. Overusing the + Only use alerts for critical messages that requires immediate attention from the user. Overusing the component can desensitize users to their importance and increase the cognitive load. @@ -291,7 +342,7 @@ const sections = [ Visit our{" "} - + Best practices for button labels {" "} section from our Button component to tailor the best label for each button to make labels consistent and @@ -324,28 +375,28 @@ const sections = [ ), }, { - title: "Best practices of each variant", + title: "Variant-specific best practices", subSections: [ { - title: "Banner alert", + title: "Alert banner", content: ( - Position and visibility: banner alerts must always be placed on top of the interface, + Position and visibility: alert banners must always be placed on top of the interface, right below the header of the application. This is particularly important for system-wide alerts that impact the user's ability to interact with the application. - Navigation: if multiple critical alerts exist, enable the option to paginate them so + Navigation: if multiple critical alerts exist, enable the option to paginate them so they can assess each message one by one, in priority order. - Persistence: keep the banner alert visible until the issue is resolved. Users should + Persistence: keep the Alert banner visible until the issue is resolved. Users should not be able to dismiss it without taking action. This ensures that critical issues are addressed and not overlooked. The persistent nature of the banner reinforces the importance of the alert. - Clarity: use the description of the alert to briefly explain the issue, but avoid + Clarity: use the description of the alert to briefly explain the issue, but avoid overloading the user with unnecessary descriptions. If further details are needed to fully understand or assess the alert, provide a button to see the details on a separate screen. @@ -353,23 +404,23 @@ const sections = [ ), }, { - title: "Modal alert", + title: "Alert dialog", content: ( <> - Immediate action: use modal alert for issues that need the user’s immediate + Immediate action: use Alert dialog for issues that need the user's immediate attention and action. Ensure they cannot proceed without addressing the alert. This prevents users from ignoring critical messages and helps in timely resolution of issues. - Clear actions: if actions are needed (besides the close action), ensure these - actions are accurate to resolve the issue, such as “Save changes” or “View details”. Actions should + Clear actions: if actions are needed (besides the close action), ensure these + actions are accurate to resolve the issue, such as "Save changes" or "View details". Actions should be easy to understand and immediately executable, guiding the user through the necessary steps to resolve the alert. - Accurate focus: each modal alert must focus on a single important message to avoid + Accurate focus: each dialog alert must focus on a single important message to avoid overwhelming the user. Avoid combining multiple issues in one alert, as it can cause confusion and reduce the effectiveness of the alert. @@ -383,21 +434,21 @@ const sections = [ <> - Placement: place inline alerts near the relevant content or component to provide + Placement: place Inline alerts near the relevant content or component to provide immediate and contextual feedback. This helps users associate the alert with the specific action or data that it relates to, making it more intuitive to understand and act upon. - Non-intrusive: ensure inline alerts do not disrupt the user’s workflow but are + Non-intrusive: ensure Inline alerts do not disrupt the user's workflow but are noticeable enough to provide the necessary feedback. - Dismissive alerts: allow users to close the inline alert if it is not critical to + Dismissive alerts: allow users to close the Inline alert if it is not critical to keep it visible at all times. This gives users control over their interface and prevents frustration from persistent alerts. - Multiple alerts handling: if multiple inline alerts are necessary, allow users to + Multiple alerts handling: if multiple Inline Alerts are necessary, allow users to navigate through them. This can be achieved through the navigation option built within the component. @@ -411,15 +462,15 @@ const sections = [ }, ]; -const AlertUsagePage = () => { +const AlertOverviewPage = () => { return ( - + ); }; -export default AlertUsagePage; +export default AlertOverviewPage; diff --git a/apps/website/screens/components/alert/overview/examples/banner.ts b/apps/website/screens/components/alert/overview/examples/banner.ts new file mode 100644 index 0000000000..0ef1699d30 --- /dev/null +++ b/apps/website/screens/components/alert/overview/examples/banner.ts @@ -0,0 +1,67 @@ +import { DxcAlert, DxcFlex, DxcInset } from "@dxc-technology/halstack-react"; + +const code = `() => { + const messagesSuccess = [ + { text: "Your document has been saved successfully." }, + { text: "Second text." } + ]; + const messagesInformation = [ + { text: "Your document has been auto-saved." }, + { text: "Second text." } + ]; + const messagesWarning = [ + { text: "You have unsaved changes in your document." }, + { text: "Second text." } + ]; + const messagesError = [ + { text: "We encountered an issue while saving your file." }, + { text: "Second text." } + ]; + + return ( + + + {} }} + secondaryAction={{ label: "Secondary action", onClick: () => {} }} + semantic="success" + title="Success" + /> + {} }} + secondaryAction={{ label: "Secondary action", onClick: () => {} }} + semantic="info" + title="Information" + /> + {} }} + secondaryAction={{ label: "Secondary action", onClick: () => {} }} + semantic="warning" + title="Warning" + /> + {} }} + secondaryAction={{ label: "Secondary action", onClick: () => {} }} + semantic="error" + title="Error" + /> + + + ); +}`; + +const scope = { + DxcAlert, + DxcInset, + DxcFlex, +}; + +export default { code, scope }; diff --git a/apps/website/screens/components/alert/overview/examples/dialog.ts b/apps/website/screens/components/alert/overview/examples/dialog.ts new file mode 100644 index 0000000000..4e2b4c54cb --- /dev/null +++ b/apps/website/screens/components/alert/overview/examples/dialog.ts @@ -0,0 +1,88 @@ +import { DxcAlert, DxcButton, DxcFlex, DxcInset } from "@dxc-technology/halstack-react"; +import { useState } from "react"; + +const code = `() => { + const [loadDialog, setLoadDialog] = useState(0); + + const messagesSuccess = [ + { text: "Your document has been saved successfully.", onClose : () => {setLoadDialog(0)} }, + { text: "You can continue editing your document or navigate to other sections.", onClose : () => {setLoadDialog(0)} } + ]; + const messagesInformation = [ + { text: "Your document has been auto-saved.", onClose : () => {setLoadDialog(0)} }, + { text: "You can continue working without worry, as all changes are being saved automatically. This feature ensure that your progress is preserved even if your forget to save manually. Feel free to review the auto-save settings in your account preferences.", onClose : () => {setLoadDialog(0)} } + ]; + const messagesWarning = [ + { text: "You have unsaved changes in your document.", onClose : () => {setLoadDialog(0)} }, + { text: "If you navigate away from this page, you will lose any changes you have made. Please save your work to avoid losing any progress. Consider reviewing your changes before saving.", onClose : () => {setLoadDialog(0)} } + ]; + const messagesError = [ + { text: "We encountered an issue while saving your file.", onClose : () => {setLoadDialog(0)} }, + { text: "You have unsaved changes in your document. If you navigate away from this page, you will lose any changes you have made. Please save your work to avoid losing any progress. Consider reviewing your changes before saving.", onClose : () => {setLoadDialog(0)} } + ]; + + return ( + + + setLoadDialog(1)} /> + setLoadDialog(2)} /> + setLoadDialog(3)} /> + setLoadDialog(4)} /> + + {loadDialog !== 0 && ( + + {loadDialog === 1 && ( + {} }} + secondaryAction={{ label: "Secondary action", onClick: () => {} }} + semantic="success" + title="Success" + /> + )} + {loadDialog === 2 && ( + {} }} + secondaryAction={{ label: "Secondary action", onClick: () => {} }} + semantic="info" + title="Information" + /> + )} + {loadDialog === 3 && ( + {} }} + secondaryAction={{ label: "Secondary action", onClick: () => {} }} + semantic="warning" + title="Warning" + /> + )} + {loadDialog === 4 && ( + {} }} + secondaryAction={{ label: "Secondary action", onClick: () => {} }} + semantic="error" + title="Error" + /> + )} + + )} + + ); +}`; + +const scope = { + DxcAlert, + DxcInset, + DxcFlex, + DxcButton, + useState, +}; + +export default { code, scope }; diff --git a/apps/website/screens/components/alert/overview/examples/inline.ts b/apps/website/screens/components/alert/overview/examples/inline.ts new file mode 100644 index 0000000000..3ba0c20b5b --- /dev/null +++ b/apps/website/screens/components/alert/overview/examples/inline.ts @@ -0,0 +1,63 @@ +import { DxcAlert, DxcFlex, DxcInset } from "@dxc-technology/halstack-react"; + +const code = `() => { + const messagesSuccess = [ + { text: "You can continue editing your document or navigate to other sections. If yo need to do further changes, feel free to do so at any time. Remember your changes are saved automatically." }, + { text: "Second text" } + ]; + const messagesInformation = [ + { text: "Your document has been auto-saved. You can continue working without worry, as all changes are being saved automatically. This feature ensure that your progress is preserved even if your forget to save manually. Feel free to review the auto-save settings in your account preferences." }, + { text: "Second text" } + ]; + const messagesWarning = [ + { text: "You have unsaved changes in your document. If you navigate away from this page, you will lose any changes you have made. Please save your work to avoid losing any progress. Consider reviewing your changes before saving." }, + { text: "Second text" } + ]; + const messagesError = [ + { text: "We encountered an issue while saving your file. You have unsaved changes in your document. If you navigate away from this page, you will lose any changes you have made. Please save your work to avoid losing any progress. Consider reviewing your changes before saving." }, + { text: "Second text" } + ]; + + return ( + + + {} }} + secondaryAction={{ label: "Secondary action", onClick: () => {} }} + semantic="success" + title="Success" + /> + {} }} + secondaryAction={{ label: "Secondary action", onClick: () => {} }} + semantic="info" + title="Information" + /> + {} }} + secondaryAction={{ label: "Secondary action", onClick: () => {} }} + semantic="warning" + title="Warning" + /> + {} }} + secondaryAction={{ label: "Secondary action", onClick: () => {} }} + semantic="error" + title="Error" + /> + + + ); +}`; + +const scope = { + DxcAlert, + DxcInset, + DxcFlex, +}; + +export default { code, scope }; diff --git a/apps/website/screens/components/alert/overview/images/alert_anatomy.png b/apps/website/screens/components/alert/overview/images/alert_anatomy.png new file mode 100644 index 0000000000..3bc0d1a41f Binary files /dev/null and b/apps/website/screens/components/alert/overview/images/alert_anatomy.png differ diff --git a/apps/website/screens/components/alert/specs/AlertSpecsPage.tsx b/apps/website/screens/components/alert/specs/AlertSpecsPage.tsx deleted file mode 100644 index 6a087054e2..0000000000 --- a/apps/website/screens/components/alert/specs/AlertSpecsPage.tsx +++ /dev/null @@ -1,332 +0,0 @@ -import Image from "@/common/Image"; -import { DxcLink, DxcBulletedList, DxcFlex, DxcTable } from "@dxc-technology/halstack-react"; -import QuickNavContainer from "@/common/QuickNavContainer"; -import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; -import Code from "@/common/Code"; -import DocFooter from "@/common/DocFooter"; -import Figure from "@/common/Figure"; -import AlertAnatomyImage from "./images/alert_anatomy.png"; -import BannerAlertSpecsImage from "./images/alert_banner_specs.png"; -import InlineAlertSpecsImage from "./images/alert_inline_specs.png"; -import ModalAlertSpecsImage from "./images/alert_modal_specs.png"; - -const sections = [ - { - title: "Specifications", - content: ( - <> -
- Banner alert design specifications -
-
- Modal alert design specifications -
-
- Inline alert design specifications -
- - ), - }, - - { - title: "Anatomy", - content: ( - <> - Alert anatomy - - Container - Icon - Title - Pagination - Divider - Close action icon - Buttons - Description - - - ), - }, - { - title: "Design tokens", - subSections: [ - { - title: "Color", - content: ( - - - - Component token - Element - Core token - Value - - - - - - errorBackgroundColor - - Container (error) - - color-red-100 - - #ffe6e9 - - - - errorIconColor - - Icon (error) - - color-red-700 - - #d0011b - - - - fontColor - - Alert - - color-grey-900 - - #333333 - - - - infoBackgroundColor - - Container (info) - - color-blue-100 - - #e6f4ff - - - - infoIconColor - - Icon (info) - - color-blue-700 - - #0086e6 - - - - modalBackgroundColor - - Container (modal) - - color-white - - #ffffff - - - - overlayColor - - Overlay - - color-grey-800-a - - #000000b3 - - - - successBackgroundColor - - Container (success) - - color-green-100 - - #eafaef - - - - successIconColor - - Icon (success) - - color-green-700 - - #24a148 - - - - warningBackgroundColor - - Container (warning) - - color-yellow-100 - - #fef9e6 - - - - warningIconColor - - Icon (warning) - - color-yellow-700 - - #c59f07 - - - - ), - }, - { - title: "Iconography", - content: ( - - - - Component token - Element - Core token - Value - - - - - - iconSize - - Icon - - - 24px - - - - ), - }, - { - title: "Typography", - content: ( - - - - Component token - Element - Core token - Value - - - - - - fontFamily - - Alert - - font-family-sans - - 'Open Sans', sans-serif - - - - fontSize - - Alert - - font-scale-02 - - 0.875rem / 14px - - - - fontStyle - - Alert - - font-style-normal - - normal - - - - fontWeight - - Alert - - font-weight-regular - - 400 - - - - modalTitleFontSize - - Title - - font-scale-05 - - 1.5rem / 24px - - - - modalTitleFontWeight - - Title - - font-weight-bold - - 700 - - - - ), - }, - ], - }, - { - title: "Accessibility", - subSections: [ - { - title: "WAI-ARIA", - content: ( - - - - Alert role - - - - ), - subSections: [ - { - title: "Authoring Practices Guide (APG)", - content: ( - - - - Alert pattern - - - - - Alert and message dialogs pattern - - - - ), - }, - ], - }, - ], - }, -]; - -const AlertSpecsPage = () => { - return ( - - - - - - - ); -}; - -export default AlertSpecsPage; diff --git a/apps/website/screens/components/alert/specs/images/alert_anatomy.png b/apps/website/screens/components/alert/specs/images/alert_anatomy.png deleted file mode 100644 index b968cc28aa..0000000000 Binary files a/apps/website/screens/components/alert/specs/images/alert_anatomy.png and /dev/null differ diff --git a/apps/website/screens/components/alert/specs/images/alert_banner_specs.png b/apps/website/screens/components/alert/specs/images/alert_banner_specs.png deleted file mode 100644 index e984aa81bc..0000000000 Binary files a/apps/website/screens/components/alert/specs/images/alert_banner_specs.png and /dev/null differ diff --git a/apps/website/screens/components/alert/specs/images/alert_inline_specs.png b/apps/website/screens/components/alert/specs/images/alert_inline_specs.png deleted file mode 100644 index 2578a6a0fb..0000000000 Binary files a/apps/website/screens/components/alert/specs/images/alert_inline_specs.png and /dev/null differ diff --git a/apps/website/screens/components/alert/specs/images/alert_modal_specs.png b/apps/website/screens/components/alert/specs/images/alert_modal_specs.png deleted file mode 100644 index f49f039ebe..0000000000 Binary files a/apps/website/screens/components/alert/specs/images/alert_modal_specs.png and /dev/null differ diff --git a/apps/website/screens/components/alert/usage/images/alert_banner.png b/apps/website/screens/components/alert/usage/images/alert_banner.png deleted file mode 100644 index a9857ef33e..0000000000 Binary files a/apps/website/screens/components/alert/usage/images/alert_banner.png and /dev/null differ diff --git a/apps/website/screens/components/alert/usage/images/alert_inline.png b/apps/website/screens/components/alert/usage/images/alert_inline.png deleted file mode 100644 index 5c55e10da8..0000000000 Binary files a/apps/website/screens/components/alert/usage/images/alert_inline.png and /dev/null differ diff --git a/apps/website/screens/components/alert/usage/images/alert_modal.png b/apps/website/screens/components/alert/usage/images/alert_modal.png deleted file mode 100644 index eb6fb07c92..0000000000 Binary files a/apps/website/screens/components/alert/usage/images/alert_modal.png and /dev/null differ diff --git a/packages/lib/src/action-icon/ActionIcon.tsx b/packages/lib/src/action-icon/ActionIcon.tsx index f243c98acd..ed985e13ee 100644 --- a/packages/lib/src/action-icon/ActionIcon.tsx +++ b/packages/lib/src/action-icon/ActionIcon.tsx @@ -1,7 +1,6 @@ import { forwardRef } from "react"; import ActionIconPropsTypes, { RefType } from "./types"; import styled from "styled-components"; -import CoreTokens from "../common/coreTokens"; import DxcIcon from "../icon/Icon"; import { Tooltip } from "../tooltip/Tooltip"; @@ -11,43 +10,27 @@ const ActionIcon = styled.button` align-items: center; justify-content: center; flex-shrink: 0; - border-radius: 2px; + border-radius: var(--border-radius-xs); + height: var(--height-s); width: 24px; - height: 24px; ${(props) => (props.disabled ? `cursor: not-allowed;` : `cursor: pointer;`)} - box-shadow: 0 0 0 2px transparent; - background-color: ${(props) => - props.disabled - ? (props.theme.disabledActionBackgroundColor ?? CoreTokens.color_transparent) - : (props.theme.actionBackgroundColor ?? CoreTokens.color_transparent)}; - color: ${(props) => - props.disabled - ? (props.theme.disabledActionIconColor ?? CoreTokens.color_grey_500) - : (props.theme.actionIconColor ?? CoreTokens.color_grey_900)}; + color: ${(props) => (props.disabled ? "var(--color-fg-neutral-medium)" : "var(--color-fg-neutral-dark)")}; ${(props) => !props.disabled && ` &:focus, &:focus-visible { - outline: none; - box-shadow: 0 0 0 2px ${props.theme.focusActionBorderColor ?? CoreTokens.color_blue_600}; - color: ${props.theme.focusActionIconColor ?? CoreTokens.color_grey_900}; + outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium); } &:hover { - background-color: ${props.theme.hoverActionBackgroundColor ?? CoreTokens.color_grey_100}; - color: ${props.theme.hoverActionIconColor ?? CoreTokens.color_grey_900}; - } - &:active { - background-color: ${props.theme.activeActionBackgroundColor ?? CoreTokens.color_grey_300}; - color: ${props.theme.activeActionIconColor ?? CoreTokens.color_grey_900}; + background-color: var(--color-bg-alpha-light); } `} - - font-size: 16px; + font-size: var(--height-xxs); > svg { + height: var(--height-xxs); width: 16px; - height: 16px; } `; diff --git a/packages/lib/src/alert/Actions.tsx b/packages/lib/src/alert/Actions.tsx new file mode 100644 index 0000000000..49dd3ef74c --- /dev/null +++ b/packages/lib/src/alert/Actions.tsx @@ -0,0 +1,38 @@ +import { memo } from "react"; +import DxcButton from "../button/Button"; +import DxcFlex from "../flex/Flex"; +import AlertPropsType from "./types"; + +const Actions = memo( + ({ + mode, + primaryAction, + secondaryAction, + semantic, + }: Pick) => + (primaryAction != null || secondaryAction != null) && ( + + {secondaryAction?.onClick && ( + + )} + {primaryAction?.onClick && ( + + )} + + ) +); + +export default Actions; diff --git a/packages/lib/src/alert/Alert.stories.tsx b/packages/lib/src/alert/Alert.stories.tsx index b67c366eec..0e582fc398 100644 --- a/packages/lib/src/alert/Alert.stories.tsx +++ b/packages/lib/src/alert/Alert.stories.tsx @@ -309,7 +309,6 @@ const AlertSuccess = () => ( message={message} primaryAction={{ label: "Primary action", onClick: () => {} }} secondaryAction={{ label: "Secondary action", onClick: () => {} }} - closable={false} /> ); diff --git a/packages/lib/src/alert/Alert.tsx b/packages/lib/src/alert/Alert.tsx index e2ed56bc3f..11506c3ad4 100644 --- a/packages/lib/src/alert/Alert.tsx +++ b/packages/lib/src/alert/Alert.tsx @@ -1,14 +1,13 @@ -import styled, { css, ThemeProvider } from "styled-components"; -import { useState, memo, useId, useEffect, useCallback, useContext } from "react"; +import styled, { css } from "styled-components"; +import { useState, useId, useEffect, useCallback, useContext } from "react"; import AlertPropsType from "./types"; import DxcIcon from "../icon/Icon"; -import DxcButton from "../button/Button"; import DxcDivider from "../divider/Divider"; import DxcActionIcon from "../action-icon/ActionIcon"; import DxcFlex from "../flex/Flex"; +import Actions from "./Actions"; import ModalAlertWrapper from "./ModalAlertWrapper"; -import CoreTokens from "../common/coreTokens"; -import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; +import { HalstackLanguageContext } from "../HalstackContext"; const AlertContainer = styled.div<{ semantic: AlertPropsType["semantic"]; @@ -17,21 +16,21 @@ const AlertContainer = styled.div<{ box-sizing: border-box; display: flex; flex-direction: column; - gap: ${CoreTokens.spacing_8}; - ${(props) => - (props.mode === "modal" || props.mode === "inline") && `border-radius: ${CoreTokens.border_radius_medium};`}; + gap: ${(props) => (props.mode === "banner" ? "var(--spacing-gap-m)" : "var(--spacing-gap-s)")}; + ${(props) => (props.mode === "modal" || props.mode === "inline") && `border-radius: var(--border-radius-s);`}; padding: ${(props) => props.mode === "modal" - ? CoreTokens.spacing_24 + ? "var(--spacing-padding-l)" : props.mode === "inline" - ? CoreTokens.spacing_12 - : `${CoreTokens.spacing_8} ${CoreTokens.spacing_12}`}; + ? "var(--spacing-padding-s)" + : "var(--spacing-padding-xs) var(--spacing-padding-s)"}; + background-color: ${(props) => - (props.mode === "modal" && props.theme.modalBackgroundColor) || - (props.semantic === "info" && props.theme.infoBackgroundColor) || - (props.semantic === "success" && props.theme.successBackgroundColor) || - (props.semantic === "warning" && props.theme.warningBackgroundColor) || - (props.semantic === "error" && props.theme.errorBackgroundColor)}; + (props.mode === "modal" && "var(--color-bg-neutral-lightest)") || + (props.semantic === "info" && "var(--color-bg-secondary-lighter)") || + (props.semantic === "success" && "var(--color-bg-success-lighter)") || + (props.semantic === "warning" && "var(--color-bg-warning-lighter)") || + (props.semantic === "error" && "var(--color-bg-error-lighter)")}; overflow: hidden; `; @@ -39,28 +38,26 @@ const TitleContainer = styled.div<{ mode: AlertPropsType["mode"]; semantic: Aler flex-grow: 1; display: flex; align-items: center; - gap: ${CoreTokens.spacing_8}; + gap: var(--spacing-gap-s); color: ${(props) => - (props.semantic === "info" && props.theme.infoIconColor) || - (props.semantic === "success" && props.theme.successIconColor) || - (props.semantic === "warning" && props.theme.warningIconColor) || - (props.semantic === "error" && props.theme.errorIconColor)}; - font-size: ${(props) => props.theme.iconSize}; + (props.semantic === "info" && "var(--color-fg-secondary-medium)") || + (props.semantic === "success" && "var(--color-fg-success-medium)") || + (props.semantic === "warning" && "var(--color-fg-warning-medium)") || + (props.semantic === "error" && "var(--color-fg-error-medium)")}; + font-size: var(--height-s); overflow: hidden; `; const typographyStyles = css` - font-family: ${(props) => props.theme.fontFamily}; - font-size: ${(props) => props.theme.fontSize}; - font-style: ${(props) => props.theme.fontStyle}; - font-weight: ${(props) => props.theme.fontWeight}; - color: ${(props) => props.theme.fontColor}; + font-family: var(--typography-font-family); + color: var(--color-fg-neutral-dark); + font-weight: var(--typography-helper-text-regular); `; const Title = styled.span<{ mode: AlertPropsType["mode"] }>` ${typographyStyles} - ${(props) => props.mode === "modal" && `font-size: ${props.theme.modalTitleFontSize}`}; - font-weight: ${(props) => props.theme.modalTitleFontWeight}; + font-size: ${(props) => (props.mode === "modal" ? "var(--typography-title-xl)" : "var(--typography-title-s)")}; + font-weight: var(--typography-title-bold); `; const Message = styled.div<{ mode: AlertPropsType["mode"] }>` @@ -68,49 +65,19 @@ const Message = styled.div<{ mode: AlertPropsType["mode"] }>` white-space: ${(props) => props.mode === "banner" && "nowrap"}; text-overflow: ${(props) => props.mode === "banner" && "ellipsis"}; overflow: ${(props) => props.mode === "banner" && "hidden"}; - + font-size: var(--typography-helper-text-m); > strong { - font-weight: ${(props) => props.theme.modalTitleFontWeight}; + font-weight: var(--typography-title-bold); + font-size: var(--typography-title-s); } `; const NavigationText = styled.span` ${typographyStyles} white-space: nowrap; + font-size: var(--typography-helper-text-s); `; -const Actions = memo( - ({ - mode, - primaryAction, - secondaryAction, - semantic, - }: Pick) => - (primaryAction != null || secondaryAction != null) && ( - - {secondaryAction?.onClick && ( - - )} - {primaryAction?.onClick && ( - - )} - - ) -); - const getIcon = (semantic: AlertPropsType["semantic"]) => { switch (semantic) { case "info": @@ -137,7 +104,6 @@ export default function DxcAlert({ const [currentIndex, setCurrentIndex] = useState(0); const id = useId(); - const colorsTheme = useContext(HalstackContext); const translatedLabels = useContext(HalstackLanguageContext); const handleNextOnClick = () => { @@ -146,102 +112,91 @@ export default function DxcAlert({ const handlePrevOnClick = useCallback(() => { setCurrentIndex((prevIndex) => (prevIndex > 0 ? prevIndex - 1 : prevIndex)); }, []); - const handleOnClose = () => { + + const handleOnClose = useCallback(() => { messages[currentIndex]?.onClose?.(); if (mode !== "modal") { setMessages((prevMessages) => prevMessages.filter((_, index) => index !== currentIndex)); } - }; + }, [messages, currentIndex, mode]); useEffect(() => { if (currentIndex === messages.length) handlePrevOnClick(); }, [currentIndex, messages, handlePrevOnClick]); return ( - - - - - - {getIcon(semantic)} - {mode === "banner" ? ( - - {title} - {messages.length > 0 && <> - {messages[currentIndex]?.text}} - - ) : ( - - {title} - - )} - - {mode === "banner" && ( - + + + + + {getIcon(semantic)} + {mode === "banner" ? ( + + {title} + {messages.length > 0 && <> - {messages[currentIndex]?.text}} + + ) : ( + + {title} + )} - {messages.length > 1 && ( - - - - {currentIndex + 1} of {messages.length} - - - - )} - {closable && ( - - {mode !== "modal" && } - 1 - ? translatedLabels.alert.closeMessageActionTitle - : translatedLabels.alert.closeAlertActionTitle - } - onClick={handleOnClose} - /> - - )} - - {mode === "modal" && } - {mode !== "banner" && ( - <> - {messages.length > 0 && ( - - {messages[currentIndex]?.text} - - )} - + {mode === "banner" && ( + + )} + {messages.length > 1 && ( + + + + {currentIndex + 1} of {messages.length} + + - + )} - - - + {closable && ( + + {mode !== "modal" && } + 1 + ? translatedLabels.alert.closeMessageActionTitle + : translatedLabels.alert.closeAlertActionTitle + } + onClick={handleOnClose} + /> + + )} + + {mode === "modal" && } + {mode !== "banner" && ( + <> + {messages.length > 0 && ( + + {messages[currentIndex]?.text} + + )} + + + )} + + ); } diff --git a/packages/lib/src/alert/ModalAlertWrapper.tsx b/packages/lib/src/alert/ModalAlertWrapper.tsx index a52d77c888..c2da5abef1 100644 --- a/packages/lib/src/alert/ModalAlertWrapper.tsx +++ b/packages/lib/src/alert/ModalAlertWrapper.tsx @@ -3,6 +3,7 @@ import styled, { createGlobalStyle } from "styled-components"; import { responsiveSizes } from "../common/variables"; import FocusLock from "../utils/FocusLock"; import { ModalAlertWrapperProps } from "./types"; +import { useEffect } from "react"; const BodyStyle = createGlobalStyle` body { @@ -24,7 +25,7 @@ const Overlay = styled.div` position: fixed; inset: 0; height: 100%; - background-color: ${(props) => props.theme.overlayColor}; + background-color: var(--color-bg-alpha-medium); `; const ModalAlertContainer = styled.div` @@ -40,8 +41,23 @@ const ModalAlertContainer = styled.div` } `; -const ModalAlertWrapper = ({ condition, onClose, children }: ModalAlertWrapperProps) => - condition ? ( +const ModalAlertWrapper = ({ condition, onClose, children }: ModalAlertWrapperProps) => { + useEffect(() => { + if (condition) { + const handleModalAlertKeydown = (event: KeyboardEvent) => { + if (event.key === "Escape") { + event.preventDefault(); + onClose?.(); + } + }; + document.addEventListener("keydown", handleModalAlertKeydown); + return () => { + document.removeEventListener("keydown", handleModalAlertKeydown); + }; + } + }, [condition, onClose]); + + return condition ? ( <> {createPortal( @@ -57,5 +73,6 @@ const ModalAlertWrapper = ({ condition, onClose, children }: ModalAlertWrapperPr ) : ( children ); +}; export default ModalAlertWrapper; diff --git a/packages/lib/src/alert/types.ts b/packages/lib/src/alert/types.ts index 5985269862..9052f009ba 100644 --- a/packages/lib/src/alert/types.ts +++ b/packages/lib/src/alert/types.ts @@ -22,8 +22,8 @@ type Message = { */ onClose?: () => void; /** - * The content of the message. The only Halstack component allowed within the text of an alert is the Link component, - * and it should be used exclusively to direct users to additional resources or relevant pages. + * The content of the message. The only Halstack component allowed within the text of an alert is the Link component, + * and it should be used exclusively to direct users to additional resources or relevant pages. * No other components are permitted within the content of an alert. */ text: ReactNode; @@ -31,8 +31,8 @@ type Message = { type CommonProps = { /** - * If true, the alert will have a close button that will remove the message from the alert, - * only in banner and inline modes. The rest of the functionality will depend + * If true, the alert will have a close button that will remove the message from the alert, + * only in banner and inline modes. The rest of the functionality will depend * on the onClose event of each message (e.g. closing the modal alert). */ closable?: boolean; @@ -57,11 +57,11 @@ type CommonProps = { type ModeSpecificProps = | { /** - * List of messages to be displayed. Each message has a close action that will, - * apart from remove from the alert the current message, call the onClose if it is defined. + * List of messages to be displayed. Each message has a close action that will, + * apart from remove from the alert the current message, call the onClose if it is defined. * If the message is an array, the alert will show a navigation bar to move between the messages. - * The modal mode only allows one message per alert. In this case, the message must be an object - * and the presence of the onClose attribute is mandatory, since the management of the closing + * The modal mode only allows one message per alert. In this case, the message must be an object + * and the presence of the onClose attribute is mandatory, since the management of the closing * of the alert relies completely on the user. */ message?: Message | Message[]; @@ -72,11 +72,11 @@ type ModeSpecificProps = } | { /** - * List of messages to be displayed. Each message has a close action that will, - * apart from remove from the alert the current message, call the onClose if it is defined. + * List of messages to be displayed. Each message has a close action that will, + * apart from remove from the alert the current message, call the onClose if it is defined. * If the message is an array, the alert will show a navigation bar to move between the messages. - * The modal mode only allows one message per alert. In this case, the message must be an object - * and the presence of the onClose attribute is mandatory, since the management of the closing + * The modal mode only allows one message per alert. In this case, the message must be an object + * and the presence of the onClose attribute is mandatory, since the management of the closing * of the alert relies completely on the user. */ message: Required;