From b7390b4cfa25893dc6460d2a6a8239aa8187e158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Fri, 9 Feb 2024 14:30:21 +0100 Subject: [PATCH 01/18] Breadcrumbs initial version --- lib/src/breadcrumbs/Breadcrumbs.stories.tsx | 118 +++++++++++ lib/src/breadcrumbs/Breadcrumbs.tsx | 208 ++++++++++++++++++++ lib/src/breadcrumbs/types.ts | 16 ++ lib/src/common/variables.ts | 12 ++ 4 files changed, 354 insertions(+) create mode 100644 lib/src/breadcrumbs/Breadcrumbs.stories.tsx create mode 100644 lib/src/breadcrumbs/Breadcrumbs.tsx create mode 100644 lib/src/breadcrumbs/types.ts diff --git a/lib/src/breadcrumbs/Breadcrumbs.stories.tsx b/lib/src/breadcrumbs/Breadcrumbs.stories.tsx new file mode 100644 index 000000000..23f3a4df3 --- /dev/null +++ b/lib/src/breadcrumbs/Breadcrumbs.stories.tsx @@ -0,0 +1,118 @@ +import React from "react"; +import Title from "../../.storybook/components/Title"; +import ExampleContainer from "../../.storybook/components/ExampleContainer"; +import DxcBreadcrumbs from "./Breadcrumbs"; + +export default { + title: "Breadcrumbs", + component: DxcBreadcrumbs, +}; + +const collapsedIcon = ( + + + +); + +const items = [ + { + label: "Home", + href: "/", + }, + { + label: "User Menu", + href: "", + }, + { + label: "Preferences", + href: "", + }, + { + label: "Customization", + href: "", + }, + { + label: "Dark Mode", + href: "", + }, +]; + +export const Chromatic = () => ( + <> + + <ExampleContainer> + <DxcBreadcrumbs + items={[ + { + label: "Home", + href: "/", + }, + { + label: "User Menu", + href: "", + }, + { + label: "Preferences", + href: "", + }, + { + label: "Dark Mode", + href: "", + }, + ]} + /> + </ExampleContainer> + <Title title="Home icon breadcrumbs" theme="light" level={3} /> + <ExampleContainer> + <DxcBreadcrumbs + items={[ + { + label: "Home", + href: "/", + }, + { + label: "User Menu", + href: "", + }, + ]} + /> + </ExampleContainer> + <Title title="Collapsed breadcrumbs" theme="light" level={3} /> + <ExampleContainer> + <DxcBreadcrumbs items={items} /> + </ExampleContainer> + <Title title="Collapsed breadcrumbs without root" theme="light" level={3} /> + <ExampleContainer> + <DxcBreadcrumbs items={items} showRoot={false} /> + </ExampleContainer> + <Title title="Collapsed breadcrumbs with custom collapsed icon" theme="light" level={3} /> + <ExampleContainer> + <DxcBreadcrumbs items={items} showRoot={false} collapsedIcon={collapsedIcon} /> + </ExampleContainer> + <Title title="Breadcrumbs collapse with three elements" theme="light" level={3} /> + <ExampleContainer> + <DxcBreadcrumbs + items={[ + { + label: "Root", + href: "/", + }, + { + label: "Main folder", + href: "", + }, + { + label: "User", + href: "", + }, + { + label: "Test", + href: "", + }, + ]} + collapsedIcon={collapsedIcon} + itemsBeforeCollapse={3} + /> + </ExampleContainer> + </> +); diff --git a/lib/src/breadcrumbs/Breadcrumbs.tsx b/lib/src/breadcrumbs/Breadcrumbs.tsx new file mode 100644 index 000000000..c57120ed4 --- /dev/null +++ b/lib/src/breadcrumbs/Breadcrumbs.tsx @@ -0,0 +1,208 @@ +import React, { useState } from "react"; +import styled, { ThemeProvider } from "styled-components"; +import useTheme from "../useTheme"; +import BreadcrumbsProps, { Item } from "./types"; +import { css } from "styled-components"; +import DxcActionIcon from "../action-icon/ActionIcon"; +import * as Popover from "@radix-ui/react-popover"; +import DxcContainer from "../container/Container"; + +const defaultCollapsedIcon = ( + <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"> + <path d="M240-400q-33 0-56.5-23.5T160-480q0-33 23.5-56.5T240-560q33 0 56.5 23.5T320-480q0 33-23.5 56.5T240-400Zm240 0q-33 0-56.5-23.5T400-480q0-33 23.5-56.5T480-560q33 0 56.5 23.5T560-480q0 33-23.5 56.5T480-400Zm240 0q-33 0-56.5-23.5T640-480q0-33 23.5-56.5T720-560q33 0 56.5 23.5T800-480q0 33-23.5 56.5T720-400Z" /> + </svg> +); + +const DropdownMenu = ({ + collapsedIcon, + items, +}: { + collapsedIcon: BreadcrumbsProps["collapsedIcon"]; + items: BreadcrumbsProps["items"]; +}) => { + const [collapsed, setCollapsed] = useState(false); + + return ( + <Popover.Root open={collapsed}> + <Popover.Trigger asChild type={undefined}> + <DxcActionIcon + icon={collapsedIcon ?? defaultCollapsedIcon} + onClick={() => { + setCollapsed(!collapsed); + }} + title="Expand/collapse breadcrumbs menu" + /> + </Popover.Trigger> + <Popover.Portal> + <Popover.Content sideOffset={6} style={{ zIndex: "2147483647" }}> + <DxcContainer + boxSizing="border-box" + boxShadow="0 4px 6px -1px rgba(0, 0, 0, 0.1)" + border={{ width: "1px", style: "solid", color: "color_grey_400" }} + borderRadius="0.25rem" + background={{ color: "color_white" }} + padding={{ top: "xxsmall", bottom: "xxsmall" }} + maxHeight="304px" + width="fitContent" + overflow={{ x: "hidden", y: "auto" }} + > + {items.map((item, index) => ( + <DxcContainer padding={{ left: "xsmall", right: "xsmall" }}> + <DxcContainer + border={ + index !== items.length - 1 + ? { bottom: { width: "1px", style: "solid", color: "color_grey_200" } } + : undefined + } + padding="xxsmall" + overflow="hidden" + > + <Label>{item.label}</Label> + </DxcContainer> + </DxcContainer> + ))} + </DxcContainer> + </Popover.Content> + </Popover.Portal> + </Popover.Root> + ); +}; + +const mapItems = (item: Item, index: number, { length }) => { + const isLast = index === length - 1; + + return ( + <ListItem key={index} aria-current={isLast ? "page" : undefined}> + {isLast ? ( + <CurrentLabel>{item.label}</CurrentLabel> + ) : ( + <Link href={item.href}> + <Text>{item.label}</Text> + </Link> + )} + </ListItem> + ); +}; + +const mapMultipleItems = ( + collapsedIcon: BreadcrumbsProps["collapsedIcon"], + items: BreadcrumbsProps["items"], + showRoot: BreadcrumbsProps["showRoot"] +) => { + const first = items[0]; + const last = items[items.length - 1]; + + return ( + <> + {showRoot && ( + <ListItem key={0}> + <Link href={first.href}> + <Text>{first.label}</Text> + </Link> + </ListItem> + )} + <ListItem key={1}> + <DropdownMenu collapsedIcon={collapsedIcon} items={items.slice(showRoot ? 1 : 0, -1)} /> + </ListItem> + <ListItem key={2} aria-current="page"> + <CurrentLabel>{last.label}</CurrentLabel> + </ListItem> + </> + ); +}; + +const DxcBreadcrumbs = ({ + ariaLabel = "Breadcrumbs", + collapsedIcon, + items, + itemsBeforeCollapse = 4, + showRoot = true, +}: BreadcrumbsProps) => { + const colorsTheme = useTheme(); + + return ( + <ThemeProvider theme={colorsTheme.breadcrumbs}> + <nav aria-label={ariaLabel}> + <OrderedList> + {itemsBeforeCollapse >= 2 && items.length > itemsBeforeCollapse + ? mapMultipleItems(collapsedIcon, items, showRoot) + : items.map(mapItems)} + </OrderedList> + </nav> + </ThemeProvider> + ); +}; + +const OrderedList = styled.ol` + padding-left: 0; + display: flex; + align-items: center; + list-style-type: none; + margin: 0; + + > li:not(:first-child) { + margin-left: 0.75rem; + + > a, + > span, + > button { + margin-left: 0.75rem; + } + &::before { + display: inline-flex; + transform: rotate(15deg); + border-right: 2px solid #999; + height: 1rem; + content: ""; + } + } +`; + +const sharedStyles = css` + display: inline-flex; + justify-content: center; + align-items: center; + height: 24px; + font-family: "Open Sans", sans-serif; + font-size: 0.875rem; +`; + +const ListItem = styled.li` + display: flex; + align-items: center; +`; + +const Label = styled.span` + ${sharedStyles} + color: #000; +`; + +const CurrentLabel = styled.span` + ${sharedStyles} + color: #999; +`; + +const Link = styled.a` + border-radius: 2px; + padding: 0 2px; + color: #000000; + text-decoration: none; + cursor: pointer; + + &:focus { + box-shadow: 0 0 0 2px #0095ff; + outline: none; + } +`; + +const Text = styled.span` + box-sizing: border-box; + ${sharedStyles} + border: 1px solid transparent; + + &:hover { + border-bottom: 1px solid #000000; + } +`; + +export default DxcBreadcrumbs; diff --git a/lib/src/breadcrumbs/types.ts b/lib/src/breadcrumbs/types.ts new file mode 100644 index 000000000..91f69dfc1 --- /dev/null +++ b/lib/src/breadcrumbs/types.ts @@ -0,0 +1,16 @@ +type SVG = React.ReactNode & React.SVGProps<SVGSVGElement>; + +export type Item = { + label: string; + href?: string; +}; + +type Props = { + ariaLabel?: string; + collapsedIcon?: SVG; + items: Array<Item>; + itemsBeforeCollapse?: number; + showRoot?: boolean; +}; + +export default Props; diff --git a/lib/src/common/variables.ts b/lib/src/common/variables.ts index a669f2f8a..d24ea1a33 100644 --- a/lib/src/common/variables.ts +++ b/lib/src/common/variables.ts @@ -87,6 +87,18 @@ export const componentTokens = { focusActionBorderColor: CoreTokens.color_blue_600, overlayColor: CoreTokens.color_grey_800_a, }, + breadcrumbs: { + actionBackgroundColor: CoreTokens.color_transparent, + hoverActionBackgroundColor: CoreTokens.color_grey_100, + activeActionBackgroundColor: CoreTokens.color_grey_300, + disabledActionBackgroundColor: CoreTokens.color_transparent, + focusActionBorderColor: CoreTokens.color_blue_600, + actionIconColor: CoreTokens.color_black, + disabledActionIconColor: CoreTokens.color_grey_500, + hoverActionIconColor: CoreTokens.color_black, + focusActionIconColor: CoreTokens.color_black, + activeActionIconColor: CoreTokens.color_black, + }, box: { backgroundColor: CoreTokens.color_white, borderRadius: CoreTokens.border_radius_medium, From 37ccdc1f14f3f855ead446c7401ef232be0b89a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Tue, 13 Feb 2024 17:03:18 +0100 Subject: [PATCH 02/18] The Breadcrumbs now uses our Dropdown --- lib/src/breadcrumbs/Breadcrumbs.stories.tsx | 31 +++- lib/src/breadcrumbs/Breadcrumbs.tsx | 151 ++++++-------------- lib/src/breadcrumbs/dropdownTheme.ts | 52 +++++++ lib/src/breadcrumbs/types.ts | 7 +- lib/src/common/variables.ts | 35 ++--- lib/src/dropdown/Dropdown.tsx | 16 +-- 6 files changed, 146 insertions(+), 146 deletions(-) create mode 100644 lib/src/breadcrumbs/dropdownTheme.ts diff --git a/lib/src/breadcrumbs/Breadcrumbs.stories.tsx b/lib/src/breadcrumbs/Breadcrumbs.stories.tsx index 23f3a4df3..ec8b13664 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.stories.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.stories.tsx @@ -2,6 +2,7 @@ import React from "react"; import Title from "../../.storybook/components/Title"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import DxcBreadcrumbs from "./Breadcrumbs"; +import DxcContainer from "../container/Container"; export default { title: "Breadcrumbs", @@ -87,7 +88,7 @@ export const Chromatic = () => ( </ExampleContainer> <Title title="Collapsed breadcrumbs with custom collapsed icon" theme="light" level={3} /> <ExampleContainer> - <DxcBreadcrumbs items={items} showRoot={false} collapsedIcon={collapsedIcon} /> + <DxcBreadcrumbs items={items} showRoot={false} /> </ExampleContainer> <Title title="Breadcrumbs collapse with three elements" theme="light" level={3} /> <ExampleContainer> @@ -110,9 +111,35 @@ export const Chromatic = () => ( href: "", }, ]} - collapsedIcon={collapsedIcon} itemsBeforeCollapse={3} /> </ExampleContainer> + <Title title="Truncation and text ellipsis (only when collapsed)" theme="light" level={3} /> + <ExampleContainer> + <DxcContainer width="200px"> + <DxcBreadcrumbs + items={[ + { + label: "Root", + href: "/", + }, + { + label: "Main folder", + href: "", + }, + { + label: "User", + href: "", + }, + { + label: "Very long label for the link", + href: "", + }, + ]} + itemsBeforeCollapse={3} + showRoot={false} + /> + </DxcContainer> + </ExampleContainer> </> ); diff --git a/lib/src/breadcrumbs/Breadcrumbs.tsx b/lib/src/breadcrumbs/Breadcrumbs.tsx index c57120ed4..fac860711 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.tsx @@ -1,11 +1,10 @@ -import React, { useState } from "react"; +import React from "react"; import styled, { ThemeProvider } from "styled-components"; import useTheme from "../useTheme"; -import BreadcrumbsProps, { Item } from "./types"; -import { css } from "styled-components"; -import DxcActionIcon from "../action-icon/ActionIcon"; -import * as Popover from "@radix-ui/react-popover"; -import DxcContainer from "../container/Container"; +import BreadcrumbsProps, { ItemType } from "./types"; +import DxcDropdown from "../dropdown/Dropdown"; +import { HalstackProvider } from "../HalstackContext"; +import dropdownTheme from "./dropdownTheme"; const defaultCollapsedIcon = ( <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"> @@ -13,82 +12,23 @@ const defaultCollapsedIcon = ( </svg> ); -const DropdownMenu = ({ - collapsedIcon, - items, -}: { - collapsedIcon: BreadcrumbsProps["collapsedIcon"]; - items: BreadcrumbsProps["items"]; -}) => { - const [collapsed, setCollapsed] = useState(false); - - return ( - <Popover.Root open={collapsed}> - <Popover.Trigger asChild type={undefined}> - <DxcActionIcon - icon={collapsedIcon ?? defaultCollapsedIcon} - onClick={() => { - setCollapsed(!collapsed); - }} - title="Expand/collapse breadcrumbs menu" - /> - </Popover.Trigger> - <Popover.Portal> - <Popover.Content sideOffset={6} style={{ zIndex: "2147483647" }}> - <DxcContainer - boxSizing="border-box" - boxShadow="0 4px 6px -1px rgba(0, 0, 0, 0.1)" - border={{ width: "1px", style: "solid", color: "color_grey_400" }} - borderRadius="0.25rem" - background={{ color: "color_white" }} - padding={{ top: "xxsmall", bottom: "xxsmall" }} - maxHeight="304px" - width="fitContent" - overflow={{ x: "hidden", y: "auto" }} - > - {items.map((item, index) => ( - <DxcContainer padding={{ left: "xsmall", right: "xsmall" }}> - <DxcContainer - border={ - index !== items.length - 1 - ? { bottom: { width: "1px", style: "solid", color: "color_grey_200" } } - : undefined - } - padding="xxsmall" - overflow="hidden" - > - <Label>{item.label}</Label> - </DxcContainer> - </DxcContainer> - ))} - </DxcContainer> - </Popover.Content> - </Popover.Portal> - </Popover.Root> - ); -}; - -const mapItems = (item: Item, index: number, { length }) => { +const mapItems = (item: ItemType, index: number, { length }) => { const isLast = index === length - 1; return ( <ListItem key={index} aria-current={isLast ? "page" : undefined}> {isLast ? ( - <CurrentLabel>{item.label}</CurrentLabel> + <CurrentItem>{item.label}</CurrentItem> ) : ( - <Link href={item.href}> + <Item href={item.href}> <Text>{item.label}</Text> - </Link> + </Item> )} </ListItem> ); }; -const mapMultipleItems = ( - collapsedIcon: BreadcrumbsProps["collapsedIcon"], - items: BreadcrumbsProps["items"], - showRoot: BreadcrumbsProps["showRoot"] -) => { +const mapMultipleItems = (items: BreadcrumbsProps["items"], showRoot: BreadcrumbsProps["showRoot"]) => { const first = items[0]; const last = items[items.length - 1]; @@ -96,16 +36,26 @@ const mapMultipleItems = ( <> {showRoot && ( <ListItem key={0}> - <Link href={first.href}> + <Item href={first.href}> <Text>{first.label}</Text> - </Link> + </Item> </ListItem> )} <ListItem key={1}> - <DropdownMenu collapsedIcon={collapsedIcon} items={items.slice(showRoot ? 1 : 0, -1)} /> + <HalstackProvider advancedTheme={dropdownTheme}> + <DxcDropdown + icon={defaultCollapsedIcon} + options={items.slice(showRoot ? 1 : 0, -1).map(({ label, href }) => ({ label, value: href }))} + onSelectOption={(value) => { + window.location.href = value; + }} + caretHidden + margin={showRoot && { left: "small" }} + /> + </HalstackProvider> </ListItem> - <ListItem key={2} aria-current="page"> - <CurrentLabel>{last.label}</CurrentLabel> + <ListItem key={2} aria-current="page" style={{ overflow: "hidden" }}> + <CurrentItem>{last.label}</CurrentItem> </ListItem> </> ); @@ -113,7 +63,6 @@ const mapMultipleItems = ( const DxcBreadcrumbs = ({ ariaLabel = "Breadcrumbs", - collapsedIcon, items, itemsBeforeCollapse = 4, showRoot = true, @@ -125,7 +74,7 @@ const DxcBreadcrumbs = ({ <nav aria-label={ariaLabel}> <OrderedList> {itemsBeforeCollapse >= 2 && items.length > itemsBeforeCollapse - ? mapMultipleItems(collapsedIcon, items, showRoot) + ? mapMultipleItems(items, showRoot) : items.map(mapItems)} </OrderedList> </nav> @@ -134,22 +83,20 @@ const DxcBreadcrumbs = ({ }; const OrderedList = styled.ol` + margin: 0; padding-left: 0; display: flex; + gap: 0.75rem; align-items: center; list-style-type: none; - margin: 0; > li:not(:first-child) { - margin-left: 0.75rem; - > a, - > span, - > button { + > span { margin-left: 0.75rem; } &::before { - display: inline-flex; + margin: 0 0.125rem; transform: rotate(15deg); border-right: 2px solid #999; height: 1rem; @@ -158,50 +105,40 @@ const OrderedList = styled.ol` } `; -const sharedStyles = css` - display: inline-flex; - justify-content: center; - align-items: center; - height: 24px; - font-family: "Open Sans", sans-serif; - font-size: 0.875rem; -`; - const ListItem = styled.li` display: flex; align-items: center; -`; - -const Label = styled.span` - ${sharedStyles} + font-family: "Open Sans", sans-serif; + font-size: 0.875rem; color: #000; `; -const CurrentLabel = styled.span` - ${sharedStyles} - color: #999; +const CurrentItem = styled.span` + font-weight: 600; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; `; -const Link = styled.a` +const Item = styled.a` border-radius: 2px; padding: 0 2px; - color: #000000; + display: inline-flex; + align-items: center; + height: 24px; + color: #000; text-decoration: none; cursor: pointer; &:focus { - box-shadow: 0 0 0 2px #0095ff; - outline: none; + outline: 2px solid #0095ff; } `; const Text = styled.span` - box-sizing: border-box; - ${sharedStyles} border: 1px solid transparent; - &:hover { - border-bottom: 1px solid #000000; + border-bottom: 1px solid #000; } `; diff --git a/lib/src/breadcrumbs/dropdownTheme.ts b/lib/src/breadcrumbs/dropdownTheme.ts new file mode 100644 index 000000000..bca9ec62f --- /dev/null +++ b/lib/src/breadcrumbs/dropdownTheme.ts @@ -0,0 +1,52 @@ +export default { + dropdown: { + // Breadcrumbs tokens + buttonIconSize: "16px", + buttonPaddingTop: "4px", + buttonPaddingBottom: "4px", + buttonPaddingLeft: "4px", + buttonPaddingRight: "4px", + buttonHeight: "24px", + buttonBorderRadius: "2px", + buttonBorderColor: "transparent", + optionFontSize: "14px", + optionPaddingTop: "0px", + optionPaddingBottom: "0px", + optionPaddingLeft: "16px", + optionPaddingRight: "16px", + + // Dropdown tokens + buttonBackgroundColor: "#ffffff", + hoverButtonBackgroundColor: "#f2f2f2", + activeButtonBackgroundColor: "#cccccc", + buttonFontFamily: "Open Sans, sans-serif", + buttonFontSize: "1rem", + buttonFontStyle: "normal", + buttonFontWeight: "400", + buttonFontColor: "#000000", + buttonIconSpacing: "10px", + buttonIconColor: "#000000", + disabledColor: "#999999", + disabledButtonBackgroundColor: "transparent", + disabledBorderColor: "transparent", + optionBackgroundColor: "#ffffff", + hoverOptionBackgroundColor: "#f2f2f2", + activeOptionBackgroundColor: "#cccccc", + optionFontFamily: "Open Sans, sans-serif", + optionFontStyle: "normal", + optionFontWeight: "400", + optionFontColor: "#000000", + optionIconSize: "20px", + optionIconSpacing: "10px", + optionIconColor: "#000000", + caretIconSize: "24px", + caretIconColor: "#000000", + caretIconSpacing: "12px", + borderRadius: "4px", + borderStyle: "none", + borderThickness: "0px", + borderColor: "transparent", + scrollBarThumbColor: "#666666", + scrollBarTrackColor: "#cccccc", + }, +}; diff --git a/lib/src/breadcrumbs/types.ts b/lib/src/breadcrumbs/types.ts index 91f69dfc1..9c3ceff72 100644 --- a/lib/src/breadcrumbs/types.ts +++ b/lib/src/breadcrumbs/types.ts @@ -1,14 +1,11 @@ -type SVG = React.ReactNode & React.SVGProps<SVGSVGElement>; - -export type Item = { +export type ItemType = { label: string; href?: string; }; type Props = { ariaLabel?: string; - collapsedIcon?: SVG; - items: Array<Item>; + items: Array<ItemType>; itemsBeforeCollapse?: number; showRoot?: boolean; }; diff --git a/lib/src/common/variables.ts b/lib/src/common/variables.ts index d24ea1a33..6834a3f38 100644 --- a/lib/src/common/variables.ts +++ b/lib/src/common/variables.ts @@ -88,16 +88,6 @@ export const componentTokens = { overlayColor: CoreTokens.color_grey_800_a, }, breadcrumbs: { - actionBackgroundColor: CoreTokens.color_transparent, - hoverActionBackgroundColor: CoreTokens.color_grey_100, - activeActionBackgroundColor: CoreTokens.color_grey_300, - disabledActionBackgroundColor: CoreTokens.color_transparent, - focusActionBorderColor: CoreTokens.color_blue_600, - actionIconColor: CoreTokens.color_black, - disabledActionIconColor: CoreTokens.color_grey_500, - hoverActionIconColor: CoreTokens.color_black, - focusActionIconColor: CoreTokens.color_black, - activeActionIconColor: CoreTokens.color_black, }, box: { backgroundColor: CoreTokens.color_white, @@ -296,9 +286,14 @@ export const componentTokens = { buttonPaddingBottom: "0px", buttonPaddingLeft: "16px", buttonPaddingRight: "16px", + buttonHeight: "40px", + buttonBorderRadius: "4px", + buttonBorderStyle: CoreTokens.border_none, + buttonBorderThickness: CoreTokens.border_width_0, + buttonBorderColor: CoreTokens.color_transparent, disabledColor: CoreTokens.color_grey_500, disabledButtonBackgroundColor: CoreTokens.color_transparent, - disabledBorderColor: CoreTokens.color_transparent, + disabledButtonBorderColor: CoreTokens.color_transparent, optionBackgroundColor: CoreTokens.color_white, hoverOptionBackgroundColor: CoreTokens.color_grey_100, activeOptionBackgroundColor: CoreTokens.color_grey_300, @@ -317,10 +312,6 @@ export const componentTokens = { 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, @@ -1296,13 +1287,13 @@ export type OpinionatedTheme = { }; export const spaces = { - xxsmall: "6px", - xsmall: "16px", - small: "24px", - medium: "36px", - large: "48px", - xlarge: "64px", - xxlarge: "100px", + xxsmall: "4px", + xsmall: "8px", + small: "12px", + medium: "16px", + large: "24px", + xlarge: "32px", + xxlarge: "48px", }; export const responsiveSizes = { diff --git a/lib/src/dropdown/Dropdown.tsx b/lib/src/dropdown/Dropdown.tsx index a3b75e053..339d3480e 100644 --- a/lib/src/dropdown/Dropdown.tsx +++ b/lib/src/dropdown/Dropdown.tsx @@ -242,10 +242,7 @@ const calculateWidth = (margin, size) => : sizes[size]; const DropdownContainer = styled.div<{ margin: DropdownPropsType["margin"]; size: DropdownPropsType["size"] }>` - display: inline-block; width: ${(props) => calculateWidth(props.margin, props.size)}; - text-overflow: ellipsis; - overflow: hidden; margin: ${(props) => (props.margin && typeof props.margin !== "object" ? spaces[props.margin] : "0px")}; margin-top: ${(props) => props.margin && typeof props.margin === "object" && props.margin.top ? spaces[props.margin.top] : ""}; @@ -267,12 +264,12 @@ const DropdownTrigger = styled.button<{ align-items: center; gap: ${(props) => props.theme.caretIconSpacing}; width: 100%; - min-height: 40px; + height: ${(props) => props.theme.buttonHeight}; min-width: ${(props) => (props.label === "" ? "0px" : calculateWidth(props.margin, props.size))}; - border-radius: ${(props) => props.theme.borderRadius}; - border-width: ${(props) => props.theme.borderThickness}; - border-style: ${(props) => props.theme.borderStyle}; - border-color: ${(props) => (props.disabled ? props.theme.disabledBorderColor : props.theme.borderColor)}; + border-radius: ${(props) => props.theme.buttonBorderRadius}; + border-width: ${(props) => props.theme.buttonBorderThickness}; + border-style: ${(props) => props.theme.buttonBorderStyle}; + border-color: ${(props) => (props.disabled ? props.theme.disabledButtonBorderColor : props.theme.buttonBorderColor)}; padding-top: ${(props) => props.theme.buttonPaddingTop}; padding-bottom: ${(props) => props.theme.buttonPaddingBottom}; padding-left: ${(props) => props.theme.buttonPaddingLeft}; @@ -286,8 +283,7 @@ const DropdownTrigger = styled.button<{ !props.disabled && ` &:focus { - outline: ${props.theme.focusColor} solid 2px; - outline-offset: -2px; + outline: 2px solid ${props.theme.focusColor}; } &:hover { background-color: ${props.theme.hoverButtonBackgroundColor}; From d219534f6bfebdb8fe48cf68bf10ddc89dbeda2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Thu, 15 Feb 2024 11:09:19 +0100 Subject: [PATCH 03/18] Adding tooltip to the Breadcrumbs --- lib/src/breadcrumbs/Breadcrumbs.tsx | 77 +++++++++++++++-------------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/lib/src/breadcrumbs/Breadcrumbs.tsx b/lib/src/breadcrumbs/Breadcrumbs.tsx index fac860711..0dd2b568d 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect, useRef } from "react"; import styled, { ThemeProvider } from "styled-components"; import useTheme from "../useTheme"; import BreadcrumbsProps, { ItemType } from "./types"; @@ -28,54 +28,56 @@ const mapItems = (item: ItemType, index: number, { length }) => { ); }; -const mapMultipleItems = (items: BreadcrumbsProps["items"], showRoot: BreadcrumbsProps["showRoot"]) => { - const first = items[0]; - const last = items[items.length - 1]; - - return ( - <> - {showRoot && ( - <ListItem key={0}> - <Item href={first.href}> - <Text>{first.label}</Text> - </Item> - </ListItem> - )} - <ListItem key={1}> - <HalstackProvider advancedTheme={dropdownTheme}> - <DxcDropdown - icon={defaultCollapsedIcon} - options={items.slice(showRoot ? 1 : 0, -1).map(({ label, href }) => ({ label, value: href }))} - onSelectOption={(value) => { - window.location.href = value; - }} - caretHidden - margin={showRoot && { left: "small" }} - /> - </HalstackProvider> - </ListItem> - <ListItem key={2} aria-current="page" style={{ overflow: "hidden" }}> - <CurrentItem>{last.label}</CurrentItem> - </ListItem> - </> - ); -}; - const DxcBreadcrumbs = ({ ariaLabel = "Breadcrumbs", items, itemsBeforeCollapse = 4, showRoot = true, }: BreadcrumbsProps) => { + const currentItemRef = useRef<HTMLSpanElement>(null); const colorsTheme = useTheme(); + useEffect(() => { + if (currentItemRef?.current != null) { + if (currentItemRef?.current.scrollWidth > currentItemRef?.current.clientWidth) + currentItemRef.current.title = items[items.length - 1].label; + else currentItemRef.current.title = ""; + } + }, [items]); + return ( <ThemeProvider theme={colorsTheme.breadcrumbs}> <nav aria-label={ariaLabel}> <OrderedList> - {itemsBeforeCollapse >= 2 && items.length > itemsBeforeCollapse - ? mapMultipleItems(items, showRoot) - : items.map(mapItems)} + {itemsBeforeCollapse >= 2 && items.length > itemsBeforeCollapse ? ( + <> + {showRoot && ( + <ListItem key={0}> + <Item href={items[0].href}> + <Text>{items[0].label}</Text> + </Item> + </ListItem> + )} + <ListItem key={1}> + <HalstackProvider advancedTheme={dropdownTheme}> + <DxcDropdown + icon={defaultCollapsedIcon} + options={items.slice(showRoot ? 1 : 0, -1).map(({ label, href }) => ({ label, value: href }))} + onSelectOption={(value) => { + window.location.href = value; + }} + caretHidden + margin={showRoot && { left: "small" }} + /> + </HalstackProvider> + </ListItem> + <ListItem key={2} aria-current="page" style={{ overflow: "hidden" }}> + <CurrentItem ref={currentItemRef}>{items[items.length - 1].label}</CurrentItem> + </ListItem> + </> + ) : ( + items.map(mapItems) + )} </OrderedList> </nav> </ThemeProvider> @@ -118,6 +120,7 @@ const CurrentItem = styled.span` overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + cursor: default; `; const Item = styled.a` From 61c0262302d812b8849fa5f427578ec4243297ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Thu, 15 Feb 2024 13:05:04 +0100 Subject: [PATCH 04/18] Adding core tokens, stories & more --- .../components/ExampleContainer.tsx | 12 +- lib/src/breadcrumbs/Breadcrumbs.stories.tsx | 139 +++++++++++------- lib/src/breadcrumbs/Breadcrumbs.tsx | 35 ++--- 3 files changed, 117 insertions(+), 69 deletions(-) diff --git a/lib/.storybook/components/ExampleContainer.tsx b/lib/.storybook/components/ExampleContainer.tsx index 06327682a..ed873a6df 100644 --- a/lib/.storybook/components/ExampleContainer.tsx +++ b/lib/.storybook/components/ExampleContainer.tsx @@ -1,9 +1,19 @@ import React from "react"; import styled from "styled-components"; +type PseudoStates = + | "pseudo-active" + | "pseudo-focus" + | "pseudo-focus-visible" + | "pseudo-focus-within" + | "pseudo-hover" + | "pseudo-link" + | "pseudo-target" + | "pseudo-visited"; + type Props = { children?: React.ReactNode; - pseudoState?: string; + pseudoState?: PseudoStates; expanded?: boolean; }; diff --git a/lib/src/breadcrumbs/Breadcrumbs.stories.tsx b/lib/src/breadcrumbs/Breadcrumbs.stories.tsx index ec8b13664..450b197c4 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.stories.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.stories.tsx @@ -3,18 +3,14 @@ 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/testing-library"; export default { title: "Breadcrumbs", component: DxcBreadcrumbs, }; -const collapsedIcon = ( - <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"> - <path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h240l80 80h320q33 0 56.5 23.5T880-640v400q0 33-23.5 56.5T800-160H160Zm0-80h640v-400H447l-80-80H160v480Zm0 0v-480 480Z" /> - </svg> -); - const items = [ { label: "Home", @@ -38,9 +34,9 @@ const items = [ }, ]; -export const Chromatic = () => ( +const Breadcrumbs = () => ( <> - <Title title="Breadcrumbs" theme="light" level={3} /> + <Title title="Default" theme="light" level={3} /> <ExampleContainer> <DxcBreadcrumbs items={[ @@ -63,58 +59,53 @@ export const Chromatic = () => ( ]} /> </ExampleContainer> - <Title title="Home icon breadcrumbs" theme="light" level={3} /> - <ExampleContainer> - <DxcBreadcrumbs - items={[ - { - label: "Home", - href: "/", - }, - { - label: "User Menu", - href: "", - }, - ]} - /> - </ExampleContainer> - <Title title="Collapsed breadcrumbs" theme="light" level={3} /> + <Title title="Collapsed variant" theme="light" level={3} /> <ExampleContainer> <DxcBreadcrumbs items={items} /> </ExampleContainer> - <Title title="Collapsed breadcrumbs without root" theme="light" level={3} /> + <Title title="Collapsed variant without root" theme="light" level={3} /> <ExampleContainer> <DxcBreadcrumbs items={items} showRoot={false} /> </ExampleContainer> - <Title title="Collapsed breadcrumbs with custom collapsed icon" theme="light" level={3} /> - <ExampleContainer> - <DxcBreadcrumbs items={items} showRoot={false} /> + <Title title="Focus state" theme="light" level={3} /> + <ExampleContainer pseudoState="pseudo-focus"> + <DxcBreadcrumbs items={items} /> + </ExampleContainer> + <Title title="Hover state" theme="light" level={3} /> + <ExampleContainer pseudoState="pseudo-hover"> + <DxcBreadcrumbs items={items} /> + </ExampleContainer> + <Title title="Active state" theme="light" level={3} /> + <ExampleContainer pseudoState="pseudo-active"> + <DxcBreadcrumbs items={items} /> </ExampleContainer> - <Title title="Breadcrumbs collapse with three elements" theme="light" level={3} /> + <Title title="Truncation and text ellipsis with tooltip (only when collapsed)" theme="light" level={3} /> <ExampleContainer> - <DxcBreadcrumbs - items={[ - { - label: "Root", - href: "/", - }, - { - label: "Main folder", - href: "", - }, - { - label: "User", - href: "", - }, - { - label: "Test", - href: "", - }, - ]} - itemsBeforeCollapse={3} - /> + <DxcContainer width="200px"> + <DxcBreadcrumbs + items={[ + { + label: "Root", + href: "/", + }, + { + label: "Main folder", + href: "", + }, + { + label: "User", + href: "", + }, + { + label: "Very long label for the link", + href: "", + }, + ]} + itemsBeforeCollapse={3} + /> + </DxcContainer> </ExampleContainer> - <Title title="Truncation and text ellipsis (only when collapsed)" theme="light" level={3} /> + <Title title="Truncation, text ellipsis with tooltip and without root" theme="light" level={3} /> <ExampleContainer> <DxcContainer width="200px"> <DxcBreadcrumbs @@ -141,5 +132,51 @@ export const Chromatic = () => ( /> </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> + <Title title="Collapsed variant with dropdown menu opened" theme="light" level={3} /> + <ExampleContainer> + <DxcContainer height="200px"> + <DxcBreadcrumbs items={items} /> + </DxcContainer> + </ExampleContainer> </> ); + +export const Chromatic = Breadcrumbs.bind({}); +Chromatic.play = async ({ canvasElement }) => { + const canvas = within(canvasElement); + const dropdowns = canvas.getAllByRole("button"); + await userEvent.click(dropdowns[dropdowns.length - 1]); +}; diff --git a/lib/src/breadcrumbs/Breadcrumbs.tsx b/lib/src/breadcrumbs/Breadcrumbs.tsx index 0dd2b568d..deaff57a8 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.tsx @@ -5,6 +5,7 @@ import BreadcrumbsProps, { ItemType } from "./types"; import DxcDropdown from "../dropdown/Dropdown"; import { HalstackProvider } from "../HalstackContext"; import dropdownTheme from "./dropdownTheme"; +import CoreTokens from "../common/coreTokens"; const defaultCollapsedIcon = ( <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"> @@ -85,22 +86,22 @@ const DxcBreadcrumbs = ({ }; const OrderedList = styled.ol` - margin: 0; - padding-left: 0; + margin: ${CoreTokens.spacing_0}; + padding-left: ${CoreTokens.spacing_0}; display: flex; - gap: 0.75rem; align-items: center; + gap: ${CoreTokens.spacing_12}; list-style-type: none; > li:not(:first-child) { > a, > span { - margin-left: 0.75rem; + margin-left: ${CoreTokens.spacing_12}; } &::before { - margin: 0 0.125rem; + margin: 0 ${CoreTokens.spacing_2}; transform: rotate(15deg); - border-right: 2px solid #999; + border-right: ${CoreTokens.border_width_1} solid ${CoreTokens.color_grey_500}; height: 1rem; content: ""; } @@ -110,13 +111,13 @@ const OrderedList = styled.ol` const ListItem = styled.li` display: flex; align-items: center; - font-family: "Open Sans", sans-serif; - font-size: 0.875rem; - color: #000; + font-family: ${CoreTokens.type_sans}; + font-size: ${CoreTokens.type_scale_02}; + color: ${CoreTokens.color_black}; `; const CurrentItem = styled.span` - font-weight: 600; + font-weight: ${CoreTokens.type_semibold}; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -124,24 +125,24 @@ const CurrentItem = styled.span` `; const Item = styled.a` - border-radius: 2px; - padding: 0 2px; + border-radius: ${CoreTokens.border_radius_small}; + padding: 0 ${CoreTokens.spacing_2}; display: inline-flex; align-items: center; height: 24px; - color: #000; - text-decoration: none; + color: ${CoreTokens.color_black}; + text-decoration: ${CoreTokens.type_no_line}; cursor: pointer; &:focus { - outline: 2px solid #0095ff; + outline: ${CoreTokens.border_width_2} solid ${CoreTokens.color_blue_600}; } `; const Text = styled.span` - border: 1px solid transparent; + border: ${CoreTokens.border_width_1} solid ${CoreTokens.color_transparent}; &:hover { - border-bottom: 1px solid #000; + border-bottom-color: ${CoreTokens.color_black}; } `; From 925309c227b0797234aa0565a5867111a065faf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Thu, 15 Feb 2024 17:04:10 +0100 Subject: [PATCH 05/18] Breadcrumbs component tests --- lib/src/breadcrumbs/Breadcrumbs.test.tsx | 64 ++++++++++++++++++++++++ lib/src/breadcrumbs/Breadcrumbs.tsx | 14 +++--- 2 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 lib/src/breadcrumbs/Breadcrumbs.test.tsx diff --git a/lib/src/breadcrumbs/Breadcrumbs.test.tsx b/lib/src/breadcrumbs/Breadcrumbs.test.tsx new file mode 100644 index 000000000..f1751cbf4 --- /dev/null +++ b/lib/src/breadcrumbs/Breadcrumbs.test.tsx @@ -0,0 +1,64 @@ +import React from "react"; +import { render } from "@testing-library/react"; +import DxcBreadcrumbs from "./Breadcrumbs"; +import userEvent from "@testing-library/user-event"; + +global.ResizeObserver = class ResizeObserver { + observe() {} + unobserve() {} + disconnect() {} +}; + +const items = [ + { + label: "Home", + href: "/", + }, + { + label: "User Menu", + href: "", + }, + { + label: "Preferences", + href: "", + }, + { + label: "Dark Mode", + href: "", + }, +]; + +describe("Breadcrumbs component tests", () => { + test("Renders with correct aria accessibility attributes", () => { + const { getByText, getByRole } = render(<DxcBreadcrumbs items={items} />); + const breadcrumbs = getByRole("navigation"); + expect(breadcrumbs.getAttribute("aria-label")).toBe("Breadcrumbs"); + expect(getByText("Dark Mode").parentElement.getAttribute("aria-current")).toBe("page"); + }); + test("Collapsed variant renders all the items inside the dropdown menu except the root and the current page", async () => { + const { queryByText, getByText, getByRole } = render(<DxcBreadcrumbs items={items} itemsBeforeCollapse={3} />); + const dropdown = getByRole("button"); + expect(queryByText("User Menu")).toBeFalsy(); + expect(queryByText("Preferences")).toBeFalsy(); + await userEvent.click(dropdown); + expect(getByText("User Menu")).toBeTruthy(); + expect(getByText("Preferences")).toBeTruthy(); + }); + test("Collapsed variant, with show root set to false, renders all the items inside the dropdown menu except the current page", async () => { + const { queryByText, getByText, getByRole } = render(<DxcBreadcrumbs items={items} itemsBeforeCollapse={3} showRoot={false} />); + const dropdown = getByRole("button"); + expect(queryByText("Home")).toBeFalsy(); + expect(queryByText("User Menu")).toBeFalsy(); + expect(queryByText("Preferences")).toBeFalsy(); + await userEvent.click(dropdown); + expect(getByText("Home")).toBeTruthy(); + expect(getByText("User Menu")).toBeTruthy(); + expect(getByText("Preferences")).toBeTruthy(); + }); + test("If itemsBeforeCollapse value is below two, ignores it and renders a collapsed variant", async () => { + const { getByText, getByRole } = render(<DxcBreadcrumbs items={items} itemsBeforeCollapse={-1} />); + expect(getByText("Home")).toBeTruthy(); + expect(getByRole("button")).toBeTruthy(); + expect(getByText("Dark Mode")).toBeTruthy(); + }); +}); diff --git a/lib/src/breadcrumbs/Breadcrumbs.tsx b/lib/src/breadcrumbs/Breadcrumbs.tsx index deaff57a8..b3519a85a 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.tsx @@ -29,6 +29,10 @@ const mapItems = (item: ItemType, index: number, { length }) => { ); }; +const onSelectOption = (value: string) => { + window.location.href = value; +}; + const DxcBreadcrumbs = ({ ariaLabel = "Breadcrumbs", items, @@ -50,7 +54,7 @@ const DxcBreadcrumbs = ({ <ThemeProvider theme={colorsTheme.breadcrumbs}> <nav aria-label={ariaLabel}> <OrderedList> - {itemsBeforeCollapse >= 2 && items.length > itemsBeforeCollapse ? ( + {items.length > Math.max(itemsBeforeCollapse, 2) ? ( <> {showRoot && ( <ListItem key={0}> @@ -64,9 +68,7 @@ const DxcBreadcrumbs = ({ <DxcDropdown icon={defaultCollapsedIcon} options={items.slice(showRoot ? 1 : 0, -1).map(({ label, href }) => ({ label, value: href }))} - onSelectOption={(value) => { - window.location.href = value; - }} + onSelectOption={onSelectOption} caretHidden margin={showRoot && { left: "small" }} /> @@ -99,7 +101,7 @@ const OrderedList = styled.ol` margin-left: ${CoreTokens.spacing_12}; } &::before { - margin: 0 ${CoreTokens.spacing_2}; + margin: ${CoreTokens.spacing_0} ${CoreTokens.spacing_2}; transform: rotate(15deg); border-right: ${CoreTokens.border_width_1} solid ${CoreTokens.color_grey_500}; height: 1rem; @@ -126,7 +128,7 @@ const CurrentItem = styled.span` const Item = styled.a` border-radius: ${CoreTokens.border_radius_small}; - padding: 0 ${CoreTokens.spacing_2}; + padding: ${CoreTokens.spacing_0} ${CoreTokens.spacing_2}; display: inline-flex; align-items: center; height: 24px; From 5bc19e72050b1a7295736c80d815e32f776e1e3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Fri, 16 Feb 2024 10:28:26 +0100 Subject: [PATCH 06/18] Adding jest types --- lib/package-lock.json | 37 +++++++++++++++++++ lib/package.json | 1 + .../components/link/code/examples/nextLink.ts | 18 +++++---- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/lib/package-lock.json b/lib/package-lock.json index 8cbc852c4..1c594bc76 100644 --- a/lib/package-lock.json +++ b/lib/package-lock.json @@ -34,6 +34,7 @@ "@testing-library/react": "^13.0.0", "@testing-library/user-event": "^13.0.0", "@types/color": "^3.0.3", + "@types/jest": "^29.5.12", "@types/react": "^18.0.18", "@types/styled-components": "5.1.29", "@types/uuid": "^9.0.6", @@ -8639,6 +8640,42 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/jest": { + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/jest/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@types/jest/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@types/jsdom": { "version": "20.0.1", "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", diff --git a/lib/package.json b/lib/package.json index 59bf9d700..bed88bef0 100644 --- a/lib/package.json +++ b/lib/package.json @@ -51,6 +51,7 @@ "@testing-library/react": "^13.0.0", "@testing-library/user-event": "^13.0.0", "@types/color": "^3.0.3", + "@types/jest": "^29.5.12", "@types/react": "^18.0.18", "@types/styled-components": "5.1.29", "@types/uuid": "^9.0.6", diff --git a/website/screens/components/link/code/examples/nextLink.ts b/website/screens/components/link/code/examples/nextLink.ts index b67f514f8..48b41b6a5 100644 --- a/website/screens/components/link/code/examples/nextLink.ts +++ b/website/screens/components/link/code/examples/nextLink.ts @@ -3,18 +3,20 @@ import Link from "next/link"; import React from "react"; const code = `() => { - const CustomLink = React.forwardRef(({ onClick, href, children, ...other }, ref) => { - return ( - <DxcLink {...other} href={href} onClick={onClick} ref={ref}> - {children} - </DxcLink> - ); - }); + const CustomLink = React.forwardRef( + ({ onClick, href, children, ...other }, ref) => { + return ( + <DxcLink {...other} href={href} onClick={onClick} ref={ref}> + {children} + </DxcLink> + ); + } + ); return ( <DxcInset space="2rem"> This is a text with a <Link href="/components/link" passHref legacyBehavior> - <CustomLink> next link</CustomLink> + <CustomLink>next</CustomLink> </Link>{" "} link. </DxcInset> From 9b80c6d2c5fd544e93b6d2135136a360276c778f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Thu, 14 Mar 2024 16:54:51 +0100 Subject: [PATCH 07/18] Breadcrumbs compatibility with other library links --- lib/src/breadcrumbs/Breadcrumbs.tsx | 106 +++++---------------------- lib/src/breadcrumbs/Item.tsx | 72 ++++++++++++++++++ lib/src/breadcrumbs/dropdownTheme.ts | 75 +++++++++---------- lib/src/breadcrumbs/types.ts | 11 ++- lib/src/common/variables.ts | 50 ++++++------- 5 files changed, 161 insertions(+), 153 deletions(-) create mode 100644 lib/src/breadcrumbs/Item.tsx diff --git a/lib/src/breadcrumbs/Breadcrumbs.tsx b/lib/src/breadcrumbs/Breadcrumbs.tsx index b3519a85a..1fb2754f1 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.tsx @@ -1,33 +1,14 @@ -import React, { useEffect, useRef } from "react"; +import React from "react"; import styled, { ThemeProvider } from "styled-components"; import useTheme from "../useTheme"; -import BreadcrumbsProps, { ItemType } from "./types"; +import BreadcrumbsProps from "./types"; import DxcDropdown from "../dropdown/Dropdown"; import { HalstackProvider } from "../HalstackContext"; import dropdownTheme from "./dropdownTheme"; import CoreTokens from "../common/coreTokens"; - -const defaultCollapsedIcon = ( - <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"> - <path d="M240-400q-33 0-56.5-23.5T160-480q0-33 23.5-56.5T240-560q33 0 56.5 23.5T320-480q0 33-23.5 56.5T240-400Zm240 0q-33 0-56.5-23.5T400-480q0-33 23.5-56.5T480-560q33 0 56.5 23.5T560-480q0 33-23.5 56.5T480-400Zm240 0q-33 0-56.5-23.5T640-480q0-33 23.5-56.5T720-560q33 0 56.5 23.5T800-480q0 33-23.5 56.5T720-400Z" /> - </svg> -); - -const mapItems = (item: ItemType, index: number, { length }) => { - const isLast = index === length - 1; - - return ( - <ListItem key={index} aria-current={isLast ? "page" : undefined}> - {isLast ? ( - <CurrentItem>{item.label}</CurrentItem> - ) : ( - <Item href={item.href}> - <Text>{item.label}</Text> - </Item> - )} - </ListItem> - ); -}; +import DxcIcon from "../icon/Icon"; +import Item from "./Item"; +import { DxcFlex } from "../main"; const onSelectOption = (value: string) => { window.location.href = value; @@ -35,51 +16,36 @@ const onSelectOption = (value: string) => { const DxcBreadcrumbs = ({ ariaLabel = "Breadcrumbs", + children, items, itemsBeforeCollapse = 4, showRoot = true, }: BreadcrumbsProps) => { - const currentItemRef = useRef<HTMLSpanElement>(null); const colorsTheme = useTheme(); - - useEffect(() => { - if (currentItemRef?.current != null) { - if (currentItemRef?.current.scrollWidth > currentItemRef?.current.clientWidth) - currentItemRef.current.title = items[items.length - 1].label; - else currentItemRef.current.title = ""; - } - }, [items]); - return ( <ThemeProvider theme={colorsTheme.breadcrumbs}> <nav aria-label={ariaLabel}> <OrderedList> - {items.length > Math.max(itemsBeforeCollapse, 2) ? ( + {children ?? items.length > Math.max(itemsBeforeCollapse, 2) ? ( <> - {showRoot && ( - <ListItem key={0}> - <Item href={items[0].href}> - <Text>{items[0].label}</Text> - </Item> - </ListItem> - )} - <ListItem key={1}> + {showRoot && <Item href={items[0].href} key={0} label={items[0].label} />} + <DxcFlex alignItems="center" as="li" key={1}> <HalstackProvider advancedTheme={dropdownTheme}> <DxcDropdown - icon={defaultCollapsedIcon} - options={items.slice(showRoot ? 1 : 0, -1).map(({ label, href }) => ({ label, value: href }))} - onSelectOption={onSelectOption} caretHidden + icon={<DxcIcon icon="more_horiz" />} margin={showRoot && { left: "small" }} + onSelectOption={onSelectOption} + options={items.slice(showRoot ? 1 : 0, -1).map(({ label, href }) => ({ label, value: href }))} /> </HalstackProvider> - </ListItem> - <ListItem key={2} aria-current="page" style={{ overflow: "hidden" }}> - <CurrentItem ref={currentItemRef}>{items[items.length - 1].label}</CurrentItem> - </ListItem> + </DxcFlex> + <Item isCurrentPage key={2} label={items[items.length - 1].label} /> </> ) : ( - items.map(mapItems) + items.map((item, index, { length }) => ( + <Item href={item.href} isCurrentPage={index === length - 1} key={index} label={item.label} /> + )) )} </OrderedList> </nav> @@ -110,42 +76,6 @@ const OrderedList = styled.ol` } `; -const ListItem = styled.li` - display: flex; - align-items: center; - font-family: ${CoreTokens.type_sans}; - font-size: ${CoreTokens.type_scale_02}; - color: ${CoreTokens.color_black}; -`; - -const CurrentItem = styled.span` - font-weight: ${CoreTokens.type_semibold}; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - cursor: default; -`; - -const Item = styled.a` - border-radius: ${CoreTokens.border_radius_small}; - padding: ${CoreTokens.spacing_0} ${CoreTokens.spacing_2}; - display: inline-flex; - align-items: center; - height: 24px; - color: ${CoreTokens.color_black}; - text-decoration: ${CoreTokens.type_no_line}; - cursor: pointer; - - &:focus { - outline: ${CoreTokens.border_width_2} solid ${CoreTokens.color_blue_600}; - } -`; - -const Text = styled.span` - border: ${CoreTokens.border_width_1} solid ${CoreTokens.color_transparent}; - &:hover { - border-bottom-color: ${CoreTokens.color_black}; - } -`; +DxcBreadcrumbs.Item = Item; export default DxcBreadcrumbs; diff --git a/lib/src/breadcrumbs/Item.tsx b/lib/src/breadcrumbs/Item.tsx new file mode 100644 index 000000000..a4def8bfc --- /dev/null +++ b/lib/src/breadcrumbs/Item.tsx @@ -0,0 +1,72 @@ +import React from "react"; +import styled from "styled-components"; +import CoreTokens from "../common/coreTokens"; +import { ItemPropsType } from "./types"; +import { useRef } from "react"; + +const Item = ({ isCurrentPage = false, href, label, ...otherProps }: ItemPropsType) => { + const currentItemRef = useRef<HTMLSpanElement>(null); + + const handleOnMouseEnter = (event: React.MouseEvent<HTMLLinkElement>) => { + 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} {...otherProps}> + <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}; + ${({ isCurrentPage }) => isCurrentPage && "overflow: hidden;"} +`; + +const CurrentPage = styled.span` + font-weight: ${CoreTokens.type_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}; + display: inline-flex; + align-items: center; + height: 24px; + color: ${CoreTokens.color_black}; + text-decoration: ${CoreTokens.type_no_line}; + cursor: pointer; + + &:focus { + outline: ${CoreTokens.border_width_2} solid ${CoreTokens.color_blue_600}; + } +`; + +const Text = styled.span` + border: ${CoreTokens.border_width_1} solid ${CoreTokens.color_transparent}; + &:hover { + border-bottom-color: ${CoreTokens.color_black}; + } +`; + +export default Item; diff --git a/lib/src/breadcrumbs/dropdownTheme.ts b/lib/src/breadcrumbs/dropdownTheme.ts index bca9ec62f..18e4bfbdc 100644 --- a/lib/src/breadcrumbs/dropdownTheme.ts +++ b/lib/src/breadcrumbs/dropdownTheme.ts @@ -1,52 +1,53 @@ +import CoreTokens from "../common/coreTokens"; + export default { dropdown: { // Breadcrumbs tokens - buttonIconSize: "16px", - buttonPaddingTop: "4px", - buttonPaddingBottom: "4px", - buttonPaddingLeft: "4px", - buttonPaddingRight: "4px", + 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: "transparent", + buttonBorderColor: CoreTokens.color_transparent, optionFontSize: "14px", - optionPaddingTop: "0px", - optionPaddingBottom: "0px", - optionPaddingLeft: "16px", - optionPaddingRight: "16px", + optionPaddingTop: CoreTokens.spacing_0, + optionPaddingBottom: CoreTokens.spacing_0, + optionPaddingLeft: CoreTokens.spacing_16, + optionPaddingRight: CoreTokens.spacing_16, // Dropdown tokens - buttonBackgroundColor: "#ffffff", - hoverButtonBackgroundColor: "#f2f2f2", - activeButtonBackgroundColor: "#cccccc", - buttonFontFamily: "Open Sans, sans-serif", - buttonFontSize: "1rem", - buttonFontStyle: "normal", - buttonFontWeight: "400", - buttonFontColor: "#000000", + 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: "#000000", - disabledColor: "#999999", - disabledButtonBackgroundColor: "transparent", - disabledBorderColor: "transparent", - optionBackgroundColor: "#ffffff", - hoverOptionBackgroundColor: "#f2f2f2", - activeOptionBackgroundColor: "#cccccc", - optionFontFamily: "Open Sans, sans-serif", - optionFontStyle: "normal", - optionFontWeight: "400", - optionFontColor: "#000000", + 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: "#000000", + optionIconColor: CoreTokens.color_black, caretIconSize: "24px", - caretIconColor: "#000000", + caretIconColor: CoreTokens.color_black, caretIconSpacing: "12px", - borderRadius: "4px", - borderStyle: "none", - borderThickness: "0px", - borderColor: "transparent", - scrollBarThumbColor: "#666666", - scrollBarTrackColor: "#cccccc", + scrollBarThumbColor: CoreTokens.color_grey_700, + scrollBarTrackColor: CoreTokens.color_grey_300, + focusColor: CoreTokens.color_blue_600, }, }; diff --git a/lib/src/breadcrumbs/types.ts b/lib/src/breadcrumbs/types.ts index 9c3ceff72..f39eedfc3 100644 --- a/lib/src/breadcrumbs/types.ts +++ b/lib/src/breadcrumbs/types.ts @@ -1,13 +1,18 @@ -export type ItemType = { - label: string; +type Item = { href?: string; + label: string; }; type Props = { ariaLabel?: string; - items: Array<ItemType>; + children?: React.ReactNode; + items: Array<Item>; itemsBeforeCollapse?: number; showRoot?: boolean; }; +export type ItemPropsType = Item & { + isCurrentPage?: boolean; +}; + export default Props; diff --git a/lib/src/common/variables.ts b/lib/src/common/variables.ts index 1d9996a8c..b611cf85e 100644 --- a/lib/src/common/variables.ts +++ b/lib/src/common/variables.ts @@ -15,16 +15,16 @@ export const componentTokens = { disabledAssistiveTextFontColor: CoreTokens.color_grey_500, assistiveTextMinWidth: "100px", assistiveTextPaddingRight: "24px", - assistiveTextPaddingLeft: "0px", + assistiveTextPaddingLeft: CoreTokens.spacing_0, titleLabelFontFamily: CoreTokens.type_sans, titleLabelFontSize: CoreTokens.type_scale_03, titleLabelFontWeight: CoreTokens.type_regular, titleLabelFontStyle: CoreTokens.type_normal, titleLabelFontColor: CoreTokens.color_black, disabledTitleLabelFontColor: CoreTokens.color_grey_500, - titleLabelPaddingTop: "0px", - titleLabelPaddingBottom: "0px", - titleLabelPaddingLeft: "0px", + titleLabelPaddingTop: CoreTokens.spacing_0, + titleLabelPaddingBottom: CoreTokens.spacing_0, + titleLabelPaddingLeft: CoreTokens.spacing_0, titleLabelPaddingRight: "16px", focusBorderColor: CoreTokens.color_blue_600, focusBorderStyle: CoreTokens.border_solid, @@ -37,7 +37,7 @@ export const componentTokens = { iconColor: CoreTokens.color_purple_700, disabledIconColor: CoreTokens.color_grey_500, iconSize: "24px", - iconMarginLeft: "0px", + iconMarginLeft: CoreTokens.spacing_0, iconMarginRight: "12px", accordionGroupSeparatorBorderColor: CoreTokens.color_grey_200_a, accordionGroupSeparatorBorderThickness: "1px", @@ -51,17 +51,17 @@ export const componentTokens = { titleFontStyle: CoreTokens.type_normal, titleFontWeight: CoreTokens.type_bold, titleTextTransform: CoreTokens.type_uppercase, - titlePaddingRight: "0px", - titlePaddingLeft: "0px", + titlePaddingRight: CoreTokens.spacing_0, + titlePaddingLeft: CoreTokens.spacing_0, inlineTextFontFamily: CoreTokens.type_sans, inlineTextFontColor: CoreTokens.color_black, inlineTextFontSize: CoreTokens.type_scale_01, inlineTextFontStyle: CoreTokens.type_normal, inlineTextFontWeight: CoreTokens.type_regular, - inlineTextPaddingLeft: "0px", - inlineTextPaddingRight: "0px", - contentPaddingLeft: "0px", - contentPaddingRight: "0px", + inlineTextPaddingLeft: CoreTokens.spacing_0, + inlineTextPaddingRight: CoreTokens.spacing_0, + contentPaddingLeft: CoreTokens.spacing_0, + contentPaddingRight: CoreTokens.spacing_0, contentPaddingTop: "20px", contentPaddingBottom: "30px", borderRadius: "4px", @@ -72,8 +72,8 @@ export const componentTokens = { warningBorderColor: CoreTokens.color_yellow_700, errorBorderColor: CoreTokens.color_red_700, iconSize: "24px", - iconPaddingLeft: "0px", - iconPaddingRight: "0px", + iconPaddingLeft: CoreTokens.spacing_0, + iconPaddingRight: CoreTokens.spacing_0, infoIconColor: CoreTokens.color_blue_800, successIconColor: CoreTokens.color_green_700, warningIconColor: CoreTokens.color_yellow_700, @@ -208,8 +208,8 @@ export const componentTokens = { borderStyle: CoreTokens.border_solid, contentPaddingLeft: "16px", contentPaddingRight: "16px", - contentPaddingTop: "0px", - contentPaddingBottom: "0px", + contentPaddingTop: CoreTokens.spacing_0, + contentPaddingBottom: CoreTokens.spacing_0, iconSize: "24px", iconSpacing: "8px", iconColor: CoreTokens.color_grey_800, @@ -282,8 +282,8 @@ export const componentTokens = { buttonIconSize: "20px", buttonIconSpacing: "10px", buttonIconColor: CoreTokens.color_black, - buttonPaddingTop: "0px", - buttonPaddingBottom: "0px", + buttonPaddingTop: CoreTokens.spacing_0, + buttonPaddingBottom: CoreTokens.spacing_0, buttonPaddingLeft: "16px", buttonPaddingRight: "16px", buttonHeight: "40px", @@ -417,8 +417,8 @@ export const componentTokens = { overlayColor: CoreTokens.color_grey_800_a, overlayOpacity: "0.7", overlayZindex: "1600", - paddingTop: "0px", - paddingBottom: "0px", + paddingTop: CoreTokens.spacing_0, + paddingBottom: CoreTokens.spacing_0, paddingRight: "24px", paddingLeft: "24px", underlinedColor: CoreTokens.color_black, @@ -479,7 +479,7 @@ export const componentTokens = { fontWeight: CoreTokens.type_regular, iconSize: "16px", iconSpacing: "4px", - underlineSpacing: "0px", + underlineSpacing: CoreTokens.spacing_0, underlineStyle: CoreTokens.border_solid, underlineThickness: "1px", disabledFontColor: CoreTokens.color_grey_500, @@ -522,12 +522,12 @@ export const componentTokens = { horizontalPadding: "2rem", marginRight: "40px", marginLeft: "20px", - itemsPerPageSelectorMarginLeft: "0px", + itemsPerPageSelectorMarginLeft: CoreTokens.spacing_0, itemsPerPageSelectorMarginRight: "0.5rem", pageSelectorMarginRight: "30px", - pageSelectorMarginLeft: "0px", + pageSelectorMarginLeft: CoreTokens.spacing_0, totalItemsContainerMarginRight: "2.5rem", - totalItemsContainerMarginLeft: "0px", + totalItemsContainerMarginLeft: CoreTokens.spacing_0, }, paragraph: { fontColor: CoreTokens.color_black, @@ -924,8 +924,8 @@ export const componentTokens = { fontSize: CoreTokens.type_scale_02, fontStyle: CoreTokens.type_normal, fontWeight: CoreTokens.type_regular, - labelPaddingTop: "0px", - labelPaddingBottom: "0px", + labelPaddingTop: CoreTokens.spacing_0, + labelPaddingBottom: CoreTokens.spacing_0, labelPaddingLeft: "16px", labelPaddingRight: "16px", height: "40px", From 3ed3f673eda61beb81542aa6439406995f72d0ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Fri, 15 Mar 2024 11:50:52 +0100 Subject: [PATCH 08/18] Some fixes to the breadcrumbs --- lib/src/breadcrumbs/Breadcrumbs.tsx | 43 +++++++++++++++-------------- lib/src/breadcrumbs/types.ts | 18 ++++++++++-- lib/src/main.ts | 2 ++ 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/lib/src/breadcrumbs/Breadcrumbs.tsx b/lib/src/breadcrumbs/Breadcrumbs.tsx index 1fb2754f1..a5ea9a1c8 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.tsx @@ -26,27 +26,28 @@ const DxcBreadcrumbs = ({ <ThemeProvider theme={colorsTheme.breadcrumbs}> <nav aria-label={ariaLabel}> <OrderedList> - {children ?? items.length > Math.max(itemsBeforeCollapse, 2) ? ( - <> - {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" }} - onSelectOption={onSelectOption} - options={items.slice(showRoot ? 1 : 0, -1).map(({ label, href }) => ({ label, value: href }))} - /> - </HalstackProvider> - </DxcFlex> - <Item isCurrentPage key={2} label={items[items.length - 1].label} /> - </> - ) : ( - items.map((item, index, { length }) => ( - <Item href={item.href} isCurrentPage={index === length - 1} key={index} label={item.label} /> - )) - )} + {children ?? + (items.length > Math.max(itemsBeforeCollapse, 2) ? ( + <> + {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" }} + onSelectOption={onSelectOption} + options={items.slice(showRoot ? 1 : 0, -1).map(({ label, href }) => ({ label, value: href }))} + /> + </HalstackProvider> + </DxcFlex> + <Item isCurrentPage key={2} label={items[items.length - 1].label} /> + </> + ) : ( + items.map((item, index, { length }) => ( + <Item href={item.href} isCurrentPage={index === length - 1} key={index} label={item.label} /> + )) + ))} </OrderedList> </nav> </ThemeProvider> diff --git a/lib/src/breadcrumbs/types.ts b/lib/src/breadcrumbs/types.ts index f39eedfc3..6317274f6 100644 --- a/lib/src/breadcrumbs/types.ts +++ b/lib/src/breadcrumbs/types.ts @@ -2,14 +2,26 @@ type Item = { href?: string; label: string; }; - -type Props = { +type CommonProps = { + ariaLabel?: string; + itemsBeforeCollapse?: number; + showRoot?: boolean; +}; +type PropsWithItems = CommonProps & { ariaLabel?: string; - children?: React.ReactNode; + children?: never; items: Array<Item>; itemsBeforeCollapse?: number; showRoot?: boolean; }; +type PropsWithChildren = CommonProps & { + ariaLabel?: string; + children: React.ReactNode; + items?: never; + itemsBeforeCollapse?: number; + showRoot?: boolean; +}; +type Props = PropsWithItems | PropsWithChildren; export type ItemPropsType = Item & { isCurrentPage?: boolean; diff --git a/lib/src/main.ts b/lib/src/main.ts index 39f9a446e..53e8538d3 100644 --- a/lib/src/main.ts +++ b/lib/src/main.ts @@ -45,6 +45,7 @@ import DxcBadge from "./badge/Badge"; import DxcStatusLight from "./status-light/StatusLight"; import DxcContextualMenu from "./contextual-menu/ContextualMenu"; import DxcDivider from "./divider/Divider"; +import DxcBreadcrumbs from "./breadcrumbs/Breadcrumbs"; import HalstackContext, { HalstackProvider, HalstackLanguageContext } from "./HalstackContext"; @@ -99,4 +100,5 @@ export { DxcStatusLight, DxcContextualMenu, DxcDivider, + DxcBreadcrumbs, }; From 93b2f6c0127c16f146a2e57f882200c976f22529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:46:58 +0100 Subject: [PATCH 09/18] Update on the breadcrumbs API --- lib/src/breadcrumbs/Breadcrumbs.tsx | 60 +++++++++++++++-------------- lib/src/breadcrumbs/Item.tsx | 13 +++++-- lib/src/breadcrumbs/types.ts | 17 +------- 3 files changed, 43 insertions(+), 47 deletions(-) diff --git a/lib/src/breadcrumbs/Breadcrumbs.tsx b/lib/src/breadcrumbs/Breadcrumbs.tsx index a5ea9a1c8..1e5e8cefc 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.tsx @@ -10,13 +10,8 @@ import DxcIcon from "../icon/Icon"; import Item from "./Item"; import { DxcFlex } from "../main"; -const onSelectOption = (value: string) => { - window.location.href = value; -}; - const DxcBreadcrumbs = ({ ariaLabel = "Breadcrumbs", - children, items, itemsBeforeCollapse = 4, showRoot = true, @@ -26,28 +21,37 @@ const DxcBreadcrumbs = ({ <ThemeProvider theme={colorsTheme.breadcrumbs}> <nav aria-label={ariaLabel}> <OrderedList> - {children ?? - (items.length > Math.max(itemsBeforeCollapse, 2) ? ( - <> - {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" }} - onSelectOption={onSelectOption} - options={items.slice(showRoot ? 1 : 0, -1).map(({ label, href }) => ({ label, value: href }))} - /> - </HalstackProvider> - </DxcFlex> - <Item isCurrentPage key={2} label={items[items.length - 1].label} /> - </> - ) : ( - items.map((item, index, { length }) => ( - <Item href={item.href} isCurrentPage={index === length - 1} key={index} label={item.label} /> - )) - ))} + {items && items.length > Math.max(itemsBeforeCollapse, 2) ? ( + <> + {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" }} + onSelectOption={(href: string) => { + const itemOnClick = items.find((item) => item.href === href)?.onClick; + if (itemOnClick) itemOnClick(href); + else window.location.href = href; + }} + options={items.slice(showRoot ? 1 : 0, -1).map(({ label, href }) => ({ label, value: href }))} + /> + </HalstackProvider> + </DxcFlex> + <Item isCurrentPage key={2} label={items[items.length - 1].label} /> + </> + ) : ( + items.map((item, index, { length }) => ( + <Item + href={item.href} + isCurrentPage={index === length - 1} + key={index} + label={item.label} + onClick={item.onClick} + /> + )) + )} </OrderedList> </nav> </ThemeProvider> @@ -77,6 +81,4 @@ const OrderedList = styled.ol` } `; -DxcBreadcrumbs.Item = Item; - export default DxcBreadcrumbs; diff --git a/lib/src/breadcrumbs/Item.tsx b/lib/src/breadcrumbs/Item.tsx index a4def8bfc..e667ee684 100644 --- a/lib/src/breadcrumbs/Item.tsx +++ b/lib/src/breadcrumbs/Item.tsx @@ -4,10 +4,10 @@ import CoreTokens from "../common/coreTokens"; import { ItemPropsType } from "./types"; import { useRef } from "react"; -const Item = ({ isCurrentPage = false, href, label, ...otherProps }: ItemPropsType) => { +const Item = ({ isCurrentPage = false, href, label, onClick }: ItemPropsType) => { const currentItemRef = useRef<HTMLSpanElement>(null); - const handleOnMouseEnter = (event: React.MouseEvent<HTMLLinkElement>) => { + const handleOnMouseEnter = (event: React.MouseEvent<HTMLAnchorElement>) => { const labelContainer = event.currentTarget; const optionElement = currentItemRef?.current; @@ -15,6 +15,13 @@ const Item = ({ isCurrentPage = false, href, label, ...otherProps }: ItemPropsTy optionElement.title = label; }; + const handleOnClick = (event: React.MouseEvent<HTMLAnchorElement>) => { + if (onClick) { + event.preventDefault(); + onClick(href); + } + }; + return ( <ListItem aria-current={isCurrentPage ? "page" : undefined} isCurrentPage={isCurrentPage}> {isCurrentPage ? ( @@ -22,7 +29,7 @@ const Item = ({ isCurrentPage = false, href, label, ...otherProps }: ItemPropsTy {label} </CurrentPage> ) : ( - <Link href={href} {...otherProps}> + <Link href={href} onClick={handleOnClick}> <Text>{label}</Text> </Link> )} diff --git a/lib/src/breadcrumbs/types.ts b/lib/src/breadcrumbs/types.ts index 6317274f6..f22b352c7 100644 --- a/lib/src/breadcrumbs/types.ts +++ b/lib/src/breadcrumbs/types.ts @@ -1,27 +1,14 @@ type Item = { href?: string; label: string; + onClick?: (href: string) => void; }; -type CommonProps = { +type Props = { ariaLabel?: string; - itemsBeforeCollapse?: number; - showRoot?: boolean; -}; -type PropsWithItems = CommonProps & { - ariaLabel?: string; - children?: never; items: Array<Item>; itemsBeforeCollapse?: number; showRoot?: boolean; }; -type PropsWithChildren = CommonProps & { - ariaLabel?: string; - children: React.ReactNode; - items?: never; - itemsBeforeCollapse?: number; - showRoot?: boolean; -}; -type Props = PropsWithItems | PropsWithChildren; export type ItemPropsType = Item & { isCurrentPage?: boolean; From a263c56d937b170d979321397d25a451b297887c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Mon, 18 Mar 2024 17:22:16 +0100 Subject: [PATCH 10/18] More updates to the Breadcrumbs --- lib/src/breadcrumbs/Breadcrumbs.test.tsx | 39 ++++++++++++++++++++++-- lib/src/breadcrumbs/Breadcrumbs.tsx | 19 +++++++----- lib/src/breadcrumbs/Item.tsx | 1 - lib/src/breadcrumbs/types.ts | 3 +- lib/src/common/coreTokens.ts | 6 ++-- 5 files changed, 53 insertions(+), 15 deletions(-) diff --git a/lib/src/breadcrumbs/Breadcrumbs.test.tsx b/lib/src/breadcrumbs/Breadcrumbs.test.tsx index f1751cbf4..6474c931a 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.test.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.test.tsx @@ -30,9 +30,9 @@ const items = [ describe("Breadcrumbs component tests", () => { test("Renders with correct aria accessibility attributes", () => { - const { getByText, getByRole } = render(<DxcBreadcrumbs items={items} />); + const { getByText, getByRole } = render(<DxcBreadcrumbs items={items} ariaLabel="example" />); const breadcrumbs = getByRole("navigation"); - expect(breadcrumbs.getAttribute("aria-label")).toBe("Breadcrumbs"); + expect(breadcrumbs.getAttribute("aria-label")).toBe("example"); expect(getByText("Dark Mode").parentElement.getAttribute("aria-current")).toBe("page"); }); test("Collapsed variant renders all the items inside the dropdown menu except the root and the current page", async () => { @@ -45,7 +45,9 @@ describe("Breadcrumbs component tests", () => { expect(getByText("Preferences")).toBeTruthy(); }); test("Collapsed variant, with show root set to false, renders all the items inside the dropdown menu except the current page", async () => { - const { queryByText, getByText, getByRole } = render(<DxcBreadcrumbs items={items} itemsBeforeCollapse={3} showRoot={false} />); + const { queryByText, getByText, getByRole } = render( + <DxcBreadcrumbs items={items} itemsBeforeCollapse={3} showRoot={false} /> + ); const dropdown = getByRole("button"); expect(queryByText("Home")).toBeFalsy(); expect(queryByText("User Menu")).toBeFalsy(); @@ -61,4 +63,35 @@ describe("Breadcrumbs component tests", () => { expect(getByRole("button")).toBeTruthy(); expect(getByText("Dark Mode")).toBeTruthy(); }); + test("The onClick prop from an item is properly called", () => { + const onItemClick = jest.fn(); + const { getByText } = render( + <DxcBreadcrumbs + onItemClick={onItemClick} + items={[ + { label: "Home", href: "/" }, + { label: "Preferences", href: "/" }, + ]} + /> + ); + userEvent.click(getByText("Home")); + expect(onItemClick).toHaveBeenCalled(); + }); + test("The onClick prop from an item is properly called (collapsed)", async () => { + const onItemClick = jest.fn(); + const { getByText, getByRole } = render( + <DxcBreadcrumbs + onItemClick={onItemClick} + items={[ + { label: "Home", href: "/" }, + { label: "Preferences", href: "/" }, + { label: "Dark Mode", href: "/" }, + ]} + itemsBeforeCollapse={2} + /> + ); + await userEvent.click(getByRole("button")); + await userEvent.click(getByText("Preferences")); + expect(onItemClick).toHaveBeenCalled(); + }); }); diff --git a/lib/src/breadcrumbs/Breadcrumbs.tsx b/lib/src/breadcrumbs/Breadcrumbs.tsx index 1e5e8cefc..da3bcde2f 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useCallback } from "react"; import styled, { ThemeProvider } from "styled-components"; import useTheme from "../useTheme"; import BreadcrumbsProps from "./types"; @@ -14,9 +14,18 @@ const DxcBreadcrumbs = ({ ariaLabel = "Breadcrumbs", items, itemsBeforeCollapse = 4, + onItemClick, showRoot = true, }: BreadcrumbsProps) => { const colorsTheme = useTheme(); + const handleOnSelectOption = useCallback( + (href: string) => { + if (onItemClick) onItemClick(href); + else window.location.href = href; + }, + [items] + ); + return ( <ThemeProvider theme={colorsTheme.breadcrumbs}> <nav aria-label={ariaLabel}> @@ -30,11 +39,7 @@ const DxcBreadcrumbs = ({ caretHidden icon={<DxcIcon icon="more_horiz" />} margin={showRoot && { left: "small" }} - onSelectOption={(href: string) => { - const itemOnClick = items.find((item) => item.href === href)?.onClick; - if (itemOnClick) itemOnClick(href); - else window.location.href = href; - }} + onSelectOption={handleOnSelectOption} options={items.slice(showRoot ? 1 : 0, -1).map(({ label, href }) => ({ label, value: href }))} /> </HalstackProvider> @@ -48,7 +53,7 @@ const DxcBreadcrumbs = ({ isCurrentPage={index === length - 1} key={index} label={item.label} - onClick={item.onClick} + onClick={onItemClick} /> )) )} diff --git a/lib/src/breadcrumbs/Item.tsx b/lib/src/breadcrumbs/Item.tsx index e667ee684..f608967fa 100644 --- a/lib/src/breadcrumbs/Item.tsx +++ b/lib/src/breadcrumbs/Item.tsx @@ -10,7 +10,6 @@ const Item = ({ isCurrentPage = false, href, label, onClick }: ItemPropsType) => const handleOnMouseEnter = (event: React.MouseEvent<HTMLAnchorElement>) => { const labelContainer = event.currentTarget; const optionElement = currentItemRef?.current; - if (optionElement.title === "" && labelContainer.scrollWidth > labelContainer.clientWidth) optionElement.title = label; }; diff --git a/lib/src/breadcrumbs/types.ts b/lib/src/breadcrumbs/types.ts index f22b352c7..5748cc0ac 100644 --- a/lib/src/breadcrumbs/types.ts +++ b/lib/src/breadcrumbs/types.ts @@ -1,17 +1,18 @@ type Item = { href?: string; label: string; - onClick?: (href: string) => void; }; type Props = { ariaLabel?: string; items: Array<Item>; itemsBeforeCollapse?: number; + onItemClick?: (href: string) => void; showRoot?: boolean; }; export type ItemPropsType = Item & { isCurrentPage?: boolean; + onClick?: (href: string) => void; }; export default Props; diff --git a/lib/src/common/coreTokens.ts b/lib/src/common/coreTokens.ts index b145250d9..750720805 100644 --- a/lib/src/common/coreTokens.ts +++ b/lib/src/common/coreTokens.ts @@ -109,10 +109,10 @@ export const getCoreColorToken = (key: CoreColorTokens) => CoreColorTokens[key]; export type CoreColorTokens = keyof typeof CoreColorTokens; /** - * Halstack Spacing Principles + * Halstack Spacing Values * @link https://developer.dxc.com/halstack/next/principles/spacing/ */ -const SpacingTokens = { +const CoreSpacingTokens = { spacing_0: "0rem", spacing_2: "0.125rem", spacing_4: "0.25rem", @@ -132,7 +132,7 @@ const SpacingTokens = { const CoreTokens = { ...CoreColorTokens, - ...SpacingTokens, + ...CoreSpacingTokens, inherit: "inherit", From c4932dcf64f4e47a4dafe94aaa391ade32a732e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:36:13 +0100 Subject: [PATCH 11/18] Updates based on feedback --- lib/src/breadcrumbs/Breadcrumbs.tsx | 68 +++++++------- lib/src/breadcrumbs/dropdownTheme.ts | 4 + lib/src/common/variables.ts | 91 ++++++++++--------- .../screens/common/themes/advanced-theme.json | 1 - .../themes/schemas/advanced.schema.json | 1 - 5 files changed, 83 insertions(+), 82 deletions(-) diff --git a/lib/src/breadcrumbs/Breadcrumbs.tsx b/lib/src/breadcrumbs/Breadcrumbs.tsx index da3bcde2f..adb3a74ea 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.tsx @@ -1,5 +1,5 @@ import React, { useCallback } from "react"; -import styled, { ThemeProvider } from "styled-components"; +import styled from "styled-components"; import useTheme from "../useTheme"; import BreadcrumbsProps from "./types"; import DxcDropdown from "../dropdown/Dropdown"; @@ -8,7 +8,7 @@ import dropdownTheme from "./dropdownTheme"; import CoreTokens from "../common/coreTokens"; import DxcIcon from "../icon/Icon"; import Item from "./Item"; -import { DxcFlex } from "../main"; +import DxcFlex from "../flex/Flex"; const DxcBreadcrumbs = ({ ariaLabel = "Breadcrumbs", @@ -27,39 +27,37 @@ const DxcBreadcrumbs = ({ ); return ( - <ThemeProvider theme={colorsTheme.breadcrumbs}> - <nav aria-label={ariaLabel}> - <OrderedList> - {items && items.length > Math.max(itemsBeforeCollapse, 2) ? ( - <> - {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" }} - onSelectOption={handleOnSelectOption} - options={items.slice(showRoot ? 1 : 0, -1).map(({ label, href }) => ({ label, value: href }))} - /> - </HalstackProvider> - </DxcFlex> - <Item isCurrentPage key={2} label={items[items.length - 1].label} /> - </> - ) : ( - items.map((item, index, { length }) => ( - <Item - href={item.href} - isCurrentPage={index === length - 1} - key={index} - label={item.label} - onClick={onItemClick} - /> - )) - )} - </OrderedList> - </nav> - </ThemeProvider> + <nav aria-label={ariaLabel}> + <OrderedList> + {items && items.length > Math.max(itemsBeforeCollapse, 2) ? ( + <> + {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" }} + onSelectOption={handleOnSelectOption} + options={items.slice(showRoot ? 1 : 0, -1).map(({ label, href }) => ({ label, value: href }))} + /> + </HalstackProvider> + </DxcFlex> + <Item isCurrentPage key={2} label={items[items.length - 1].label} /> + </> + ) : ( + items.map((item, index, { length }) => ( + <Item + href={item.href} + isCurrentPage={index === length - 1} + key={index} + label={item.label} + onClick={onItemClick} + /> + )) + )} + </OrderedList> + </nav> ); }; diff --git a/lib/src/breadcrumbs/dropdownTheme.ts b/lib/src/breadcrumbs/dropdownTheme.ts index 18e4bfbdc..5fe908d28 100644 --- a/lib/src/breadcrumbs/dropdownTheme.ts +++ b/lib/src/breadcrumbs/dropdownTheme.ts @@ -46,6 +46,10 @@ export default { 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/lib/src/common/variables.ts b/lib/src/common/variables.ts index b611cf85e..9912652a1 100644 --- a/lib/src/common/variables.ts +++ b/lib/src/common/variables.ts @@ -14,7 +14,7 @@ export const componentTokens = { assistiveTextFontColor: CoreTokens.color_grey_700, disabledAssistiveTextFontColor: CoreTokens.color_grey_500, assistiveTextMinWidth: "100px", - assistiveTextPaddingRight: "24px", + assistiveTextPaddingRight: CoreTokens.spacing_24, assistiveTextPaddingLeft: CoreTokens.spacing_0, titleLabelFontFamily: CoreTokens.type_sans, titleLabelFontSize: CoreTokens.type_scale_03, @@ -25,7 +25,7 @@ export const componentTokens = { titleLabelPaddingTop: CoreTokens.spacing_0, titleLabelPaddingBottom: CoreTokens.spacing_0, titleLabelPaddingLeft: CoreTokens.spacing_0, - titleLabelPaddingRight: "16px", + titleLabelPaddingRight: CoreTokens.spacing_16, focusBorderColor: CoreTokens.color_blue_600, focusBorderStyle: CoreTokens.border_solid, focusBorderThickness: "2px", @@ -38,7 +38,7 @@ export const componentTokens = { disabledIconColor: CoreTokens.color_grey_500, iconSize: "24px", iconMarginLeft: CoreTokens.spacing_0, - iconMarginRight: "12px", + iconMarginRight: CoreTokens.spacing_12, accordionGroupSeparatorBorderColor: CoreTokens.color_grey_200_a, accordionGroupSeparatorBorderThickness: "1px", accordionGroupSeparatorBorderRadius: "0px", @@ -87,8 +87,6 @@ export const componentTokens = { focusActionBorderColor: CoreTokens.color_blue_600, overlayColor: CoreTokens.color_grey_800_a, }, - breadcrumbs: { - }, box: { backgroundColor: CoreTokens.color_white, borderRadius: CoreTokens.border_radius_medium, @@ -117,7 +115,7 @@ export const componentTokens = { bulletIconWidth: "1.5rem", bulletHeight: "5px", bulletWidth: "5px", - bulletMarginRight: "0.5rem", + bulletMarginRight: CoreTokens.spacing_8, }, button: { labelFontLineHeight: CoreTokens.type_leading_normal, @@ -191,7 +189,7 @@ export const componentTokens = { fontColor: CoreTokens.color_black, disabledFontColor: CoreTokens.color_grey_500, focusColor: CoreTokens.color_blue_600, - checkLabelSpacing: "8px", + checkLabelSpacing: CoreTokens.spacing_8, }, chip: { backgroundColor: CoreTokens.color_grey_200, @@ -206,12 +204,12 @@ export const componentTokens = { borderRadius: "80px", borderThickness: CoreTokens.border_width_0, borderStyle: CoreTokens.border_solid, - contentPaddingLeft: "16px", - contentPaddingRight: "16px", + contentPaddingLeft: CoreTokens.spacing_16, + contentPaddingRight: CoreTokens.spacing_16, contentPaddingTop: CoreTokens.spacing_0, contentPaddingBottom: CoreTokens.spacing_0, iconSize: "24px", - iconSpacing: "8px", + iconSpacing: CoreTokens.spacing_8, iconColor: CoreTokens.color_grey_800, hoverIconColor: CoreTokens.color_grey_900, activeIconColor: CoreTokens.color_black, @@ -284,8 +282,8 @@ export const componentTokens = { buttonIconColor: CoreTokens.color_black, buttonPaddingTop: CoreTokens.spacing_0, buttonPaddingBottom: CoreTokens.spacing_0, - buttonPaddingLeft: "16px", - buttonPaddingRight: "16px", + buttonPaddingLeft: CoreTokens.spacing_16, + buttonPaddingRight: CoreTokens.spacing_16, buttonHeight: "40px", buttonBorderRadius: "4px", buttonBorderStyle: CoreTokens.border_none, @@ -307,11 +305,15 @@ export const componentTokens = { optionIconColor: CoreTokens.color_black, optionPaddingTop: "6px", optionPaddingBottom: "6px", - optionPaddingLeft: "16px", - optionPaddingRight: "16px", + optionPaddingLeft: CoreTokens.spacing_16, + optionPaddingRight: CoreTokens.spacing_16, caretIconSize: "24px", caretIconColor: CoreTokens.color_black, - caretIconSpacing: "12px", + caretIconSpacing: CoreTokens.spacing_12, + 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, @@ -329,7 +331,6 @@ export const componentTokens = { focusDropBorderColor: CoreTokens.color_blue_600, disabledDropBorderColor: CoreTokens.color_grey_500, dragoverDropBackgroundColor: CoreTokens.color_blue_50, - activeFileItemIconBackgrounColor: CoreTokens.color_grey_300, errorFileItemBorderColor: CoreTokens.color_red_700, errorFileItemBackgroundColor: CoreTokens.color_red_50, errorFilePreviewBackgroundColor: CoreTokens.color_red_200, @@ -375,7 +376,7 @@ export const componentTokens = { bottomLinksDividerColor: CoreTokens.color_blue_600, bottomLinksDividerThickness: "1px", bottomLinksDividerStyle: CoreTokens.border_solid, - bottomLinksDividerSpacing: "8px", + bottomLinksDividerSpacing: CoreTokens.spacing_8, bottomLinksFontFamily: CoreTokens.type_sans, bottomLinksFontSize: CoreTokens.type_scale_01, bottomLinksFontStyle: CoreTokens.type_normal, @@ -391,7 +392,7 @@ export const componentTokens = { logoHeight: "32px", logoWidth: "auto", socialLinksSize: "24px", - socialLinksGutter: "16px", + socialLinksGutter: CoreTokens.spacing_16, socialLinksColor: CoreTokens.color_white, }, header: { @@ -419,8 +420,8 @@ export const componentTokens = { overlayZindex: "1600", paddingTop: CoreTokens.spacing_0, paddingBottom: CoreTokens.spacing_0, - paddingRight: "24px", - paddingLeft: "24px", + paddingRight: CoreTokens.spacing_24, + paddingLeft: CoreTokens.spacing_24, underlinedColor: CoreTokens.color_black, underlinedThickness: "2px", underlinedStyle: CoreTokens.border_solid, @@ -478,7 +479,7 @@ export const componentTokens = { fontStyle: CoreTokens.type_normal, fontWeight: CoreTokens.type_regular, iconSize: "16px", - iconSpacing: "4px", + iconSpacing: CoreTokens.spacing_4, underlineSpacing: CoreTokens.spacing_0, underlineStyle: CoreTokens.border_solid, underlineThickness: "1px", @@ -518,20 +519,20 @@ export const componentTokens = { fontStyle: CoreTokens.type_normal, fontWeight: CoreTokens.type_regular, fontTextTransform: "none", - verticalPadding: "0.75rem", - horizontalPadding: "2rem", - marginRight: "40px", + verticalPadding: CoreTokens.spacing_12, + horizontalPadding: CoreTokens.spacing_32, + marginRight: CoreTokens.spacing_40, marginLeft: "20px", itemsPerPageSelectorMarginLeft: CoreTokens.spacing_0, - itemsPerPageSelectorMarginRight: "0.5rem", + itemsPerPageSelectorMarginRight: CoreTokens.spacing_8, pageSelectorMarginRight: "30px", pageSelectorMarginLeft: CoreTokens.spacing_0, - totalItemsContainerMarginRight: "2.5rem", + totalItemsContainerMarginRight: CoreTokens.spacing_40, totalItemsContainerMarginLeft: CoreTokens.spacing_0, }, paragraph: { - fontColor: CoreTokens.color_black, display: "block", + fontColor: CoreTokens.color_black, fontSize: CoreTokens.type_scale_03, fontWeight: CoreTokens.type_regular, }, @@ -717,10 +718,10 @@ export const componentTokens = { linkFontTextTransform: "none", linkFontLetterSpacing: CoreTokens.type_spacing_wide_01, linkTextDecoration: CoreTokens.type_no_line, - linkMarginTop: "4px", - linkMarginBottom: "4px", - linkMarginRight: "16px", - linkMarginLeft: "16px", + linkMarginTop: CoreTokens.spacing_4, + linkMarginBottom: CoreTokens.spacing_4, + linkMarginRight: CoreTokens.spacing_16, + linkMarginLeft: CoreTokens.spacing_16, linkFocusColor: CoreTokens.color_blue_600, scrollBarThumbColor: CoreTokens.color_grey_200_a, scrollBarTrackColor: CoreTokens.color_transparent, @@ -834,7 +835,7 @@ export const componentTokens = { thumbShift: "1.25rem", trackHeight: "12px", trackWidth: "36px", - spaceBetweenLabelSwitch: "8px", + spaceBetweenLabelSwitch: CoreTokens.spacing_8, }, table: { rowSeparatorThickness: "1px", @@ -847,8 +848,8 @@ export const componentTokens = { dataFontWeight: CoreTokens.type_regular, dataFontColor: CoreTokens.color_black, dataFontTextTransform: "none", - dataPaddingTop: "16px", - dataPaddingBottom: "16px", + dataPaddingTop: CoreTokens.spacing_16, + dataPaddingBottom: CoreTokens.spacing_16, dataPaddingRight: "20px", dataPaddingLeft: "20px", dataPaddingTopReduced: CoreTokens.spacing_8, @@ -869,8 +870,8 @@ export const componentTokens = { headerFontWeight: CoreTokens.type_regular, headerFontColor: CoreTokens.color_white, headerFontTextTransform: "none", - headerPaddingTop: "16px", - headerPaddingBottom: "16px", + headerPaddingTop: CoreTokens.spacing_16, + headerPaddingBottom: CoreTokens.spacing_16, headerPaddingRight: "20px", headerPaddingLeft: "20px", headerPaddingTopReduced: CoreTokens.spacing_8, @@ -926,8 +927,8 @@ export const componentTokens = { fontWeight: CoreTokens.type_regular, labelPaddingTop: CoreTokens.spacing_0, labelPaddingBottom: CoreTokens.spacing_0, - labelPaddingLeft: "16px", - labelPaddingRight: "16px", + labelPaddingLeft: CoreTokens.spacing_16, + labelPaddingRight: CoreTokens.spacing_16, height: "40px", iconColor: CoreTokens.color_white, iconSectionWidth: "40px", @@ -1295,13 +1296,13 @@ export type OpinionatedTheme = { }; export const spaces = { - xxsmall: "4px", - xsmall: "8px", - small: "12px", - medium: "16px", - large: "24px", - xlarge: "32px", - xxlarge: "48px", + xxsmall: CoreTokens.spacing_4, + xsmall: CoreTokens.spacing_8, + small: CoreTokens.spacing_12, + medium: CoreTokens.spacing_16, + large: CoreTokens.spacing_24, + xlarge: CoreTokens.spacing_32, + xxlarge: CoreTokens.spacing_48, }; export const responsiveSizes = { diff --git a/website/screens/common/themes/advanced-theme.json b/website/screens/common/themes/advanced-theme.json index 38893d177..f07222bb8 100644 --- a/website/screens/common/themes/advanced-theme.json +++ b/website/screens/common/themes/advanced-theme.json @@ -324,7 +324,6 @@ "focusDropBorderColor": "#0095ff", "disabledDropBorderColor": "#999999", "dragoverDropBackgroundColor": "#f5fbff", - "activeFileItemIconBackgrounColor": "#cccccc", "errorFileItemBorderColor": "#d0011b", "errorFileItemBackgroundColor": "#fff5f6", "errorFilePreviewBackgroundColor": "#ffccd3", diff --git a/website/screens/theme-generator/themes/schemas/advanced.schema.json b/website/screens/theme-generator/themes/schemas/advanced.schema.json index 49212cb8a..79ee5152a 100644 --- a/website/screens/theme-generator/themes/schemas/advanced.schema.json +++ b/website/screens/theme-generator/themes/schemas/advanced.schema.json @@ -324,7 +324,6 @@ "focusDropBorderColor": "color", "disabledDropBorderColor": "color", "dragoverDropBackgroundColor": "color", - "activeFileItemIconBackgrounColor": "color", "errorFileItemBorderColor": "color", "errorFileItemBackgroundColor": "color", "errorFilePreviewBackgroundColor": "color", From 6de03bdd1a3cf625a5d132253cb7bbd6e190af2d Mon Sep 17 00:00:00 2001 From: Mil4n0r <morenocarmonaenrique@gmail.com> Date: Wed, 20 Mar 2024 13:51:29 +0100 Subject: [PATCH 12/18] Added accessibility fixes --- .../Breadcrumbs.accessibility.test.tsx | 47 +++++++++++++++++++ lib/src/breadcrumbs/Breadcrumbs.stories.tsx | 12 +++++ .../specific/breadcrumbs/disabledRules.js | 8 ++++ 3 files changed, 67 insertions(+) create mode 100644 lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx create mode 100644 lib/test/accessibility/rules/specific/breadcrumbs/disabledRules.js diff --git a/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx b/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx new file mode 100644 index 000000000..43c6a083a --- /dev/null +++ b/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx @@ -0,0 +1,47 @@ +import React from "react"; +import { render } from "@testing-library/react"; +import { axe } from "../../test/accessibility/axe-helper.js"; +import DxcBreadcrumbs from "./Breadcrumbs.jsx"; +import { disabledRules as rules } from "../../test/accessibility/rules/specific/breadcrumbs/disabledRules.js"; + +const disabledRules = { + rules: rules.reduce((rulesObj, rule) => { + rulesObj[rule] = { enabled: false }; + return rulesObj; + }, {}), +}; + +const items = [ + { + label: "Home", + href: "/", + }, + { + label: "User Menu", + href: "", + }, + { + label: "Preferences", + href: "", + }, + { + label: "Dark Mode", + href: "", + }, +]; + +describe("Breadcrumbs component accessibility tests", () => { + it("Should not have basic accessibility issues", async () => { + const { container } = render(<DxcBreadcrumbs items={items} itemsBeforeCollapse={3} ariaLabel="example" />); + const results = await axe(container, disabledRules); + expect(results).toHaveNoViolations(); + }); + + it("Should not have basic accessibility issues without root", async () => { + const { container } = render( + <DxcBreadcrumbs items={items} itemsBeforeCollapse={3} ariaLabel="example" showRoot={false} /> + ); + const results = await axe(container, disabledRules); + expect(results).toHaveNoViolations(); + }); +}); diff --git a/lib/src/breadcrumbs/Breadcrumbs.stories.tsx b/lib/src/breadcrumbs/Breadcrumbs.stories.tsx index 450b197c4..32f74a5a0 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.stories.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.stories.tsx @@ -5,10 +5,22 @@ import DxcBreadcrumbs from "./Breadcrumbs"; import DxcContainer from "../container/Container"; import { HalstackProvider } from "../HalstackContext"; import { userEvent, within } from "@storybook/testing-library"; +import { disabledRules } from "../../test/accessibility/rules/specific/breadcrumbs/disabledRules"; +import preview from "../../.storybook/preview"; export default { title: "Breadcrumbs", component: DxcBreadcrumbs, + parameters: { + a11y: { + config: { + rules: [ + ...disabledRules.map((ruleId) => ({ id: ruleId, enabled: false })), + ...preview?.parameters?.a11y?.config?.rules, + ], + }, + }, + }, }; const items = [ diff --git a/lib/test/accessibility/rules/specific/breadcrumbs/disabledRules.js b/lib/test/accessibility/rules/specific/breadcrumbs/disabledRules.js new file mode 100644 index 000000000..c37ae007f --- /dev/null +++ b/lib/test/accessibility/rules/specific/breadcrumbs/disabledRules.js @@ -0,0 +1,8 @@ +/** + * Array of accessibility rule IDs to be disabled in both Jest and Storybook for the breadcrumbs component. + * + */ +export const disabledRules = [ + // Disable landmark unique valid rule to prevent errors from having multiple nav in the same page (that can happen in testing environments) + "landmark-unique", +]; From c2f888313d4af2472d5982d17d07b6ade8594cc2 Mon Sep 17 00:00:00 2001 From: Mil4n0r <morenocarmonaenrique@gmail.com> Date: Wed, 20 Mar 2024 13:59:33 +0100 Subject: [PATCH 13/18] Added @types for jest-axe to move from js to tsx tests --- lib/package-lock.json | 20 ++++++++++++++++++++ lib/package.json | 1 + lib/setupJestAxe.js | 2 +- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/package-lock.json b/lib/package-lock.json index 2cccb551c..4a06dc308 100644 --- a/lib/package-lock.json +++ b/lib/package-lock.json @@ -37,6 +37,7 @@ "@testing-library/user-event": "^13.0.0", "@types/color": "^3.0.3", "@types/jest": "^29.5.12", + "@types/jest-axe": "^3.5.9", "@types/react": "^18.0.18", "@types/styled-components": "5.1.29", "@types/uuid": "^9.0.6", @@ -9375,6 +9376,25 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/jest-axe": { + "version": "3.5.9", + "resolved": "https://registry.npmjs.org/@types/jest-axe/-/jest-axe-3.5.9.tgz", + "integrity": "sha512-z98CzR0yVDalCEuhGXXO4/zN4HHuSebAukXDjTLJyjEAgoUf1H1i+sr7SUB/mz8CRS/03/XChsx0dcLjHkndoQ==", + "dev": true, + "dependencies": { + "@types/jest": "*", + "axe-core": "^3.5.5" + } + }, + "node_modules/@types/jest-axe/node_modules/axe-core": { + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-3.5.6.tgz", + "integrity": "sha512-LEUDjgmdJoA3LqklSTwKYqkjcZ4HKc4ddIYGSAiSkr46NTjzg2L9RNB+lekO9P7Dlpa87+hBtzc2Fzn/+GUWMQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/@types/jest/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", diff --git a/lib/package.json b/lib/package.json index a46674e77..ae93c9645 100644 --- a/lib/package.json +++ b/lib/package.json @@ -57,6 +57,7 @@ "@testing-library/user-event": "^13.0.0", "@types/color": "^3.0.3", "@types/jest": "^29.5.12", + "@types/jest-axe": "^3.5.9", "@types/react": "^18.0.18", "@types/styled-components": "5.1.29", "@types/uuid": "^9.0.6", diff --git a/lib/setupJestAxe.js b/lib/setupJestAxe.js index 37a12a7fe..b7587e7cc 100644 --- a/lib/setupJestAxe.js +++ b/lib/setupJestAxe.js @@ -1,3 +1,3 @@ -import { toHaveNoViolations } from "jest-axe"; +import { toHaveNoViolations } from 'jest-axe'; expect.extend(toHaveNoViolations); From d023c146b1cebc7f0af38d86db9741e7c87ffeac Mon Sep 17 00:00:00 2001 From: Mil4n0r <morenocarmonaenrique@gmail.com> Date: Wed, 20 Mar 2024 14:07:00 +0100 Subject: [PATCH 14/18] Fixed typo in breadcrumbs import extension --- lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx b/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx index 43c6a083a..965ed82db 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx @@ -1,7 +1,7 @@ import React from "react"; import { render } from "@testing-library/react"; import { axe } from "../../test/accessibility/axe-helper.js"; -import DxcBreadcrumbs from "./Breadcrumbs.jsx"; +import DxcBreadcrumbs from "./Breadcrumbs"; import { disabledRules as rules } from "../../test/accessibility/rules/specific/breadcrumbs/disabledRules.js"; const disabledRules = { From c66893b987ae61e5a0cf78af9b3498501dc7f998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Thu, 21 Mar 2024 12:55:58 +0100 Subject: [PATCH 15/18] Breadcrumbs updates based on feedback --- lib/setupJestAxe.js | 2 +- lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx | 6 +++++- lib/src/breadcrumbs/Breadcrumbs.tsx | 2 -- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/setupJestAxe.js b/lib/setupJestAxe.js index b7587e7cc..37a12a7fe 100644 --- a/lib/setupJestAxe.js +++ b/lib/setupJestAxe.js @@ -1,3 +1,3 @@ -import { toHaveNoViolations } from 'jest-axe'; +import { toHaveNoViolations } from "jest-axe"; expect.extend(toHaveNoViolations); diff --git a/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx b/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx index 965ed82db..98d82bb63 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx @@ -32,11 +32,15 @@ const items = [ describe("Breadcrumbs component accessibility tests", () => { it("Should not have basic accessibility issues", async () => { + const { container } = render(<DxcBreadcrumbs items={items} ariaLabel="example" />); + const results = await axe(container, disabledRules); + expect(results).toHaveNoViolations(); + }); + it("Should not have basic accessibility issues when collapsed", async () => { const { container } = render(<DxcBreadcrumbs items={items} itemsBeforeCollapse={3} ariaLabel="example" />); const results = await axe(container, disabledRules); expect(results).toHaveNoViolations(); }); - it("Should not have basic accessibility issues without root", async () => { const { container } = render( <DxcBreadcrumbs items={items} itemsBeforeCollapse={3} ariaLabel="example" showRoot={false} /> diff --git a/lib/src/breadcrumbs/Breadcrumbs.tsx b/lib/src/breadcrumbs/Breadcrumbs.tsx index adb3a74ea..82eea9eac 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.tsx @@ -1,6 +1,5 @@ import React, { useCallback } from "react"; import styled from "styled-components"; -import useTheme from "../useTheme"; import BreadcrumbsProps from "./types"; import DxcDropdown from "../dropdown/Dropdown"; import { HalstackProvider } from "../HalstackContext"; @@ -17,7 +16,6 @@ const DxcBreadcrumbs = ({ onItemClick, showRoot = true, }: BreadcrumbsProps) => { - const colorsTheme = useTheme(); const handleOnSelectOption = useCallback( (href: string) => { if (onItemClick) onItemClick(href); From 7f47f8f8d2668fcdeb82bc62c399540bd4624246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Thu, 21 Mar 2024 16:59:09 +0100 Subject: [PATCH 16/18] Breadcrumbs updates based on feedback --- lib/src/breadcrumbs/Breadcrumbs.stories.tsx | 6 ++---- lib/src/breadcrumbs/Breadcrumbs.test.tsx | 8 ++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/src/breadcrumbs/Breadcrumbs.stories.tsx b/lib/src/breadcrumbs/Breadcrumbs.stories.tsx index 32f74a5a0..b4d467361 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.stories.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.stories.tsx @@ -178,10 +178,8 @@ const Breadcrumbs = () => ( </ExampleContainer> </ExampleContainer> <Title title="Collapsed variant with dropdown menu opened" theme="light" level={3} /> - <ExampleContainer> - <DxcContainer height="200px"> - <DxcBreadcrumbs items={items} /> - </DxcContainer> + <ExampleContainer expanded> + <DxcBreadcrumbs items={items} /> </ExampleContainer> </> ); diff --git a/lib/src/breadcrumbs/Breadcrumbs.test.tsx b/lib/src/breadcrumbs/Breadcrumbs.test.tsx index 6474c931a..199a62069 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.test.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.test.tsx @@ -69,13 +69,13 @@ describe("Breadcrumbs component tests", () => { <DxcBreadcrumbs onItemClick={onItemClick} items={[ - { label: "Home", href: "/" }, - { label: "Preferences", href: "/" }, + { label: "Home", href: "/home" }, + { label: "Preferences", href: "/preferences" }, ]} /> ); userEvent.click(getByText("Home")); - expect(onItemClick).toHaveBeenCalled(); + expect(onItemClick).toHaveBeenCalledWith("/home"); }); test("The onClick prop from an item is properly called (collapsed)", async () => { const onItemClick = jest.fn(); @@ -92,6 +92,6 @@ describe("Breadcrumbs component tests", () => { ); await userEvent.click(getByRole("button")); await userEvent.click(getByText("Preferences")); - expect(onItemClick).toHaveBeenCalled(); + expect(onItemClick).toHaveBeenCalledWith("/"); }); }); From ec7841e1efba8304ec9b911b00cd9ff230c79600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Fri, 22 Mar 2024 09:41:34 +0100 Subject: [PATCH 17/18] Breadcrumbs stories updated --- lib/src/breadcrumbs/Breadcrumbs.stories.tsx | 25 +++++++++++---------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/src/breadcrumbs/Breadcrumbs.stories.tsx b/lib/src/breadcrumbs/Breadcrumbs.stories.tsx index b4d467361..c518631bb 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.stories.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.stories.tsx @@ -48,8 +48,9 @@ const items = [ const Breadcrumbs = () => ( <> - <Title title="Default" theme="light" level={3} /> + <Title title="Breadcrumbs" theme="light" level={2} /> <ExampleContainer> + <Title title="Default" theme="light" level={3} /> <DxcBreadcrumbs items={[ { @@ -71,28 +72,28 @@ const Breadcrumbs = () => ( ]} /> </ExampleContainer> - <Title title="Collapsed variant" theme="light" level={3} /> <ExampleContainer> + <Title title="Collapsed variant" theme="light" level={3} /> <DxcBreadcrumbs items={items} /> </ExampleContainer> - <Title title="Collapsed variant without root" theme="light" level={3} /> <ExampleContainer> + <Title title="Collapsed variant without root" theme="light" level={3} /> <DxcBreadcrumbs items={items} showRoot={false} /> </ExampleContainer> - <Title title="Focus state" theme="light" level={3} /> <ExampleContainer pseudoState="pseudo-focus"> + <Title title="Focus state" theme="light" level={3} /> <DxcBreadcrumbs items={items} /> </ExampleContainer> - <Title title="Hover state" theme="light" level={3} /> <ExampleContainer pseudoState="pseudo-hover"> + <Title title="Hover state" theme="light" level={3} /> <DxcBreadcrumbs items={items} /> </ExampleContainer> - <Title title="Active state" theme="light" level={3} /> <ExampleContainer pseudoState="pseudo-active"> + <Title title="Active state" theme="light" level={3} /> <DxcBreadcrumbs items={items} /> </ExampleContainer> - <Title title="Truncation and text ellipsis with tooltip (only when collapsed)" theme="light" level={3} /> <ExampleContainer> + <Title title="Truncation and text ellipsis with tooltip (only when collapsed)" theme="light" level={3} /> <DxcContainer width="200px"> <DxcBreadcrumbs items={[ @@ -117,8 +118,8 @@ const Breadcrumbs = () => ( /> </DxcContainer> </ExampleContainer> - <Title title="Truncation, text ellipsis with tooltip and without root" theme="light" level={3} /> <ExampleContainer> + <Title title="Truncation, text ellipsis with tooltip and without root" theme="light" level={3} /> <DxcContainer width="200px"> <DxcBreadcrumbs items={[ @@ -144,10 +145,10 @@ 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} /> + <Title title="Dropdown theming doesn't affect the collapsed trigger" theme="light" level={3} /> <ExampleContainer> + <Title title="Opinionated theming" theme="light" level={4} /> <HalstackProvider theme={{ dropdown: { @@ -160,8 +161,8 @@ const Breadcrumbs = () => ( <DxcBreadcrumbs items={items} itemsBeforeCollapse={3} /> </HalstackProvider> </ExampleContainer> - <Title title="Advanced theming" theme="light" level={4} /> <ExampleContainer> + <Title title="Advanced theming" theme="light" level={4} /> <HalstackProvider advancedTheme={{ dropdown: { @@ -177,8 +178,8 @@ const Breadcrumbs = () => ( </HalstackProvider> </ExampleContainer> </ExampleContainer> - <Title title="Collapsed variant with dropdown menu opened" theme="light" level={3} /> <ExampleContainer expanded> + <Title title="Collapsed variant with dropdown menu opened" theme="light" level={3} /> <DxcBreadcrumbs items={items} /> </ExampleContainer> </> From 1849ef6b47d399f1ec91c43d7b0987dc772d8e29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Fri, 22 Mar 2024 09:55:16 +0100 Subject: [PATCH 18/18] More breadcrumbs stories --- lib/src/breadcrumbs/Breadcrumbs.stories.tsx | 35 +++++++++++---------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/lib/src/breadcrumbs/Breadcrumbs.stories.tsx b/lib/src/breadcrumbs/Breadcrumbs.stories.tsx index c518631bb..31187d644 100644 --- a/lib/src/breadcrumbs/Breadcrumbs.stories.tsx +++ b/lib/src/breadcrumbs/Breadcrumbs.stories.tsx @@ -48,9 +48,8 @@ const items = [ const Breadcrumbs = () => ( <> - <Title title="Breadcrumbs" theme="light" level={2} /> + <Title title="Default" theme="light" level={3} /> <ExampleContainer> - <Title title="Default" theme="light" level={3} /> <DxcBreadcrumbs items={[ { @@ -72,28 +71,34 @@ const Breadcrumbs = () => ( ]} /> </ExampleContainer> + <Title title="Collapsed variant" theme="light" level={3} /> <ExampleContainer> - <Title title="Collapsed variant" theme="light" level={3} /> <DxcBreadcrumbs items={items} /> </ExampleContainer> + <Title title="Collapsed variant without root" theme="light" level={3} /> <ExampleContainer> - <Title title="Collapsed variant without root" theme="light" level={3} /> <DxcBreadcrumbs items={items} showRoot={false} /> </ExampleContainer> + <Title title="Collapsed variant with dropdown menu opened" theme="light" level={3} /> + <ExampleContainer> + <DxcContainer height="150px"> + <DxcBreadcrumbs items={items} /> + </DxcContainer> + </ExampleContainer> + <Title title="Focus state" theme="light" level={3} /> <ExampleContainer pseudoState="pseudo-focus"> - <Title title="Focus state" theme="light" level={3} /> <DxcBreadcrumbs items={items} /> </ExampleContainer> + <Title title="Hover state" theme="light" level={3} /> <ExampleContainer pseudoState="pseudo-hover"> - <Title title="Hover state" theme="light" level={3} /> <DxcBreadcrumbs items={items} /> </ExampleContainer> + <Title title="Active state" theme="light" level={3} /> <ExampleContainer pseudoState="pseudo-active"> - <Title title="Active state" theme="light" level={3} /> <DxcBreadcrumbs items={items} /> </ExampleContainer> + <Title title="Truncation and text ellipsis with tooltip (only when collapsed)" theme="light" level={3} /> <ExampleContainer> - <Title title="Truncation and text ellipsis with tooltip (only when collapsed)" theme="light" level={3} /> <DxcContainer width="200px"> <DxcBreadcrumbs items={[ @@ -118,8 +123,8 @@ const Breadcrumbs = () => ( /> </DxcContainer> </ExampleContainer> + <Title title="Truncation, text ellipsis with tooltip and without root" theme="light" level={3} /> <ExampleContainer> - <Title title="Truncation, text ellipsis with tooltip and without root" theme="light" level={3} /> <DxcContainer width="200px"> <DxcBreadcrumbs items={[ @@ -145,10 +150,10 @@ const Breadcrumbs = () => ( /> </DxcContainer> </ExampleContainer> + <Title title="Dropdown theming doesn't affect the collapsed trigger" theme="light" level={3} /> <ExampleContainer> - <Title title="Dropdown theming doesn't affect the collapsed trigger" theme="light" level={3} /> + <Title title="Opinionated theming" theme="light" level={4} /> <ExampleContainer> - <Title title="Opinionated theming" theme="light" level={4} /> <HalstackProvider theme={{ dropdown: { @@ -161,8 +166,8 @@ const Breadcrumbs = () => ( <DxcBreadcrumbs items={items} itemsBeforeCollapse={3} /> </HalstackProvider> </ExampleContainer> + <Title title="Advanced theming" theme="light" level={4} /> <ExampleContainer> - <Title title="Advanced theming" theme="light" level={4} /> <HalstackProvider advancedTheme={{ dropdown: { @@ -178,10 +183,6 @@ const Breadcrumbs = () => ( </HalstackProvider> </ExampleContainer> </ExampleContainer> - <ExampleContainer expanded> - <Title title="Collapsed variant with dropdown menu opened" theme="light" level={3} /> - <DxcBreadcrumbs items={items} /> - </ExampleContainer> </> ); @@ -189,5 +190,5 @@ export const Chromatic = Breadcrumbs.bind({}); Chromatic.play = async ({ canvasElement }) => { const canvas = within(canvasElement); const dropdowns = canvas.getAllByRole("button"); - await userEvent.click(dropdowns[dropdowns.length - 1]); + await userEvent.click(dropdowns[2]); };