diff --git a/apps/website/pages/components/dialog/code.tsx b/apps/website/pages/components/dialog/code.tsx new file mode 100644 index 0000000000..5fb24c3fa1 --- /dev/null +++ b/apps/website/pages/components/dialog/code.tsx @@ -0,0 +1,17 @@ +import Head from "next/head"; +import type { ReactElement } from "react"; +import DialogPageLayout from "screens/components/dialog/DialogPageLayout"; +import DialogCodePage from "screens/components/dialog/code/DialogCodePage"; + +const Code = () => ( + <> + + Dialog Code — Halstack Design System + + + +); + +Code.getLayout = (page: ReactElement) => {page}; + +export default Code; diff --git a/apps/website/pages/components/dialog/index.tsx b/apps/website/pages/components/dialog/index.tsx index 43353cee91..b58ea3bdd4 100644 --- a/apps/website/pages/components/dialog/index.tsx +++ b/apps/website/pages/components/dialog/index.tsx @@ -1,7 +1,7 @@ import Head from "next/head"; import type { ReactElement } from "react"; import DialogPageLayout from "screens/components/dialog/DialogPageLayout"; -import DialogCodePage from "screens/components/dialog/code/DialogCodePage"; +import DialogOverviewPage from "screens/components/dialog/overview/DialogOverviewPage"; const Index = () => { return ( @@ -9,13 +9,11 @@ const Index = () => { Dialog — 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/dialog/specifications.tsx b/apps/website/pages/components/dialog/specifications.tsx deleted file mode 100644 index 6477cbed0a..0000000000 --- a/apps/website/pages/components/dialog/specifications.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import DialogPageLayout from "screens/components/dialog/DialogPageLayout"; -import DialogSpecsPage from "screens/components/dialog/specs/DialogSpecsPage"; - -const Specifications = () => { - return ( - <> - - Dialog Specs — Halstack Design System - - - - ); -}; - -Specifications.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Specifications; diff --git a/apps/website/pages/components/dialog/usage.tsx b/apps/website/pages/components/dialog/usage.tsx deleted file mode 100644 index ecbe9fcb60..0000000000 --- a/apps/website/pages/components/dialog/usage.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import DialogPageLayout from "screens/components/dialog/DialogPageLayout"; -import DialogUsagePage from "screens/components/dialog/usage/DialogUsagePage"; - -const Usage = () => { - return ( - <> - - Dialog Usage — Halstack Design System - - - - ); -}; - -Usage.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Usage; diff --git a/apps/website/screens/components/dialog/DialogPageLayout.tsx b/apps/website/screens/components/dialog/DialogPageLayout.tsx index ef53a736fb..9e9ded478f 100644 --- a/apps/website/screens/components/dialog/DialogPageLayout.tsx +++ b/apps/website/screens/components/dialog/DialogPageLayout.tsx @@ -6,9 +6,8 @@ import { ReactNode } from "react"; const DialogPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ - { label: "Code", path: "/components/dialog" }, - { label: "Usage", path: "/components/dialog/usage" }, - { label: "Specifications", path: "/components/dialog/specifications" }, + { label: "Overview", path: "/components/dialog" }, + { label: "Code", path: "/components/dialog/code" }, ]; return ( @@ -18,10 +17,9 @@ const DialogPageHeading = ({ children }: { children: ReactNode }) => { A modal dialog is a message box or child window that requires user interaction before returning to the - parent window. These boxes appear on top of the open parent window that is currently displayed on the - screen. + parent window. - + {children} diff --git a/apps/website/screens/components/dialog/code/DialogCodePage.tsx b/apps/website/screens/components/dialog/code/DialogCodePage.tsx index 45f4e3bf50..4d60d479d9 100644 --- a/apps/website/screens/components/dialog/code/DialogCodePage.tsx +++ b/apps/website/screens/components/dialog/code/DialogCodePage.tsx @@ -24,6 +24,23 @@ const sections = [ + + + + + children + + + + React.ReactNode + + + Area within the dialog that can be used to render custom content. We strongly encourage users to not + change the tabindex of the inner components or elements. This can lead to unpredictable + behaviour for keyboard users, affecting the order of focus and focus locking within the dialog. + + - + closable @@ -35,23 +52,23 @@ const sections = [ - onCloseClick + onBackgroundClick {"() => void"} - This function will be called when the user clicks the close button. The responsibility of hiding the + This function will be called when the user clicks on the background of the modal. The responsibility of hiding the dialog lies with the user. - - onBackgroundClick + onCloseClick {"() => void"} - This function will be called when the on the background of the modal. The responsibility of hiding the + This function will be called when the user clicks the close button. The responsibility of hiding the dialog lies with the user. - @@ -66,23 +83,6 @@ const sections = [ true - - - - - children - - - - React.ReactNode - - - Area within the dialog that can be used to render custom content. We strongly encourage users to not - change the tabindex of the inner components or elements. This can lead to unpredictable - behaviour for keyboard users, affecting the order of focus and focus locking within the dialog. - - - - tabIndex @@ -135,7 +135,7 @@ const DialogCodePage = () => { return ( - + diff --git a/apps/website/screens/components/dialog/overview/DialogOverviewPage.tsx b/apps/website/screens/components/dialog/overview/DialogOverviewPage.tsx new file mode 100644 index 0000000000..ec25f5966e --- /dev/null +++ b/apps/website/screens/components/dialog/overview/DialogOverviewPage.tsx @@ -0,0 +1,151 @@ +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 content from "./images/dialog_content.png"; +import overlay from "./images/dialog_overlay.png"; +import anatomy from "./images/dialog_anatomy.png"; + +const sections = [ + { + title: "Introduction", + content: ( + + The dialog component is a modal window that{" "} + captures user attention for critical interactions, such as confirmations, alerts, or form + inputs. It appears above the main content and requires user action before continuing. To maintain usability, + dialogs should be concise, focused on a single task, and provide clear actions like confirmation or dismissal. + + ), + }, + { + title: "Anatomy", + content: ( + <> + Dialog's anatomy + + + Title: displays the main heading of the dialog, providing users with a clear and immediate + understanding of its purpose + + + Container: the structural wrapper that holds all dialog elements, ensuring proper + alignment, spacing, and responsiveness. + + + Close action: an optional button, usually represented by an “X” icon, allowing users to + dismiss the dialog without completing an action. + + + Content: the main area where relevant information, messages, or interactive elements (e.g., + forms) are displayed. + + + Actions: a set of buttons at the bottom of the dialog that guide users toward completing or + canceling the intended action. + + + + + * Please note that while these elements are not included by default in the component's configuration, they + are the expected components in a dialog. + + + + ), + }, + { + title: "Placing content in a dialog", + content: ( + <> +
+ Example of a form inside of a Halstack dialog +
+ + Any content (Halstack components or custom) can be placed inside the dialog component. Dialog tasks should be + direct and easy to complete. However, to ensure that the component is used as intended and to prevent it from + becoming unusable, please consider the following aspects: + + + + Avoid placing complex or large amounts of data, as it will increase the cognitive load and + users will struggle when processing information. + + + As much as possible, avoid scrolling in a dialog, as it disrupts focus and usability, + especially for modal interactions that require immediate attention. It can also make critical actions harder + to access, forcing users to scroll to find the key information or buttons. Instead, keep dialogs concise or + consider alternative patterns to display the information. + + + + ), + }, + { + title: "Overlay in dialogs", + content: ( + <> + + The overlay element helps focus the user's attention on the dialog by creating a + semi-transparent layer between the main application and the modal content. This visual separation reduces + distractions, ensures the dialog stands out, and reinforces the need for user action before returning to the + underlying interface. Additionally, it enhances accessibility by preventing unintended interactions with + background elements. + +
+ Example of the overlay usage in a dialog +
+ + ), + }, + { + title: "Best practices", + content: ( + <> + + + Use dialogs for critical interactions: reserve dialogs for important decisions that require + user confirmation or input. + + + Ensure clarity: titles and messages should be direct, with clear descriptions of the + action's consequences. + + + Provide easy dismissal: always include a way to close the dialog, whether through a cancel + button or a close icon. + + + Maintain focus: keep the user's attention within the dialog by managing focus and + preventing background interactions. + + + Provide clear actions: use distinct primary and secondary buttons for confirmation and + cancellation. + + + Keep it focused: dialogs should be concise and address a single task to avoid overwhelming + users. + + + Avoid scrolling: keep content brief to prevent excessive scrolling; use alternative + patterns for complex interactions. + + + + ), + }, +]; + +const DialogOverviewPage = () => ( + + + + + + +); + +export default DialogOverviewPage; diff --git a/apps/website/screens/components/dialog/overview/images/dialog_anatomy.png b/apps/website/screens/components/dialog/overview/images/dialog_anatomy.png new file mode 100644 index 0000000000..1a8247ba7f Binary files /dev/null and b/apps/website/screens/components/dialog/overview/images/dialog_anatomy.png differ diff --git a/apps/website/screens/components/dialog/overview/images/dialog_content.png b/apps/website/screens/components/dialog/overview/images/dialog_content.png new file mode 100644 index 0000000000..8d16455454 Binary files /dev/null and b/apps/website/screens/components/dialog/overview/images/dialog_content.png differ diff --git a/apps/website/screens/components/dialog/overview/images/dialog_overlay.png b/apps/website/screens/components/dialog/overview/images/dialog_overlay.png new file mode 100644 index 0000000000..66f5c47143 Binary files /dev/null and b/apps/website/screens/components/dialog/overview/images/dialog_overlay.png differ diff --git a/apps/website/screens/components/dialog/specs/DialogSpecsPage.tsx b/apps/website/screens/components/dialog/specs/DialogSpecsPage.tsx deleted file mode 100644 index 0ea5c60f56..0000000000 --- a/apps/website/screens/components/dialog/specs/DialogSpecsPage.tsx +++ /dev/null @@ -1,347 +0,0 @@ -import { DxcLink, DxcBulletedList, DxcFlex, DxcTable, DxcParagraph } from "@dxc-technology/halstack-react"; -import Image from "@/common/Image"; -import Code from "@/common/Code"; -import DocFooter from "@/common/DocFooter"; -import Figure from "@/common/Figure"; -import QuickNavContainer from "@/common/QuickNavContainer"; -import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; -import dialogAnatomyImage from "./images/dialog_anatomy.png"; -import dialogSpecsImage from "./images/dialog_specs.png"; - -const sections = [ - { - title: "Specifications", - content: ( -
- Dialog design specifications -
- ), - }, - { - title: "Anatomy", - content: ( - <> - Dialog anatomy - - Dialog container - Title - Close action - Dialog actions - Content - - - ), - }, - { - title: "Design tokens", - subSections: [ - { - title: "Color", - content: ( - - - - Component token - Element - Core token - Value - - - - - - overlayColor - - Overlay - - color-grey-800-a - - #000000b3 - - - - backgroundColor - - Container - - color-white - - #ffffff - - - - boxShadowColor - - Container shadow - - color-grey-300-a - - #00000033 - - - - closeIconColor - - Icon close - - color-black - - #000000 - - - - closeIconBackgroundColor - - Icon close - - color-transparent - - transparent - - - - ), - }, - { - title: "Typography", - content: ( - - - - Property - Element - Core token - Value - - - - - - font-family - - Title - - font-family-sans - - 'Open Sans, sans-serif' - - - - font-size - - Title - - font-scale-05 - - 1.5rem / 24px - - - - font-weight - - Title - - font-weight-semibold - - 600 - - - - ), - }, - { - title: "Border", - content: ( - <> - - - - Property - Element - Core token - Value - - - - - - border-width - - Container - - border-width-0 - - 0rem / 0px - - - - border-style - - Container - - border-style-none - - none - - - - border-radius - - Container - - border-radius-medium - - 0.25rem / 4px - - - - - ), - }, - { - title: "Spacing", - content: ( - - - - Property - Element - Core token - Value - - - - - - margin-right - - Title icon - - spacing-12 - - 0.75rem / 12px - - - - margin-bottom - - Title - - spacing-24 - - 1.5rem / 24px - - - - ), - }, - { - title: "Size", - content: ( - - - - Property - Element - Core token - Value - - - - - - min-width - - Container - - - 696px - - - - max-width - - Container - - - 80% - - - - width - - Close action - - - 24px - - - - height - - Close action - - - 24px - - - - ), - }, - ], - }, - { - title: "Accessibility", - subSections: [ - { - title: "WCAG", - content: ( - <> - - - Understanding WCAG 2.2 -{" "} - - 2.1.2 No Keyboard Trap - - - - Understanding WCAG 2.2 -{" "} - - 2.4.3 Focus Order - - - - - ), - }, - { - title: "WAI-ARIA", - content: ( - - - WAI-ARIA Authoring practices 1.2 -{" "} - - 3.9 Dialog (Modal) - - - - WAI-ARIA Authoring practices 1.2 -{" "} - - Modal Dialog Example - - - - ), - }, - ], - }, -]; - -const DialogSpecsPage = () => { - return ( - - - - - - - ); -}; - -export default DialogSpecsPage; diff --git a/apps/website/screens/components/dialog/specs/images/dialog_anatomy.png b/apps/website/screens/components/dialog/specs/images/dialog_anatomy.png deleted file mode 100644 index 36b882bece..0000000000 Binary files a/apps/website/screens/components/dialog/specs/images/dialog_anatomy.png and /dev/null differ diff --git a/apps/website/screens/components/dialog/specs/images/dialog_specs.png b/apps/website/screens/components/dialog/specs/images/dialog_specs.png deleted file mode 100644 index 10b0bbc67f..0000000000 Binary files a/apps/website/screens/components/dialog/specs/images/dialog_specs.png and /dev/null differ diff --git a/apps/website/screens/components/dialog/usage/DialogUsagePage.tsx b/apps/website/screens/components/dialog/usage/DialogUsagePage.tsx deleted file mode 100644 index 451c1b38d2..0000000000 --- a/apps/website/screens/components/dialog/usage/DialogUsagePage.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { DxcBulletedList, DxcFlex, DxcParagraph } from "@dxc-technology/halstack-react"; -import QuickNavContainer from "@/common/QuickNavContainer"; -import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; -import DocFooter from "@/common/DocFooter"; -import Figure from "@/common/Figure"; -import Image from "@/common/Image"; -import dialogContent from "./images/dialog_content.png"; -import dialogOverlay from "./images/dialog_overlay.png"; - -const sections = [ - { - title: "Usage", - content: ( - - - The dialog always should have a title to introduce the actions or information that will get displayed on the - screen. - - - Can contain a descriptive text or a phrase related to the action that triggered the dialog. - - - Can have some combinations of actions, like buttons to accept/cancel the action. There can be one, two or more - buttons. - - - If the dialog is not including a cancel action, provide a way to close it. - - - Modal dialog boxes should overlay only a portion of the underlying page to keep the user oriented within the - workflow. - - - In a dialog, the focus should remain within the modal box until the user completes the required action (if - any) or closes it. In addition, and as a general rule, the focus should appear on the first focusable child - when the dialog is opened. - - - ), - }, - - { - title: "Content", - content: ( - <> -
- Example of a dialog using Halstack components as content -
- - Any content (Halstack components or custom) can be placed inside the dialog component. Dialog tasks should be - direct and easy to complete. - - - Do not use to display complex or large amounts of data. - Do not recreate a full app or page in a dialog. - Try always to avoid scrolling. - - - ), - }, - { - title: "Overlay", - content: ( - <> -
- Example of the overlay usage -
- - The overlay element makes possible to get the user attention into the dialog creating a layer between the - actual application and the modal information shown in the user interface. - - - ), - }, -]; - -const DialogUsagePage = () => { - return ( - - - - - - - ); -}; - -export default DialogUsagePage; diff --git a/apps/website/screens/components/dialog/usage/images/dialog_content.png b/apps/website/screens/components/dialog/usage/images/dialog_content.png deleted file mode 100644 index 70d24206a0..0000000000 Binary files a/apps/website/screens/components/dialog/usage/images/dialog_content.png and /dev/null differ diff --git a/apps/website/screens/components/dialog/usage/images/dialog_overlay.png b/apps/website/screens/components/dialog/usage/images/dialog_overlay.png deleted file mode 100644 index c2cf8075a5..0000000000 Binary files a/apps/website/screens/components/dialog/usage/images/dialog_overlay.png and /dev/null differ diff --git a/packages/lib/src/dialog/Dialog.stories.tsx b/packages/lib/src/dialog/Dialog.stories.tsx index 6854d5935b..7c86b7b279 100644 --- a/packages/lib/src/dialog/Dialog.stories.tsx +++ b/packages/lib/src/dialog/Dialog.stories.tsx @@ -5,7 +5,6 @@ import Title from "../../.storybook/components/Title"; import DxcAlert from "../alert/Alert"; import DxcButton from "../button/Button"; import DxcFlex from "../flex/Flex"; -import { HalstackProvider } from "../HalstackContext"; import DxcHeading from "../heading/Heading"; import DxcInset from "../inset/Inset"; import DxcParagraph from "../paragraph/Paragraph"; @@ -33,14 +32,6 @@ const customViewports = { }, }; -const opinionatedTheme = { - dialog: { - baseColor: "#ffffff", - closeIconColor: "#000000", - overlayColor: "#000000b3", - }, -}; - const Dialog = () => ( @@ -65,32 +56,6 @@ const Dialog = () => ( </ExampleContainer> ); -const DialogOpinionated = () => ( - <ExampleContainer expanded={true}> - <Title title="Default dialog" theme="light" level={4} /> - <HalstackProvider theme={opinionatedTheme}> - <DxcDialog> - <DxcInset space="1.5rem"> - <DxcFlex direction="column" gap="1rem"> - <DxcHeading level={4} text="Example title" /> - <DxcParagraph> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi egestas luctus porttitor. Donec massa - magna, placerat sit amet felis eget, venenatis fringilla ipsum. Lorem ipsum dolor sit amet, consectetur - adipiscing elit. Donec congue laoreet orci, nec elementum dolor consequat quis. Curabitur rhoncus justo - sed dapibus tincidunt. Vestibulum cursus ut risus sit amet congue. Nunc luctus, urna ullamcorper facilisis - Jia Le, risus eros aliquam erat, ut efficitur ante neque id odio. Nam orci leo, dignissim sit amet dolor - ut, congue gravida enim. Donec rhoncus aliquam nisl, ac cursus enim bibendum vitae. Nunc sit amet elit - ornare, malesuada urna eu, fringilla mauris. Vivamus bibendum turpis est, id elementum purus euismod sit - amet. Etiam sit amet maximus augue. Vivamus erat sapien, ultricies fringilla tellus id, condimentum - blandit justo. Praesent quis nunc dignissim, pharetra neque molestie, molestie lectus. - </DxcParagraph> - </DxcFlex> - </DxcInset> - </DxcDialog> - </HalstackProvider> - </ExampleContainer> -); - const DialogInput = () => ( <ExampleContainer expanded={true}> <Title title="Dialog with inputs" theme="light" level={4} /> @@ -351,10 +316,6 @@ export const DefaultDialog: Story = { render: Dialog, }; -export const DefaultDialogOpinionated: Story = { - render: DialogOpinionated, -}; - export const DialogWithInputs: Story = { render: DialogInput, }; diff --git a/packages/lib/src/dialog/Dialog.tsx b/packages/lib/src/dialog/Dialog.tsx index b5e4af27e2..db85a04bd1 100644 --- a/packages/lib/src/dialog/Dialog.tsx +++ b/packages/lib/src/dialog/Dialog.tsx @@ -3,7 +3,7 @@ import { createPortal } from "react-dom"; import styled, { createGlobalStyle, ThemeProvider } from "styled-components"; import { responsiveSizes } from "../common/variables"; import DxcActionIcon from "../action-icon/ActionIcon"; -import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; +import { HalstackLanguageContext } from "../HalstackContext"; import FocusLock from "../utils/FocusLock"; import DialogPropsType from "./types"; @@ -27,7 +27,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 Dialog = styled.div<{ closable: DialogPropsType["closable"] }>` @@ -36,10 +36,9 @@ const Dialog = styled.div<{ closable: DialogPropsType["closable"] }>` max-width: 80%; min-width: 696px; border-radius: 4px; - background-color: ${(props) => props.theme.backgroundColor}; + background-color: var(--color-bg-neutral-lightest); ${(props) => props.closable && "min-height: 72px;"} - box-shadow: ${(props) => - `${props.theme.boxShadowOffsetX} ${props.theme.boxShadowOffsetY} ${props.theme.boxShadowBlur} ${props.theme.boxShadowColor}`}; + box-shadow: var(--shadow-low-x-position) var(--shadow-low-y-position) var(--shadow-low-blur) var(--shadow-low-spread) var(--shadow-dark); z-index: 2147483647; @media (max-width: ${responsiveSizes.medium}rem) { @@ -62,7 +61,6 @@ const DxcDialog = ({ overlay = true, tabIndex = 0, }: DialogPropsType): JSX.Element => { - const colorsTheme = useContext(HalstackContext); const translatedLabels = useContext(HalstackLanguageContext); useEffect(() => { @@ -80,7 +78,7 @@ const DxcDialog = ({ }, [onCloseClick]); return ( - <ThemeProvider theme={colorsTheme.dialog}> + <> <BodyStyle /> {createPortal( <DialogContainer> @@ -89,28 +87,21 @@ const DxcDialog = ({ <FocusLock> {children} {closable && ( - <ThemeProvider - theme={{ - actionBackgroundColor: colorsTheme.dialog.closeIconBackgroundColor, - actionIconColor: colorsTheme.dialog.closeIconColor, - }} - > - <CloseIconActionContainer> - <DxcActionIcon - icon="close" - onClick={onCloseClick} - tabIndex={tabIndex} - title={translatedLabels.dialog.closeIconAriaLabel} - /> - </CloseIconActionContainer> - </ThemeProvider> + <CloseIconActionContainer> + <DxcActionIcon + icon="close" + onClick={onCloseClick} + tabIndex={tabIndex} + title={translatedLabels.dialog.closeIconAriaLabel} + /> + </CloseIconActionContainer> )} </FocusLock> </Dialog> </DialogContainer>, document.body )} - </ThemeProvider> + </> ); };