diff --git a/apps/website/pages/components/breadcrumbs/code.tsx b/apps/website/pages/components/breadcrumbs/code.tsx new file mode 100644 index 0000000000..d2a465bb4f --- /dev/null +++ b/apps/website/pages/components/breadcrumbs/code.tsx @@ -0,0 +1,17 @@ +import Head from "next/head"; +import type { ReactElement } from "react"; +import BreadcrumbsPageLayout from "screens/components/breadcrumbs/BreadcrumbsPageLayout"; +import BreadcrumbsCodePage from "screens/components/breadcrumbs/code/BreadcrumbsCodePage"; + +const Code = () => ( + <> + + Breadcrumbs code — Halstack Design System + + + +); + +Code.getLayout = (page: ReactElement) => {page}; + +export default Code; diff --git a/apps/website/pages/components/breadcrumbs/index.tsx b/apps/website/pages/components/breadcrumbs/index.tsx index 0816cbb238..1dd988b222 100644 --- a/apps/website/pages/components/breadcrumbs/index.tsx +++ b/apps/website/pages/components/breadcrumbs/index.tsx @@ -1,21 +1,17 @@ import Head from "next/head"; import type { ReactElement } from "react"; -import BreadcrumbsCodePage from "screens/components/breadcrumbs/code/BreadcrumbsCodePage"; import BreadcrumbsPageLayout from "screens/components/breadcrumbs/BreadcrumbsPageLayout"; +import BreadcrumbsOverviewPage from "screens/components/breadcrumbs/overview/BreadcrumbsOverviewPage"; -const Usage = () => { - return ( - <> - - Breadcrumbs — Halstack Design System - - - - ); -}; +const Index = () => ( + <> + + Breadcrumbs — Halstack Design System + + + +); -Usage.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; +Index.getLayout = (page: ReactElement) => {page}; -export default Usage; +export default Index; diff --git a/apps/website/pages/components/breadcrumbs/specifications.tsx b/apps/website/pages/components/breadcrumbs/specifications.tsx deleted file mode 100644 index abe0373d74..0000000000 --- a/apps/website/pages/components/breadcrumbs/specifications.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import BreadcrumbsPageLayout from "screens/components/breadcrumbs/BreadcrumbsPageLayout"; -import BreadcrumbsSpecsPage from "screens/components/breadcrumbs/specs/BreadcrumbsSpecsPage"; - -const Specifications = () => { - return ( - <> - - Breadcrumbs Specs — Halstack Design System - - - - ); -}; - -Specifications.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Specifications; diff --git a/apps/website/pages/components/breadcrumbs/usage.tsx b/apps/website/pages/components/breadcrumbs/usage.tsx deleted file mode 100644 index e5353edc79..0000000000 --- a/apps/website/pages/components/breadcrumbs/usage.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import BreadcrumbsPageLayout from "screens/components/breadcrumbs/BreadcrumbsPageLayout"; -import BreadcrumbsUsagePage from "screens/components/breadcrumbs/usage/BreadcrumbsUsagePage"; - -const Usage = () => { - return ( - <> - - Breadcrumbs Usage — Halstack Design System - - - - ); -}; - -Usage.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Usage; diff --git a/apps/website/screens/components/breadcrumbs/BreadcrumbsPageLayout.tsx b/apps/website/screens/components/breadcrumbs/BreadcrumbsPageLayout.tsx index a96d5c06b4..16a8142011 100644 --- a/apps/website/screens/components/breadcrumbs/BreadcrumbsPageLayout.tsx +++ b/apps/website/screens/components/breadcrumbs/BreadcrumbsPageLayout.tsx @@ -6,9 +6,8 @@ import { ReactNode } from "react"; const BreadcrumbsPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ - { label: "Code", path: "/components/breadcrumbs" }, - { label: "Usage", path: "/components/breadcrumbs/usage" }, - { label: "Specifications", path: "/components/breadcrumbs/specifications" }, + { label: "Overview", path: "/components/breadcrumbs" }, + { label: "Code", path: "/components/breadcrumbs/code" }, ]; return ( @@ -20,7 +19,7 @@ const BreadcrumbsPageHeading = ({ children }: { children: ReactNode }) => { A breadcrumbs trail is a secondary form of navigation that allows users to keep track and maintain awareness of their location as they move through a hierarchically structured web application. - + {children} diff --git a/apps/website/screens/components/breadcrumbs/code/BreadcrumbsCodePage.tsx b/apps/website/screens/components/breadcrumbs/code/BreadcrumbsCodePage.tsx index 0fe08b8970..5753b01673 100644 --- a/apps/website/screens/components/breadcrumbs/code/BreadcrumbsCodePage.tsx +++ b/apps/website/screens/components/breadcrumbs/code/BreadcrumbsCodePage.tsx @@ -124,15 +124,13 @@ const sections = [ }, ]; -const BreadcrumbsCodePage = () => { - return ( - - - - - - - ); -}; +const BreadcrumbsCodePage = () => ( + + + + + + +); export default BreadcrumbsCodePage; diff --git a/apps/website/screens/components/breadcrumbs/overview/BreadcrumbsOverviewPage.tsx b/apps/website/screens/components/breadcrumbs/overview/BreadcrumbsOverviewPage.tsx new file mode 100644 index 0000000000..5a3589e08a --- /dev/null +++ b/apps/website/screens/components/breadcrumbs/overview/BreadcrumbsOverviewPage.tsx @@ -0,0 +1,118 @@ +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 Example from "@/common/example/Example"; +import collapsed from "./examples/collapsed"; +import noRoot from "./examples/noRoot"; +import Image from "@/common/Image"; +import anatomy from "./images/breadcrumbs_anatomy.png"; + +const sections = [ + { + title: "Introduction", + content: ( + + Breadcrumbs are a navigational component used in user interfaces to improve the user experience by providing a{" "} + clear path to previous steps or navigational levels. They typically display the user's current + location within the hierarchical structure of an application, allowing for easy navigation and improved + orientation. + + ), + }, + { + title: "Anatomy", + content: ( + <> + Breadcrumbs's anatomy + + + Unvisited item: these are links that represent the navigational path taken by the user but + are not the current page + + + Divider: a visual element that complements the label, providing additional meaning or + enhancing recognition. + + + Visited item: displays the textual action that the button is going to carry out. + + + + ), + }, + { + title: "Collapsed breadcrumbs", + content: ( + <> + + When there are more than four items in the breadcrumbs, the component will{" "} + collapse the items in a dropdown menu to avoid overloading the interface. Users can click on + the dropdown to view the hidden items and navigate to the desired level. + + + Although this can be configurable, we highly encourage our users to stick with the collapsing at five or more + items. This was not a random decision, we carefully selected it to ensure the component does not overload the + interface and remains an effective navigational aid. + + + + Depending on the amount of available space, the collapsed breadcrumbs can be synthesized more{" "} + by removing the root element and displaying only the collapsed dropdown and the current page. + + + Be mindful of your user's cognitive load and collapse breadcrumbs appropriately. + + ), + }, + { + title: "Best practices", + content: ( + + + Use breadcrumbs to support secondary navigation in hierarchical structures, helping users + understand where they are and how they got there. + + + Always include the full path from the homepage or root section to the current page to provide meaningful + context. + + + Display only one breadcrumb trail per page to avoid confusion and maintain a clear navigation + hierarchy. + + + Make all breadcrumb items navigable except for the last one, which should reflect the current page and remain + non-interactive. + + + Keep labels concise and consistent with the destination page titles to avoid ambiguity. + + + Place breadcrumbs at the top of the page, just below the main navigation or header, to follow + common UI patterns and user expectations. + + + Avoid using breadcrumbs as a replacement for primary navigation—they are meant to complement it, not replace + menus or sidebars. + + + When truncating breadcrumbs due to space constraints,{" "} + prioritize showing the first and last items while collapsing the middle items with a clear + overflow mechanism. + + + ), + }, +]; + +const BreadcrumbsOverviewPage = () => ( + + + + + + +); + +export default BreadcrumbsOverviewPage; diff --git a/apps/website/screens/components/breadcrumbs/usage/examples/collapsed.tsx b/apps/website/screens/components/breadcrumbs/overview/examples/collapsed.tsx similarity index 100% rename from apps/website/screens/components/breadcrumbs/usage/examples/collapsed.tsx rename to apps/website/screens/components/breadcrumbs/overview/examples/collapsed.tsx diff --git a/apps/website/screens/components/breadcrumbs/usage/examples/noRoot.tsx b/apps/website/screens/components/breadcrumbs/overview/examples/noRoot.tsx similarity index 100% rename from apps/website/screens/components/breadcrumbs/usage/examples/noRoot.tsx rename to apps/website/screens/components/breadcrumbs/overview/examples/noRoot.tsx diff --git a/apps/website/screens/components/breadcrumbs/overview/images/breadcrumbs_anatomy.png b/apps/website/screens/components/breadcrumbs/overview/images/breadcrumbs_anatomy.png new file mode 100644 index 0000000000..8c0d1c9e08 Binary files /dev/null and b/apps/website/screens/components/breadcrumbs/overview/images/breadcrumbs_anatomy.png differ diff --git a/apps/website/screens/components/breadcrumbs/specs/BreadcrumbsSpecsPage.tsx b/apps/website/screens/components/breadcrumbs/specs/BreadcrumbsSpecsPage.tsx deleted file mode 100644 index ac1a756391..0000000000 --- a/apps/website/screens/components/breadcrumbs/specs/BreadcrumbsSpecsPage.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { DxcBulletedList, DxcFlex, DxcParagraph } from "@dxc-technology/halstack-react"; -import Image from "@/common/Image"; -import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; -import DocFooter from "@/common/DocFooter"; -import QuickNavContainer from "@/common/QuickNavContainer"; -import Figure from "@/common/Figure"; -import specs from "./images/breadcrumbs_specs.png"; -import anatomy from "./images/breadcrumbs_anatomy.png"; - -const sections = [ - { - title: "Specifications", - content: ( -
- Breadcrumbs design specifications -
- ), - }, - { - title: "Anatomy", - content: ( - <> - Breadcrumbs anatomy - - Breadcrumbs item - Divider - - - ), - }, - { - title: "Design tokens", - content: This component currently has no design tokens., - }, -]; - -const BreadcrumbsSpecsPage = () => { - return ( - - - - - - - ); -}; - -export default BreadcrumbsSpecsPage; diff --git a/apps/website/screens/components/breadcrumbs/specs/images/breadcrumbs_anatomy.png b/apps/website/screens/components/breadcrumbs/specs/images/breadcrumbs_anatomy.png deleted file mode 100644 index 05f7782559..0000000000 Binary files a/apps/website/screens/components/breadcrumbs/specs/images/breadcrumbs_anatomy.png and /dev/null differ diff --git a/apps/website/screens/components/breadcrumbs/specs/images/breadcrumbs_specs.png b/apps/website/screens/components/breadcrumbs/specs/images/breadcrumbs_specs.png deleted file mode 100644 index 3696d820c9..0000000000 Binary files a/apps/website/screens/components/breadcrumbs/specs/images/breadcrumbs_specs.png and /dev/null differ diff --git a/apps/website/screens/components/breadcrumbs/usage/BreadcrumbsUsagePage.tsx b/apps/website/screens/components/breadcrumbs/usage/BreadcrumbsUsagePage.tsx deleted file mode 100644 index aefbb75888..0000000000 --- a/apps/website/screens/components/breadcrumbs/usage/BreadcrumbsUsagePage.tsx +++ /dev/null @@ -1,97 +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 Example from "@/common/example/Example"; -import collapsed from "./examples/collapsed"; -import noRoot from "./examples/noRoot"; - -const sections = [ - { - title: "Usage", - content: ( - - Breadcrumbs are a navigational component used in user interfaces to improve the user experience by providing a - clear path to previous steps or navigational levels. They typically display the user's current location within - the hierarchical structure of an application, allowing for easy navigation and improved orientation. - - ), - subSections: [ - { - title: "Do's", - content: ( - - - Use breadcrumbs when there are more than two navigational levels within the same hierarchy to help users - easily navigate and understand their position. - - - Ensure each label of the Breadcrumbs items is informative yet brief, providing users with a clear - understanding of where each link will take them without overwhelming them with too much text. - - - Always position breadcrumbs in the top left corner of the interface. - - - ), - }, - { - title: "Don'ts", - content: ( - - - Avoid overloading the component. Including too many items in the breadcrumbs increases the cognitive load - of the interface and can confuse users. We recommend using the collapsed version of the component when - there are more than four items. - - - Don't use breadcrumbs to replace primary navigation or links, as they are intended to serve as a - supplementary navigation aid. - - - Refrain from using breadcrumbs to represent the sequence of steps to complete an action. Remember that it - is a navigation component through levels with an established hierarchy. - - - ), - }, - ], - }, - { - title: "Collapsed Breadcrumbs", - content: ( - <> - - When there are more than four items in the breadcrumbs, the component will collapse the items in a dropdown - menu to avoid overloading the interface. Users can click on the dropdown to view the hidden items and navigate - to the desired level. - - - Although this can be configurable, we highly encourage our users to stick with the collapsing at five or more - items. This was not a random decision, we carefully selected it to ensure the component does not overload the - interface and remains an effective navigational aid. - - - - Depending on the amount of available space, the collapsed breadcrumbs can be synthesized more by removing the - root element and displaying only the collapsed dropdown and the current page. - - - Be mindful of your user's cognitive load and collapse breadcrumbs appropriately. - - ), - }, -]; - -const BreadcrumbsUsagePage = () => { - return ( - - - - - - - ); -}; - -export default BreadcrumbsUsagePage; diff --git a/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx b/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx index 828addc0ba..c515ebbf83 100644 --- a/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx +++ b/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx @@ -2,7 +2,6 @@ import Title from "../../.storybook/components/Title"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import DxcBreadcrumbs from "./Breadcrumbs"; import DxcContainer from "../container/Container"; -import { HalstackProvider } from "../HalstackContext"; import { userEvent, within } from "@storybook/test"; import { disabledRules } from "../../test/accessibility/rules/specific/breadcrumbs/disabledRules"; import preview from "../../.storybook/preview"; @@ -94,7 +93,7 @@ const Breadcrumbs = () => ( - <ExampleContainer pseudoState="pseudo-active"> + <ExampleContainer pseudoState={["pseudo-active", "pseudo-focus"]}> <DxcBreadcrumbs items={items} /> </ExampleContainer> <Title title="Truncation and text ellipsis with tooltip (only when collapsed)" theme="light" level={3} /> @@ -150,39 +149,6 @@ const Breadcrumbs = () => ( /> </DxcContainer> </ExampleContainer> - <Title title="Dropdown theming doesn't affect the collapsed trigger" theme="light" level={3} /> - <ExampleContainer> - <Title title="Opinionated theming" theme="light" level={4} /> - <ExampleContainer> - <HalstackProvider - theme={{ - dropdown: { - baseColor: "#fabada", - fontColor: "#999", - optionFontColor: "#4d4d4d", - }, - }} - > - <DxcBreadcrumbs items={items} itemsBeforeCollapse={3} /> - </HalstackProvider> - </ExampleContainer> - <Title title="Advanced theming" theme="light" level={4} /> - <ExampleContainer> - <HalstackProvider - advancedTheme={{ - dropdown: { - buttonBackgroundColor: "#fabada", - buttonHeight: "100px", - buttonBorderThickness: "2px", - buttonBorderStyle: "solid", - buttonBorderColor: "#000", - }, - }} - > - <DxcBreadcrumbs items={items} itemsBeforeCollapse={3} /> - </HalstackProvider> - </ExampleContainer> - </ExampleContainer> </> ); @@ -193,6 +159,6 @@ export const Chromatic: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const dropdowns = canvas.getAllByRole("button"); - dropdowns[2] != null && await userEvent.click(dropdowns[2]); + dropdowns[2] != null && (await userEvent.click(dropdowns[2])); }, }; diff --git a/packages/lib/src/breadcrumbs/Breadcrumbs.tsx b/packages/lib/src/breadcrumbs/Breadcrumbs.tsx index 52a30c90e1..af6a8265fe 100644 --- a/packages/lib/src/breadcrumbs/Breadcrumbs.tsx +++ b/packages/lib/src/breadcrumbs/Breadcrumbs.tsx @@ -2,13 +2,33 @@ import { useCallback } from "react"; import styled from "styled-components"; import BreadcrumbsProps from "./types"; import DxcDropdown from "../dropdown/Dropdown"; -import { HalstackProvider } from "../HalstackContext"; -import dropdownTheme from "./dropdownTheme"; -import CoreTokens from "../common/coreTokens"; import DxcIcon from "../icon/Icon"; import Item from "./Item"; -import DxcFlex from "../flex/Flex"; import { Option } from "../dropdown/types"; +import DxcFlex from "../flex/Flex"; + +const OrderedList = styled.ol` + display: flex; + align-items: center; + gap: var(--spacing-gap-m); + list-style-type: none; + margin: 0; + padding: 0; + + > li:not(:first-child) { + > a, + > span { + margin-left: var(--spacing-gap-m); + } + &::before { + border-right: var(--border-width-s) var(--border-style-default) var(--border-color-neutral-strong); + content: ""; + height: var(--height-xxs); + margin: var(--spacing-padding-none) var(--spacing-padding-xxs); + transform: rotate(15deg); + } + } +`; const DxcBreadcrumbs = ({ ariaLabel = "Breadcrumbs", @@ -19,11 +39,8 @@ const DxcBreadcrumbs = ({ }: BreadcrumbsProps) => { const handleOnSelectOption = useCallback( (href: string) => { - if (onItemClick) { - onItemClick(href); - } else { - window.location.href = href; - } + if (onItemClick) onItemClick(href); + else window.location.href = href; }, [items] ); @@ -35,17 +52,14 @@ const DxcBreadcrumbs = ({ <> {showRoot && <Item href={items[0]?.href} key={0} label={items[0]?.label ?? ""} />} <DxcFlex alignItems="center" as="li" key={1}> - <HalstackProvider advancedTheme={dropdownTheme}> - <DxcDropdown - caretHidden - icon={<DxcIcon icon="more_horiz" />} - margin={showRoot ? { left: "small" } : undefined} - onSelectOption={handleOnSelectOption} - options={items - .slice(showRoot ? 1 : 0, -1) - .map(({ label, href }) => ({ label, value: href }) as Option)} - /> - </HalstackProvider> + <DxcDropdown + caretHidden + icon={<DxcIcon icon="more_horiz" />} + margin={showRoot ? { left: "small" } : undefined} + onSelectOption={handleOnSelectOption} + options={items.slice(showRoot ? 1 : 0, -1).map(({ label, href }) => ({ label, value: href }) as Option)} + title="More options" + /> </DxcFlex> <Item isCurrentPage key={2} label={items[items.length - 1]?.label ?? ""} /> </> @@ -65,27 +79,4 @@ const DxcBreadcrumbs = ({ ); }; -const OrderedList = styled.ol` - margin: ${CoreTokens.spacing_0}; - padding-left: ${CoreTokens.spacing_0}; - display: flex; - align-items: center; - gap: ${CoreTokens.spacing_12}; - list-style-type: none; - - > li:not(:first-child) { - > a, - > span { - margin-left: ${CoreTokens.spacing_12}; - } - &::before { - margin: ${CoreTokens.spacing_0} ${CoreTokens.spacing_2}; - transform: rotate(15deg); - border-right: ${CoreTokens.border_width_1} solid ${CoreTokens.color_grey_500}; - height: 1rem; - content: ""; - } - } -`; - export default DxcBreadcrumbs; diff --git a/packages/lib/src/breadcrumbs/Item.tsx b/packages/lib/src/breadcrumbs/Item.tsx index 472e027690..b6bb47b094 100644 --- a/packages/lib/src/breadcrumbs/Item.tsx +++ b/packages/lib/src/breadcrumbs/Item.tsx @@ -1,78 +1,77 @@ import { useRef, MouseEvent } from "react"; import styled from "styled-components"; -import CoreTokens from "../common/coreTokens"; import { ItemPropsType } from "./types"; -const Item = ({ isCurrentPage = false, href, label, onClick }: ItemPropsType) => { - const currentItemRef = useRef<HTMLSpanElement | null>(null); - - const handleOnMouseEnter = (event: MouseEvent<HTMLAnchorElement>) => { - const labelContainer = event.currentTarget; - const optionElement = currentItemRef.current; - if (optionElement?.title === "" && labelContainer.scrollWidth > labelContainer.clientWidth) { - optionElement.title = label; - } - }; - - const handleOnClick = (event: MouseEvent<HTMLAnchorElement>) => { - event.preventDefault(); - if (href) { - onClick?.(href); - } - }; - - return ( - <ListItem aria-current={isCurrentPage ? "page" : undefined} isCurrentPage={isCurrentPage}> - {isCurrentPage ? ( - <CurrentPage ref={currentItemRef} onMouseEnter={handleOnMouseEnter}> - {label} - </CurrentPage> - ) : ( - <Link href={href} onClick={handleOnClick}> - <Text>{label}</Text> - </Link> - )} - </ListItem> - ); -}; - const ListItem = styled.li<{ isCurrentPage?: ItemPropsType["isCurrentPage"] }>` display: flex; align-items: center; - font-family: ${CoreTokens.type_sans}; - font-size: ${CoreTokens.type_scale_02}; - color: ${CoreTokens.color_black}; + color: var(--color-fg-neutral-dark); + font-family: var(--typography-font-family); + font-size: var(--typography-label-m); + font-weight: var(--typography-label-regular); ${({ isCurrentPage }) => isCurrentPage && "overflow: hidden;"} `; const CurrentPage = styled.span` - font-weight: ${CoreTokens.type_semibold}; + padding: var(--spacing-padding-none) var(--spacing-padding-xxs); + font-weight: var(--typography-label-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - cursor: default; `; const Link = styled.a` - border-radius: ${CoreTokens.border_radius_small}; - padding: ${CoreTokens.spacing_0} ${CoreTokens.spacing_2}; + border-radius: var(--border-radius-s); + padding: var(--spacing-padding-none) var(--spacing-padding-xxs); display: inline-flex; align-items: center; - height: 24px; - color: ${CoreTokens.color_black}; - text-decoration: ${CoreTokens.type_no_line}; + height: var(--height-s); + color: inherit; + text-decoration: none; cursor: pointer; &:focus { - outline: ${CoreTokens.border_width_2} solid ${CoreTokens.color_blue_600}; + outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium); + outline-offset: -2px; } `; const Text = styled.span` - border: ${CoreTokens.border_width_1} solid ${CoreTokens.color_transparent}; + border: var(--border-width-s) var(--border-style-default) transparent; + &:hover { - border-bottom-color: ${CoreTokens.color_black}; + border-bottom-color: var(--color-fg-neutral-dark); } `; +const Item = ({ isCurrentPage = false, href, label, onClick }: ItemPropsType) => { + const currentItemRef = useRef<HTMLSpanElement | null>(null); + + const handleOnClick = (event: MouseEvent<HTMLAnchorElement>) => { + event.preventDefault(); + if (href) onClick?.(href); + }; + + const handleOnMouseEnter = (event: MouseEvent<HTMLAnchorElement>) => { + const labelContainer = event.currentTarget; + const optionElement = currentItemRef.current; + if (optionElement?.title === "" && labelContainer.scrollWidth > labelContainer.clientWidth) + optionElement.title = label; + }; + + return ( + <ListItem aria-current={isCurrentPage ? "page" : undefined} isCurrentPage={isCurrentPage}> + {isCurrentPage ? ( + <CurrentPage ref={currentItemRef} onMouseEnter={handleOnMouseEnter}> + {label} + </CurrentPage> + ) : ( + <Link href={href} onClick={handleOnClick}> + <Text>{label}</Text> + </Link> + )} + </ListItem> + ); +}; + export default Item; diff --git a/packages/lib/src/breadcrumbs/dropdownTheme.ts b/packages/lib/src/breadcrumbs/dropdownTheme.ts deleted file mode 100644 index 5fe908d28b..0000000000 --- a/packages/lib/src/breadcrumbs/dropdownTheme.ts +++ /dev/null @@ -1,57 +0,0 @@ -import CoreTokens from "../common/coreTokens"; - -export default { - dropdown: { - // Breadcrumbs tokens - buttonIconSize: CoreTokens.spacing_16, - buttonPaddingTop: CoreTokens.spacing_4, - buttonPaddingBottom: CoreTokens.spacing_4, - buttonPaddingLeft: CoreTokens.spacing_4, - buttonPaddingRight: CoreTokens.spacing_4, - buttonHeight: "24px", - buttonBorderRadius: "2px", - buttonBorderColor: CoreTokens.color_transparent, - optionFontSize: "14px", - optionPaddingTop: CoreTokens.spacing_0, - optionPaddingBottom: CoreTokens.spacing_0, - optionPaddingLeft: CoreTokens.spacing_16, - optionPaddingRight: CoreTokens.spacing_16, - - // Dropdown tokens - buttonBackgroundColor: CoreTokens.color_white, - hoverButtonBackgroundColor: CoreTokens.color_grey_100, - activeButtonBackgroundColor: CoreTokens.color_grey_300, - buttonFontFamily: CoreTokens.type_sans, - buttonFontSize: CoreTokens.type_scale_03, - buttonFontStyle: CoreTokens.type_normal, - buttonFontWeight: CoreTokens.type_regular, - buttonFontColor: CoreTokens.color_black, - buttonIconSpacing: "10px", - buttonIconColor: CoreTokens.color_black, - buttonBorderStyle: CoreTokens.border_none, - buttonBorderThickness: CoreTokens.border_width_0, - disabledColor: CoreTokens.color_grey_500, - disabledButtonBackgroundColor: CoreTokens.color_transparent, - disabledButtonBorderColor: CoreTokens.color_transparent, - optionBackgroundColor: CoreTokens.color_white, - hoverOptionBackgroundColor: CoreTokens.color_grey_100, - activeOptionBackgroundColor: CoreTokens.color_grey_300, - optionFontFamily: CoreTokens.type_sans, - optionFontStyle: CoreTokens.type_normal, - optionFontWeight: CoreTokens.type_regular, - optionFontColor: CoreTokens.color_black, - optionIconSize: "20px", - optionIconSpacing: "10px", - optionIconColor: CoreTokens.color_black, - caretIconSize: "24px", - caretIconColor: CoreTokens.color_black, - caretIconSpacing: "12px", - borderRadius: "4px", - borderStyle: CoreTokens.border_none, - borderThickness: CoreTokens.border_width_0, - borderColor: CoreTokens.color_transparent, - scrollBarThumbColor: CoreTokens.color_grey_700, - scrollBarTrackColor: CoreTokens.color_grey_300, - focusColor: CoreTokens.color_blue_600, - }, -}; diff --git a/packages/lib/src/dropdown/Dropdown.stories.tsx b/packages/lib/src/dropdown/Dropdown.stories.tsx index f7453809ed..6d655d1a89 100644 --- a/packages/lib/src/dropdown/Dropdown.stories.tsx +++ b/packages/lib/src/dropdown/Dropdown.stories.tsx @@ -89,74 +89,68 @@ const Dropdown = () => ( <> <ExampleContainer> <Title title="Default" theme="light" level={4} /> - <DxcDropdown label="Default" options={options} onSelectOption={(value) => {}} /> + <DxcDropdown label="Default" options={options} onSelectOption={() => {}} /> </ExampleContainer> <ExampleContainer pseudoState="pseudo-hover"> <Title title="Hovered" theme="light" level={4} /> - <DxcDropdown label="Hovered" options={options} onSelectOption={(value) => {}} /> + <DxcDropdown label="Hovered" options={options} onSelectOption={() => {}} /> </ExampleContainer> <ExampleContainer pseudoState="pseudo-focus"> <Title title="Focused" theme="light" level={4} /> - <DxcDropdown label="Focused" options={options} onSelectOption={(value) => {}} /> + <DxcDropdown label="Focused" options={options} onSelectOption={() => {}} /> </ExampleContainer> - <ExampleContainer pseudoState="pseudo-active"> - <Title title="Actived" theme="light" level={4} /> - <DxcDropdown label="Actived" options={options} onSelectOption={(value) => {}} /> + <ExampleContainer pseudoState={["pseudo-active", "pseudo-focus"]}> + <Title title="Active" theme="light" level={4} /> + <DxcDropdown label="Active" options={options} onSelectOption={() => {}} /> </ExampleContainer> <ExampleContainer> <Title title="Disabled" theme="light" level={4} /> - <DxcDropdown label="Disabled" options={options} onSelectOption={(value) => {}} disabled /> + <DxcDropdown label="Disabled" options={options} onSelectOption={() => {}} disabled /> </ExampleContainer> <ExampleContainer> <Title title="Caret hidden" theme="light" level={4} /> - <DxcDropdown label="Caret hidden" options={options} onSelectOption={(value) => {}} caretHidden /> + <DxcDropdown label="Caret hidden" options={options} onSelectOption={() => {}} caretHidden /> </ExampleContainer> <ExampleContainer> <Title title="With icon before" theme="light" level={4} /> - <DxcDropdown label="Icon before" options={options} onSelectOption={(value) => {}} icon={iconSVG} /> + <DxcDropdown label="Icon before" options={options} onSelectOption={() => {}} icon={iconSVG} /> </ExampleContainer> <ExampleContainer> <Title title="With icon after" theme="light" level={4} /> <DxcDropdown label="Icon after" options={options} - onSelectOption={(value) => {}} + onSelectOption={() => {}} icon="shopping_cart" iconPosition="after" /> </ExampleContainer> <ExampleContainer> <Title title="Only icon" theme="light" level={4} /> - <DxcDropdown options={options} onSelectOption={(value) => {}} icon={iconSVG} /> + <DxcDropdown options={options} onSelectOption={() => {}} icon={iconSVG} /> </ExampleContainer> <ExampleContainer> <Title title="Only icon without caret" theme="light" level={4} /> - <DxcDropdown options={options} onSelectOption={(value) => {}} icon="menu" caretHidden /> + <DxcDropdown options={options} onSelectOption={() => {}} icon="menu" caretHidden /> </ExampleContainer> <ExampleContainer> <Title title="Large icon (SVG)" theme="light" level={4} /> - <DxcDropdown label="Large icon" options={options} onSelectOption={(value) => {}} icon={iconSVGLarge} /> + <DxcDropdown label="Large icon" options={options} onSelectOption={() => {}} icon={iconSVGLarge} /> </ExampleContainer> <ExampleContainer> <Title title="Large icon (image)" theme="light" level={4} /> - <DxcDropdown label="Large icon" options={options} onSelectOption={(value) => {}} icon="menu" /> + <DxcDropdown label="Large icon" options={options} onSelectOption={() => {}} icon="menu" /> </ExampleContainer> <ExampleContainer> <Title title="Disabled with icon" theme="light" level={4} /> - <DxcDropdown - label="Disabled with icon" - options={options} - onSelectOption={(value) => {}} - icon={iconSVG} - disabled - /> + <DxcDropdown label="Disabled with icon" options={options} onSelectOption={() => {}} icon={iconSVG} disabled /> </ExampleContainer> <ExampleContainer> <Title title="Ellipsis" theme="light" level={4} /> <DxcDropdown label="Very long text in dropdown button" options={options} - onSelectOption={(value) => {}} + onSelectOption={() => {}} icon={iconSVG} size="medium" /> @@ -164,68 +158,56 @@ const Dropdown = () => ( <Title title="Margins" theme="light" level={2} /> <ExampleContainer> <Title title="Xxsmall" theme="light" level={4} /> - <DxcDropdown label="Xxsmall" options={options} onSelectOption={(value) => {}} icon={iconSVG} margin="xxsmall" /> + <DxcDropdown label="Xxsmall" options={options} onSelectOption={() => {}} icon={iconSVG} margin="xxsmall" /> </ExampleContainer> <ExampleContainer> <Title title="Xsmall" theme="light" level={4} /> - <DxcDropdown label="Xsmall" options={options} onSelectOption={(value) => {}} icon={iconSVG} margin="xsmall" /> + <DxcDropdown label="Xsmall" options={options} onSelectOption={() => {}} icon={iconSVG} margin="xsmall" /> </ExampleContainer> <ExampleContainer> <Title title="Small" theme="light" level={4} /> - <DxcDropdown label="Small" options={options} onSelectOption={(value) => {}} icon={iconSVG} margin="small" /> + <DxcDropdown label="Small" options={options} onSelectOption={() => {}} icon={iconSVG} margin="small" /> </ExampleContainer> <ExampleContainer> <Title title="Medium" theme="light" level={4} /> - <DxcDropdown label="Medium" options={options} onSelectOption={(value) => {}} icon={iconSVG} margin="medium" /> + <DxcDropdown label="Medium" options={options} onSelectOption={() => {}} icon={iconSVG} margin="medium" /> </ExampleContainer> <ExampleContainer> <Title title="Large" theme="light" level={4} /> - <DxcDropdown label="Large" options={options} onSelectOption={(value) => {}} icon={iconSVG} margin="large" /> + <DxcDropdown label="Large" options={options} onSelectOption={() => {}} icon={iconSVG} margin="large" /> </ExampleContainer> <ExampleContainer> <Title title="Xlarge" theme="light" level={4} /> - <DxcDropdown label="Xlarge" options={options} onSelectOption={(value) => {}} icon={iconSVG} margin="xlarge" /> + <DxcDropdown label="Xlarge" options={options} onSelectOption={() => {}} icon={iconSVG} margin="xlarge" /> </ExampleContainer> <ExampleContainer> <Title title="Xxlarge" theme="light" level={4} /> - <DxcDropdown label="Xxlarge" options={options} onSelectOption={(value) => {}} icon={iconSVG} margin="xxlarge" /> + <DxcDropdown label="Xxlarge" options={options} onSelectOption={() => {}} icon={iconSVG} margin="xxlarge" /> </ExampleContainer> <Title title="Sizes" theme="light" level={2} /> <ExampleContainer> <Title title="Small" theme="light" level={4} /> - <DxcDropdown label="Small" options={options} onSelectOption={(value) => {}} icon={iconSVG} size="small" /> + <DxcDropdown label="Small" options={options} onSelectOption={() => {}} icon={iconSVG} size="small" /> </ExampleContainer> <ExampleContainer> <Title title="Medium" theme="light" level={4} /> - <DxcDropdown label="Medium" options={options} onSelectOption={(value) => {}} icon={iconSVG} size="medium" /> + <DxcDropdown label="Medium" options={options} onSelectOption={() => {}} icon={iconSVG} size="medium" /> </ExampleContainer> <ExampleContainer> <Title title="Large" theme="light" level={4} /> - <DxcDropdown label="Large" options={options} onSelectOption={(value) => {}} icon={iconSVG} size="large" /> + <DxcDropdown label="Large" options={options} onSelectOption={() => {}} icon={iconSVG} size="large" /> </ExampleContainer> <ExampleContainer> <Title title="FitContent" theme="light" level={4} /> - <DxcDropdown - label="FitContent" - options={options} - onSelectOption={(value) => {}} - icon={iconSVG} - size="fitContent" - /> + <DxcDropdown label="FitContent" options={options} onSelectOption={() => {}} icon={iconSVG} size="fitContent" /> </ExampleContainer> <ExampleContainer> <Title title="FillParent" theme="light" level={4} /> - <DxcDropdown - label="FillParent" - options={options} - onSelectOption={(value) => {}} - icon={iconSVG} - size="fillParent" - /> + <DxcDropdown label="FillParent" options={options} onSelectOption={() => {}} icon={iconSVG} size="fillParent" /> </ExampleContainer> <ExampleContainer expanded> <Title title="Opened menu" theme="light" level={4} /> - <DxcDropdown label="Label" options={options} onSelectOption={(value) => {}} margin={{ top: "xxlarge" }} /> + <DxcDropdown label="Label" options={options} onSelectOption={() => {}} margin={{ top: "xxlarge" }} /> </ExampleContainer> </> ); @@ -276,8 +258,8 @@ const DropdownListStates = () => { dropdownTriggerId="dtx1" iconsPosition="before" visualFocusIndex={-1} - menuItemOnClick={(value) => {}} - onKeyDown={(e) => {}} + menuItemOnClick={() => {}} + onKeyDown={() => {}} options={optionWithIcon} styles={{ width: 240 }} /> @@ -289,8 +271,8 @@ const DropdownListStates = () => { dropdownTriggerId="dtx2" iconsPosition="before" visualFocusIndex={-1} - menuItemOnClick={(value) => {}} - onKeyDown={(e) => {}} + menuItemOnClick={() => {}} + onKeyDown={() => {}} options={optionWithIcon} styles={{ width: 240 }} /> @@ -302,8 +284,8 @@ const DropdownListStates = () => { dropdownTriggerId="dtx3" iconsPosition="before" visualFocusIndex={0} - menuItemOnClick={(value) => {}} - onKeyDown={(e) => {}} + menuItemOnClick={() => {}} + onKeyDown={() => {}} options={options} styles={{ width: 240 }} /> @@ -316,8 +298,8 @@ const DropdownListStates = () => { dropdownTriggerId="dtx4" iconsPosition="before" visualFocusIndex={-1} - menuItemOnClick={(value) => {}} - onKeyDown={(e) => {}} + menuItemOnClick={() => {}} + onKeyDown={() => {}} options={optionsIcon} styles={{ width: 240 }} /> @@ -327,8 +309,8 @@ const DropdownListStates = () => { dropdownTriggerId="dtx5" iconsPosition="after" visualFocusIndex={-1} - menuItemOnClick={(value) => {}} - onKeyDown={(e) => {}} + menuItemOnClick={() => {}} + onKeyDown={() => {}} options={optionsIcon} styles={{ width: 240 }} /> @@ -344,7 +326,7 @@ const TooltipTitle = () => ( <DxcDropdown title="Show options" options={options} - onSelectOption={(value) => {}} + onSelectOption={() => {}} icon="menu" caretHidden margin="large" diff --git a/packages/lib/src/dropdown/Dropdown.tsx b/packages/lib/src/dropdown/Dropdown.tsx index 3fed994a6e..daac4234da 100644 --- a/packages/lib/src/dropdown/Dropdown.tsx +++ b/packages/lib/src/dropdown/Dropdown.tsx @@ -4,7 +4,6 @@ import styled from "styled-components"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; -import HalstackContext from "../HalstackContext"; import useWidth from "../utils/useWidth"; import DropdownMenu from "./DropdownMenu"; import DropdownPropsType from "./types"; @@ -133,7 +132,6 @@ const DxcDropdown = ({ const [isOpen, changeIsOpen] = useState(false); const [visualFocusIndex, setVisualFocusIndex] = useState(0); - const colorsTheme = useContext(HalstackContext); const triggerRef = useRef<HTMLButtonElement | null>(null); const menuRef = useRef<HTMLUListElement | null>(null); const width = useWidth(triggerRef.current); @@ -309,7 +307,7 @@ const DxcDropdown = ({ visualFocusIndex={visualFocusIndex} menuItemOnClick={handleMenuItemOnClick} onKeyDown={handleMenuOnKeyDown} - styles={{ width, zIndex: "2147483647" }} + styles={{ width }} ref={menuRef} /> </Popover.Content> diff --git a/packages/lib/src/dropdown/DropdownMenu.tsx b/packages/lib/src/dropdown/DropdownMenu.tsx index 92bead8f4c..f5361523dc 100644 --- a/packages/lib/src/dropdown/DropdownMenu.tsx +++ b/packages/lib/src/dropdown/DropdownMenu.tsx @@ -15,6 +15,7 @@ const DropdownMenuContainer = styled.ul` var(--shadow-dark); outline: none; overflow-y: auto; + z-index: 2147483647; ${scrollbarStyles} `;