From 0562eec9c219534f78c462008110ae8a860815d8 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, 8 Oct 2024 16:43:02 +0200 Subject: [PATCH 01/11] Adding tooltip to Select & Contextual Menu --- .../lib/src/contextual-menu/ItemAction.tsx | 21 +- packages/lib/src/select/ListOption.tsx | 80 ++++--- packages/lib/src/select/Select.tsx | 212 +++++++++--------- packages/lib/src/select/selectUtils.ts | 2 +- packages/lib/src/tooltip/Tooltip.tsx | 120 +++++----- packages/lib/src/tooltip/TooltipContext.tsx | 3 - packages/lib/src/tooltip/types.tsx | 6 + 7 files changed, 226 insertions(+), 218 deletions(-) delete mode 100644 packages/lib/src/tooltip/TooltipContext.tsx diff --git a/packages/lib/src/contextual-menu/ItemAction.tsx b/packages/lib/src/contextual-menu/ItemAction.tsx index ed488074d..0e3debb61 100644 --- a/packages/lib/src/contextual-menu/ItemAction.tsx +++ b/packages/lib/src/contextual-menu/ItemAction.tsx @@ -3,18 +3,7 @@ import styled from "styled-components"; import CoreTokens from "../common/coreTokens"; import { ItemActionProps } from "./types"; import DxcIcon from "../icon/Icon"; -import DxcTooltip from "../tooltip/Tooltip"; - -// TODO: The tooltip is not working fine, text-overflow is not ellipsis due to wrapper container. -const TooltipWrapper = ({ - condition, - children, - label, -}: { - condition: boolean; - children: React.ReactNode; - label: string; -}) => (condition ? {children} : <>{children}); +import { TooltipWrapper } from "../tooltip/Tooltip"; const ItemAction = ({ badge, collapseIcon, icon, label, depthLevel, ...props }: ItemActionProps) => { const [hasTooltip, setHasTooltip] = useState(false); @@ -28,10 +17,10 @@ const ItemAction = ({ badge, collapseIcon, icon, label, depthLevel, ...props }: {icon && depthLevel === 0 && {typeof icon === "string" ? : icon}} ) => { - // const text = event.currentTarget; - // if (text.title === "" && text.scrollWidth > text.clientWidth) setHasTooltip(true); - // }} + onMouseEnter={(event: React.MouseEvent) => { + const text = event.currentTarget; + if (text.title === "" && text.scrollWidth > text.clientWidth) setHasTooltip(true); + }} > {label} diff --git a/packages/lib/src/select/ListOption.tsx b/packages/lib/src/select/ListOption.tsx index bdf43dd51..dc8b3b244 100644 --- a/packages/lib/src/select/ListOption.tsx +++ b/packages/lib/src/select/ListOption.tsx @@ -2,6 +2,9 @@ import styled from "styled-components"; import { OptionProps } from "./types"; import DxcCheckbox from "../checkbox/Checkbox"; import DxcIcon from "../icon/Icon"; +import { useState } from "react"; +import { Tooltip } from "@radix-ui/react-tooltip"; +import { TooltipWrapper } from "../tooltip/Tooltip"; const ListOption = ({ id, @@ -13,50 +16,53 @@ const ListOption = ({ isLastOption, isSelected, }: OptionProps): JSX.Element => { - const handleOnMouseEnter = (event: React.MouseEvent) => { - const label = event.currentTarget; - const optionElement = document.getElementById(id); - if (optionElement.title === "" && label.scrollWidth > label.clientWidth) optionElement.title = option.label; + const [hasTooltip, setHasTooltip] = useState(false); + + const handleOnMouseEnter = (event: React.MouseEvent) => { + const text = event.currentTarget; + if (text.title === "" && text.scrollWidth > text.clientWidth) setHasTooltip(true); }; return ( - { - onClick(option); - }} - visualFocused={visualFocused} - selected={isSelected} - role="option" - aria-selected={!multiple ? isSelected : undefined} - > - + { + onClick(option); + }} visualFocused={visualFocused} selected={isSelected} - last={isLastOption} - grouped={isGroupedOption} - multiple={multiple} + role="option" + aria-pressed={!multiple ? isSelected : undefined} > - {multiple && ( -
- -
- )} - {option.icon && ( - - {typeof option.icon === "string" ? : option.icon} - - )} - - {option.label} - {!multiple && isSelected && ( - - - + + {multiple && ( +
+ +
+ )} + {option.icon && ( + + {typeof option.icon === "string" ? : option.icon} + )} -
-
-
+ + {option.label} + {!multiple && isSelected && ( + + + + )} + + + + ); }; diff --git a/packages/lib/src/select/Select.tsx b/packages/lib/src/select/Select.tsx index f3eb96e16..b7cb211ef 100644 --- a/packages/lib/src/select/Select.tsx +++ b/packages/lib/src/select/Select.tsx @@ -4,7 +4,7 @@ import styled, { ThemeProvider } from "styled-components"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; -import DxcTooltip from "../tooltip/Tooltip"; +import DxcTooltip, { TooltipWrapper } from "../tooltip/Tooltip"; import useTheme from "../useTheme"; import useTranslatedLabels from "../useTranslatedLabels"; import useWidth from "../utils/useWidth"; @@ -52,10 +52,9 @@ const DxcSelect = forwardRef( const [searchValue, setSearchValue] = useState(""); const [visualFocusIndex, changeVisualFocusIndex] = useState(-1); const [isOpen, changeIsOpen] = useState(false); - + const [hasTooltip, setHasTooltip] = useState(false); const selectRef = useRef(null); const selectSearchInputRef = useRef(null); - const selectedOptionLabelRef = useRef(null); const width = useWidth(selectRef.current); const colorsTheme = useTheme(); @@ -229,13 +228,10 @@ const DxcSelect = forwardRef( [handleSelectChangeValue, closeListbox, multiple] ); - useLayoutEffect(() => { - if (selectedOptionLabelRef?.current != null) { - if (selectedOptionLabelRef?.current.scrollWidth > selectedOptionLabelRef?.current.clientWidth) - selectedOptionLabelRef.current.title = getSelectedOptionLabel(placeholder, selectedOption); - else selectedOptionLabelRef.current.title = ""; - } - }, [placeholder, selectedOption]); + const handleOnMouseEnter = (event: React.MouseEvent) => { + const text = event.currentTarget; + if (text.title === "" && text.scrollWidth > text.clientWidth) setHasTooltip(true); + }; return ( @@ -255,112 +251,114 @@ const DxcSelect = forwardRef( {helperText && {helperText}} - = 0 ? `option-${visualFocusIndex}` : undefined} + aria-invalid={error ? true : false} + aria-errormessage={error ? errorId : undefined} + aria-required={!disabled && !optional} + > + {multiple && Array.isArray(selectedOption) && selectedOption.length > 0 && ( + + {selectedOption.length} + + { + // Avoid input to lose focus when pressed + event.preventDefault(); + }} + onClick={handleClearOptionsActionOnClick} + tabIndex={-1} + aria-label={translatedLabels.select.actionClearSelectionTitle} + > + + + + + )} + + + {searchable && ( + + )} + {(!searchable || searchValue === "") && ( + + + {getSelectedOptionLabel(placeholder, selectedOption)} + + + )} + + {!disabled && error && ( + + + + )} + {searchable && searchValue.length > 0 && ( + + { - // Avoid input to lose focus when pressed + // Avoid input to lose focus event.preventDefault(); }} - onClick={handleClearOptionsActionOnClick} + onClick={handleClearSearchActionOnClick} tabIndex={-1} - aria-label={translatedLabels.select.actionClearSelectionTitle} + aria-label={translatedLabels.select.actionClearSearchTitle} > - + - - )} - - - {searchable && ( - - )} - {(!searchable || searchValue === "") && ( - - - {getSelectedOptionLabel(placeholder, selectedOption)} - - )} - - {!disabled && error && ( - - - - )} - {searchable && searchValue.length > 0 && ( - - { - // Avoid input to lose focus - event.preventDefault(); - }} - onClick={handleClearSearchActionOnClick} - tabIndex={-1} - aria-label={translatedLabels.select.actionClearSearchTitle} - > - - - - )} - - - - + + + + + Array.isArray(selectedOption) diff --git a/packages/lib/src/tooltip/Tooltip.tsx b/packages/lib/src/tooltip/Tooltip.tsx index 8881ae6a4..0c1e77cae 100644 --- a/packages/lib/src/tooltip/Tooltip.tsx +++ b/packages/lib/src/tooltip/Tooltip.tsx @@ -1,60 +1,16 @@ -import * as Tooltip from "@radix-ui/react-tooltip"; import styled from "styled-components"; import CoreTokens from "../common/coreTokens"; -import TooltipPropsType from "./types"; -import { TooltipContext } from "./TooltipContext"; -import { useContext } from "react"; - -const triangleIcon = ( - - - -); - -const DxcTooltip = ({ position = "bottom", label, children }: TooltipPropsType): JSX.Element => { - const hasTooltip = useContext(TooltipContext); - - return ( - - {label && !hasTooltip ? ( - - - - {children} - - - - {label} - - {triangleIcon} - - - - - - ) : ( - children - )} - - ); -}; +import TooltipPropsType, { TooltipWrapperProps } from "./types"; +import { createContext, useContext } from "react"; +import { Root, Trigger, Portal, Arrow, Content } from "@radix-ui/react-tooltip"; +import { Provider } from "@radix-ui/react-tooltip"; const TooltipTriggerContainer = styled.div` - display: inline-flex; position: relative; + display: inline-flex; `; -const StyledTooltipContent = styled(Tooltip.Content)` +const StyledTooltipContent = styled(Content)` z-index: 2147483647; animation-duration: 0.2s; @@ -120,14 +76,70 @@ const StyledTooltipContent = styled(Tooltip.Content)` const TooltipContainer = styled.div` box-sizing: border-box; - padding: 8px 12px; + max-width: 242px; border-radius: 4px; + border-color: ${CoreTokens.color_grey_800}; + padding: 8px 12px; font-size: ${CoreTokens.type_scale_01}; font-family: ${CoreTokens.type_sans}; - max-width: 242px; color: ${CoreTokens.color_white}; background-color: ${CoreTokens.color_grey_800}; - border-color: ${CoreTokens.color_grey_800}; `; -export default DxcTooltip; +const triangleIcon = ( + + + +); + +const TooltipContext = createContext(false); + +export const Tooltip = ({ + hasAdditionalContainer = false, + position = "bottom", + label, + children, +}: { hasAdditionalContainer?: boolean } & TooltipPropsType): JSX.Element => { + const hasTooltip = useContext(TooltipContext); + + return ( + + {label && !hasTooltip ? ( + + + + {hasAdditionalContainer ? {children} : children} + + + + {label} + + {triangleIcon} + + + + + + ) : ( + children + )} + + ); +}; + +export const TooltipWrapper = ({ condition, children, label }: TooltipWrapperProps) => + condition ? {children} : <>{children}; + +export default function DxcTooltip(props: TooltipPropsType) { + return ; +} diff --git a/packages/lib/src/tooltip/TooltipContext.tsx b/packages/lib/src/tooltip/TooltipContext.tsx deleted file mode 100644 index 5ac842bf3..000000000 --- a/packages/lib/src/tooltip/TooltipContext.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import { createContext } from "react"; - -export const TooltipContext = createContext(false); diff --git a/packages/lib/src/tooltip/types.tsx b/packages/lib/src/tooltip/types.tsx index 25a4a996a..ae2ad8bd9 100644 --- a/packages/lib/src/tooltip/types.tsx +++ b/packages/lib/src/tooltip/types.tsx @@ -13,4 +13,10 @@ type Props = { children: React.ReactNode; }; +export type TooltipWrapperProps = { + condition: boolean; + children: React.ReactNode; + label: string; +}; + export default Props; From 40a046bd45318e47b8bb8e38398d7ff29ad40ce5 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, 9 Oct 2024 17:00:31 +0200 Subject: [PATCH 02/11] Adding tooltip to Select & Contextual Menu --- packages/lib/src/action-icon/ActionIcon.tsx | 6 +- packages/lib/src/badge/Badge.tsx | 44 +++-- packages/lib/src/button/Button.tsx | 6 +- .../ContextualMenu.stories.tsx | 17 ++ .../lib/src/contextual-menu/ItemAction.tsx | 1 + packages/lib/src/date-input/DatePicker.tsx | 10 +- packages/lib/src/file-input/FileItem.tsx | 1 - packages/lib/src/footer/Footer.tsx | 6 +- packages/lib/src/header/Header.tsx | 6 +- packages/lib/src/layout/ApplicationLayout.tsx | 6 +- packages/lib/src/select/ListOption.tsx | 1 + packages/lib/src/select/Select.stories.tsx | 166 ++++++------------ packages/lib/src/select/Select.tsx | 137 ++++++++------- packages/lib/src/tabs/Tab.tsx | 6 +- packages/lib/src/toggle-group/ToggleGroup.tsx | 6 +- packages/lib/src/tooltip/Tooltip.tsx | 1 + 16 files changed, 193 insertions(+), 227 deletions(-) diff --git a/packages/lib/src/action-icon/ActionIcon.tsx b/packages/lib/src/action-icon/ActionIcon.tsx index df0da9a91..eda97467e 100644 --- a/packages/lib/src/action-icon/ActionIcon.tsx +++ b/packages/lib/src/action-icon/ActionIcon.tsx @@ -3,12 +3,12 @@ import ActionIconPropsTypes, { RefType } from "./types"; import styled from "styled-components"; import CoreTokens from "../common/coreTokens"; import DxcIcon from "../icon/Icon"; -import DxcTooltip from "../tooltip/Tooltip"; +import { Tooltip } from "../tooltip/Tooltip"; const DxcActionIcon = forwardRef( ({ disabled = false, title, icon, onClick, tabIndex }, ref): JSX.Element => { return ( - + ( > {typeof icon === "string" ? : icon} - + ); } ); diff --git a/packages/lib/src/badge/Badge.tsx b/packages/lib/src/badge/Badge.tsx index 937c421ab..d7ae2e361 100644 --- a/packages/lib/src/badge/Badge.tsx +++ b/packages/lib/src/badge/Badge.tsx @@ -3,7 +3,7 @@ import BadgePropsType from "./types"; import DxcFlex from "../flex/Flex"; import CoreTokens from "../common/coreTokens"; import DxcIcon from "../icon/Icon"; -import DxcTooltip from "../tooltip/Tooltip"; +import { Tooltip } from "../tooltip/Tooltip"; const contextualColorMap = { grey: { @@ -91,28 +91,26 @@ const DxcBadge = ({ icon, notificationLimit = 99, size = "medium", -}: BadgePropsType): JSX.Element => { - return ( - - - {(mode === "contextual" && ( - - {icon && ( - {typeof icon === "string" ? : icon} - )} - - )) || - - ); -}; +}: BadgePropsType): JSX.Element => ( + + + {(mode === "contextual" && ( + + {icon && ( + {typeof icon === "string" ? : icon} + )} + + )) || + +); const getColor = (mode, color) => (mode === "contextual" ? contextualColorMap[color].text : CoreTokens.color_white); diff --git a/packages/lib/src/button/Button.tsx b/packages/lib/src/button/Button.tsx index 95829d765..54a51db86 100644 --- a/packages/lib/src/button/Button.tsx +++ b/packages/lib/src/button/Button.tsx @@ -4,7 +4,7 @@ import { getMargin } from "../common/utils"; import useTheme from "../useTheme"; import ButtonPropsType from "./types"; import DxcIcon from "../icon/Icon"; -import DxcTooltip from "../tooltip/Tooltip"; +import { Tooltip } from "../tooltip/Tooltip"; const DxcButton = ({ label = "", @@ -24,7 +24,7 @@ const DxcButton = ({ return ( - + - + ); }; diff --git a/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx b/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx index 3716725a2..5ee709d48 100644 --- a/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx +++ b/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx @@ -6,6 +6,7 @@ import DxcContainer from "../container/Container"; import useTheme from "../useTheme"; import DxcContextualMenu, { ContextualMenuContext } from "./ContextualMenu"; import SingleItem from "./SingleItem"; +import { userEvent, within } from "@storybook/test"; export default { title: "Contextual Menu", @@ -215,3 +216,19 @@ export const SingleItemStates = () => { ); }; + +const ItemWithEllipsis = () => ( + + + <DxcContainer width="300px"> + <DxcContextualMenu items={itemsWithTruncatedText} /> + </DxcContainer> + </ExampleContainer> +); + +export const ContextualMenuTooltip = ItemWithEllipsis.bind({}); +ContextualMenuTooltip.play = async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.hover(canvas.getByText("Item with a very long label that should be truncated")); + await userEvent.hover(canvas.getByText("Item with a very long label that should be truncated")); +}; diff --git a/packages/lib/src/contextual-menu/ItemAction.tsx b/packages/lib/src/contextual-menu/ItemAction.tsx index 0e3debb61..e3a8de3e4 100644 --- a/packages/lib/src/contextual-menu/ItemAction.tsx +++ b/packages/lib/src/contextual-menu/ItemAction.tsx @@ -20,6 +20,7 @@ const ItemAction = ({ badge, collapseIcon, icon, label, depthLevel, ...props }: onMouseEnter={(event: React.MouseEvent<HTMLSpanElement>) => { const text = event.currentTarget; if (text.title === "" && text.scrollWidth > text.clientWidth) setHasTooltip(true); + else setHasTooltip(false); }} > {label} diff --git a/packages/lib/src/date-input/DatePicker.tsx b/packages/lib/src/date-input/DatePicker.tsx index bc799140a..a10f389f0 100644 --- a/packages/lib/src/date-input/DatePicker.tsx +++ b/packages/lib/src/date-input/DatePicker.tsx @@ -7,7 +7,7 @@ import Calendar from "./Calendar"; import YearPicker from "./YearPicker"; import useTranslatedLabels from "../useTranslatedLabels"; import DxcIcon from "../icon/Icon"; -import DxcTooltip from "../tooltip/Tooltip"; +import {Tooltip} from "../tooltip/Tooltip"; const today = dayjs(); @@ -34,14 +34,14 @@ const DatePicker = ({ date, onDateSelect, id }: DatePickerPropsType): JSX.Elemen return ( <DatePickerContainer id={id}> <PickerHeader> - <DxcTooltip label={translatedLabels.calendar.previousMonthTitle}> + <Tooltip label={translatedLabels.calendar.previousMonthTitle}> <HeaderButton aria-label={translatedLabels.calendar.previousMonthTitle} onClick={() => handleMonthChange(innerDate.set("month", innerDate.get("month") - 1))} > <DxcIcon icon="keyboard_arrow_left" /> </HeaderButton> - </DxcTooltip> + </Tooltip> <HeaderYearTrigger aria-live="polite" onClick={() => setContent((content) => (content === "yearPicker" ? "calendar" : "yearPicker"))} @@ -51,14 +51,14 @@ const DatePicker = ({ date, onDateSelect, id }: DatePickerPropsType): JSX.Elemen </HeaderYearTriggerLabel> <DxcIcon icon={content === "yearPicker" ? "arrow_drop_up" : "arrow_drop_down"} /> </HeaderYearTrigger> - <DxcTooltip label={translatedLabels.calendar.nextMonthTitle}> + <Tooltip label={translatedLabels.calendar.nextMonthTitle}> <HeaderButton aria-label={translatedLabels.calendar.nextMonthTitle} onClick={() => handleMonthChange(innerDate.set("month", innerDate.get("month") + 1))} > <DxcIcon icon="keyboard_arrow_right" /> </HeaderButton> - </DxcTooltip> + </Tooltip> </PickerHeader> {content === "calendar" && ( <Calendar diff --git a/packages/lib/src/file-input/FileItem.tsx b/packages/lib/src/file-input/FileItem.tsx index dab91a72e..3943335b0 100644 --- a/packages/lib/src/file-input/FileItem.tsx +++ b/packages/lib/src/file-input/FileItem.tsx @@ -5,7 +5,6 @@ import useTheme from "../useTheme"; import useTranslatedLabels from "../useTranslatedLabels"; import { FileItemProps } from "./types"; import DxcIcon from "../icon/Icon"; -import DxcTooltip from "../tooltip/Tooltip"; import DxcActionIcon from "../action-icon/ActionIcon"; const FileItem = ({ diff --git a/packages/lib/src/footer/Footer.tsx b/packages/lib/src/footer/Footer.tsx index d80d56adc..b5df7a312 100644 --- a/packages/lib/src/footer/Footer.tsx +++ b/packages/lib/src/footer/Footer.tsx @@ -3,7 +3,7 @@ import styled, { ThemeProvider } from "styled-components"; import { responsiveSizes, spaces } from "../common/variables"; import DxcFlex from "../flex/Flex"; import DxcIcon from "../icon/Icon"; -import DxcTooltip from "../tooltip/Tooltip"; +import { Tooltip } from "../tooltip/Tooltip"; import useTheme from "../useTheme"; import useTranslatedLabels from "../useTranslatedLabels"; import { dxcLogo, dxcSmallLogo } from "./Icons"; @@ -46,7 +46,7 @@ const DxcFooter = ({ {mode === "default" && ( <DxcFlex gap={colorsTheme.footer.socialLinksGutter as Spaces}> {socialLinks?.map((link, index) => ( - <DxcTooltip label={link.title}> + <Tooltip label={link.title}> <SocialAnchor href={link.href} tabIndex={tabIndex} @@ -58,7 +58,7 @@ const DxcFooter = ({ {typeof link.logo === "string" ? <DxcIcon icon={link.logo} /> : link.logo} </SocialIconContainer> </SocialAnchor> - </DxcTooltip> + </Tooltip> ))} </DxcFlex> )} diff --git a/packages/lib/src/header/Header.tsx b/packages/lib/src/header/Header.tsx index d7be0086a..a79d5f796 100644 --- a/packages/lib/src/header/Header.tsx +++ b/packages/lib/src/header/Header.tsx @@ -6,7 +6,7 @@ import DxcIcon from "../icon/Icon"; import useTheme from "../useTheme"; import useTranslatedLabels from "../useTranslatedLabels"; import HeaderPropsType from "./types"; -import DxcTooltip from "../tooltip/Tooltip"; +import { Tooltip } from "../tooltip/Tooltip"; import DxcFlex from "../flex/Flex"; const Dropdown = (props: ComponentProps<typeof DxcDropdown>) => ( @@ -119,11 +119,11 @@ const DxcHeader = ({ <ResponsiveMenu hasVisibility={isMenuVisible}> <DxcFlex justifyContent="space-between" alignItems="center"> <ResponsiveLogoContainer>{headerResponsiveLogo}</ResponsiveLogoContainer> - <DxcTooltip label={translatedLabels.header.closeIcon}> + <Tooltip label={translatedLabels.header.closeIcon}> <CloseAction tabIndex={tabIndex} onClick={handleMenu} aria-label={translatedLabels.header.closeIcon}> <DxcIcon icon="close" /> </CloseAction> - </DxcTooltip> + </Tooltip> </DxcFlex> <Content isResponsive={isResponsive} diff --git a/packages/lib/src/layout/ApplicationLayout.tsx b/packages/lib/src/layout/ApplicationLayout.tsx index a27b2a584..74372d190 100644 --- a/packages/lib/src/layout/ApplicationLayout.tsx +++ b/packages/lib/src/layout/ApplicationLayout.tsx @@ -7,7 +7,7 @@ import DxcIcon from "../icon/Icon"; import DxcSidenav from "../sidenav/Sidenav"; import { SidenavContextProvider, useResponsiveSidenavVisibility } from "../sidenav/SidenavContext"; import useTranslatedLabels from "../useTranslatedLabels"; -import DxcTooltip from "../tooltip/Tooltip"; +import { Tooltip } from "../tooltip/Tooltip"; import layoutIcons from "./Icons"; import AppLayoutPropsType, { AppLayoutMainPropsType } from "./types"; @@ -101,7 +101,7 @@ const DxcApplicationLayout = ({ <HeaderContainer>{headerContent}</HeaderContainer> {sidenav && isResponsive && ( <VisibilityToggle> - <DxcTooltip label={translatedLabels.applicationLayout.visibilityToggleTitle}> + <Tooltip label={translatedLabels.applicationLayout.visibilityToggleTitle}> <HamburgerTrigger onClick={handleSidenavVisibility} aria-label={visibilityToggleLabel ? undefined : translatedLabels.applicationLayout.visibilityToggleTitle} @@ -109,7 +109,7 @@ const DxcApplicationLayout = ({ <DxcIcon icon="Menu" /> {visibilityToggleLabel} </HamburgerTrigger> - </DxcTooltip> + </Tooltip> </VisibilityToggle> )} <BodyContainer> diff --git a/packages/lib/src/select/ListOption.tsx b/packages/lib/src/select/ListOption.tsx index dc8b3b244..3faeea7d7 100644 --- a/packages/lib/src/select/ListOption.tsx +++ b/packages/lib/src/select/ListOption.tsx @@ -21,6 +21,7 @@ const ListOption = ({ const handleOnMouseEnter = (event: React.MouseEvent<HTMLSpanElement>) => { const text = event.currentTarget; if (text.title === "" && text.scrollWidth > text.clientWidth) setHasTooltip(true); + else setHasTooltip(false); }; return ( diff --git a/packages/lib/src/select/Select.stories.tsx b/packages/lib/src/select/Select.stories.tsx index da4896e08..e841a9691 100644 --- a/packages/lib/src/select/Select.stories.tsx +++ b/packages/lib/src/select/Select.stories.tsx @@ -212,7 +212,7 @@ const options_material = [ }, ]; -const optionsWithEllipsisMedium = [ +const optionsWithEllipsis = [ { label: "Optiond1234567890123456789012345678901234", value: "1" }, { label: "Optiond12345678901234567890123456789012345", value: "2" }, { label: "Option 031111111111111111111111111111222", value: "3" }, @@ -347,13 +347,13 @@ const Select = () => ( <Title title="Multiple selection with ellipsis" theme="light" level={4} /> <DxcSelect label="Label" options={single_options} multiple defaultValue={["1", "2", "3", "4"]} /> <Title title="Value with ellipsis" theme="light" level={4} /> - <DxcSelect label="Label" options={optionsWithEllipsisMedium} defaultValue="1" /> + <DxcSelect label="Label" options={optionsWithEllipsis} defaultValue="1" /> <Title title="Options with ellipsis" theme="light" level={4} /> <DxcSelect label="Label" placeholder="Choose an option" defaultValue="1" - options={optionsWithEllipsisMedium} + options={optionsWithEllipsis} margin={{ top: "xxlarge" }} /> </ExampleContainer> @@ -696,15 +696,6 @@ const SearchableSelect = () => ( </ExampleContainer> ); -const SearchableSelectOpinionated = () => ( - <ExampleContainer expanded> - <Title title="Searchable select" theme="light" level={4} /> - <HalstackProvider theme={opinionatedTheme}> - <DxcSelect label="Select Label" searchable options={single_options} placeholder="Choose an option" /> - </HalstackProvider> - </ExampleContainer> -); - const SearchValue = () => ( <ExampleContainer expanded> <Title title="Searchable select with value" theme="light" level={4} /> @@ -718,21 +709,6 @@ const SearchValue = () => ( </ExampleContainer> ); -const SearchValueOpinionated = () => ( - <ExampleContainer expanded> - <Title title="Searchable select with value" theme="light" level={4} /> - <HalstackProvider theme={opinionatedTheme}> - <DxcSelect - label="Select Label" - searchable - defaultValue="1" - options={single_options} - placeholder="Choose an option" - /> - </HalstackProvider> - </ExampleContainer> -); - const MultipleSelect = () => ( <> <ExampleContainer expanded> @@ -748,21 +724,6 @@ const MultipleSelect = () => ( </> ); -const MultipleSelectOpinionated = () => ( - <ExampleContainer expanded> - <Title title="Multiple select" theme="light" level={4} /> - <HalstackProvider theme={opinionatedTheme}> - <DxcSelect - label="Select label" - options={single_options} - defaultValue={["1", "4"]} - multiple - placeholder="Choose an option" - /> - </HalstackProvider> - </ExampleContainer> -); - const DefaultGroupedOptionsSelect = () => ( <ExampleContainer expanded> <Title title="Grouped options simple select" theme="light" level={4} /> @@ -792,21 +753,6 @@ const MultipleGroupedOptionsSelect = () => ( </ExampleContainer> ); -const MultipleGroupedOptionsSelectOpinionated = () => ( - <ExampleContainer expanded> - <Title title="Grouped options multiple select" theme="light" level={4} /> - <HalstackProvider theme={opinionatedTheme}> - <DxcSelect - label="Label" - options={group_options} - defaultValue={["0", "2"]} - multiple - placeholder="Choose an option" - /> - </HalstackProvider> - </ExampleContainer> -); - const MultipleSearchable = () => ( <ExampleContainer expanded> <Title title="Searchable multiple select with value" theme="light" level={4} /> @@ -821,19 +767,42 @@ const MultipleSearchable = () => ( </ExampleContainer> ); -const MultipleSearchableOpinionated = () => ( +const TooltipValue = () => ( <ExampleContainer expanded> - <Title title="Searchable multiple select with value" theme="light" level={4} /> - <HalstackProvider theme={opinionatedTheme}> - <DxcSelect - label="Select Label" - searchable - multiple - defaultValue={["1", "4"]} - options={single_options} - placeholder="Choose an option" - /> - </HalstackProvider> + <Title title="Selected value(s) have tooltip when they overflow" theme="light" level={4} /> + <DxcSelect label="Label" options={single_options} multiple defaultValue={["1", "2", "3", "4"]} /> + </ExampleContainer> +); + +const TooltipOption = () => { + const colorsTheme = useTheme(); + + return ( + <ThemeProvider theme={colorsTheme.select}> + <ExampleContainer expanded> + <Title title="List option has tooltip when it overflows" theme="light" level={4} />{" "} + <Listbox + id="x8" + currentValue="1" + options={optionsWithEllipsis} + visualFocusIndex={-1} + lastOptionIndex={2} + multiple={false} + optional={false} + optionalItem={{ label: "Empty", value: "" }} + searchable={false} + handleOptionOnClick={() => {}} + styles={{ width: 360 }} + /> + </ExampleContainer> + </ThemeProvider> + ); +}; + +const TooltipClear = () => ( + <ExampleContainer> + <Title title="Clear action tooltip" theme="light" level={4} /> + <DxcSelect label="Label" options={single_options} multiple defaultValue={["1", "2", "3", "4"]} /> </ExampleContainer> ); @@ -856,36 +825,18 @@ Searchable.play = async ({ canvasElement }) => { await userEvent.type(canvas.getByRole("combobox"), "r"); }; -export const SearchableOpinionated = SearchableSelectOpinionated.bind({}); -SearchableOpinionated.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.type(canvas.getByRole("combobox"), "r"); -}; - export const SearchableWithValue = SearchValue.bind({}); SearchableWithValue.play = async ({ canvasElement }) => { const canvas = within(canvasElement); await userEvent.click(canvas.getByRole("combobox")); }; -export const SearchableWithValueOpinionated = SearchValueOpinionated.bind({}); -SearchableWithValueOpinionated.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.click(canvas.getByRole("combobox")); -}; - export const MultipleSearchableWithValue = MultipleSearchable.bind({}); MultipleSearchableWithValue.play = async ({ canvasElement }) => { const canvas = within(canvasElement); await userEvent.click(canvas.getAllByRole("combobox")[0]); }; -export const MultipleSearchableWithValueOpinionated = MultipleSearchableOpinionated.bind({}); -MultipleSearchableWithValueOpinionated.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("combobox")[0]); -}; - export const GroupOptionsDisplayed = DefaultGroupedOptionsSelect.bind({}); GroupOptionsDisplayed.play = async ({ canvasElement }) => { const canvas = within(canvasElement); @@ -906,12 +857,6 @@ MultipleOptionsDisplayed.play = async ({ canvasElement }) => { await userEvent.click(canvas.getAllByRole("combobox")[0]); }; -export const MultipleOptionsDisplayedOpinionated = MultipleSelectOpinionated.bind({}); -MultipleOptionsDisplayedOpinionated.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("combobox")[0]); -}; - export const MultipleGroupedOptionsDisplayed = MultipleGroupedOptionsSelect.bind({}); MultipleGroupedOptionsDisplayed.play = async ({ canvasElement }) => { const canvas = within(canvasElement); @@ -919,28 +864,31 @@ MultipleGroupedOptionsDisplayed.play = async ({ canvasElement }) => { await userEvent.click(select); }; -export const MultipleGroupedOptionsDisplayedOpinionated = MultipleGroupedOptionsSelectOpinionated.bind({}); -MultipleGroupedOptionsDisplayedOpinionated.play = async ({ canvasElement }) => { +export const ValueWithEllipsisTooltip = TooltipValue.bind({}); +ValueWithEllipsisTooltip.play = async ({ canvasElement }) => { const canvas = within(canvasElement); - const select = canvas.getByRole("combobox"); - await userEvent.click(select); + await userEvent.hover(canvas.getByText("Option 01, Option 02, Option 03, Option 04")); + await userEvent.hover(canvas.getByText("Option 01, Option 02, Option 03, Option 04")); }; -const Tooltip = () => { - const colorsTheme: any = useTheme(); - return ( - <ThemeProvider theme={colorsTheme}> - <Title title="Default tooltip" theme="light" level={2} /> - <ExampleContainer> - <DxcSelect label="Label" options={single_options} multiple defaultValue={["1", "2", "3", "4"]} /> - </ExampleContainer> - </ThemeProvider> - ); +export const ListboxOptionWithEllipsisTooltip = TooltipOption.bind({}); +ListboxOptionWithEllipsisTooltip.play = async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.hover(canvas.getByText("Optiond12345678901234567890123456789012345")); + await userEvent.hover(canvas.getByText("Optiond12345678901234567890123456789012345")); }; -export const SelectTooltip = Tooltip.bind({}); -SelectTooltip.play = async ({ canvasElement }) => { +export const ClearActionTooltip = TooltipClear.bind({}); +ClearActionTooltip.play = async ({ canvasElement }) => { const canvas = within(canvasElement); const clearSelectionButton = canvas.getByRole("button"); await userEvent.hover(clearSelectionButton); }; + +export const SearchableClearActionTooltip = SearchableSelect.bind({}); +SearchableClearActionTooltip.play = async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.type(canvas.getByRole("combobox"), "r"); + const clearSelectionButton = canvas.getByRole("button"); + await userEvent.hover(clearSelectionButton); +}; diff --git a/packages/lib/src/select/Select.tsx b/packages/lib/src/select/Select.tsx index b7cb211ef..2569def64 100644 --- a/packages/lib/src/select/Select.tsx +++ b/packages/lib/src/select/Select.tsx @@ -1,10 +1,10 @@ import * as Popover from "@radix-ui/react-popover"; -import { forwardRef, useCallback, useId, useLayoutEffect, useMemo, useRef, useState } from "react"; +import { forwardRef, useCallback, useId, useMemo, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; -import DxcTooltip, { TooltipWrapper } from "../tooltip/Tooltip"; +import { Tooltip, TooltipWrapper } from "../tooltip/Tooltip"; import useTheme from "../useTheme"; import useTranslatedLabels from "../useTranslatedLabels"; import useWidth from "../utils/useWidth"; @@ -231,6 +231,7 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>( const handleOnMouseEnter = (event: React.MouseEvent<HTMLSpanElement>) => { const text = event.currentTarget; if (text.title === "" && text.scrollWidth > text.clientWidth) setHasTooltip(true); + else setHasTooltip(false); }; return ( @@ -251,47 +252,47 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>( {helperText && <HelperText disabled={disabled}>{helperText}</HelperText>} <Popover.Root open={isOpen}> <Popover.Trigger asChild type={undefined}> - <TooltipWrapper condition={hasTooltip} label={getSelectedOptionLabel(placeholder, selectedOption)}> - <Select - id={selectId} - disabled={disabled} - error={error} - onBlur={handleSelectOnBlur} - onClick={handleSelectOnClick} - onFocus={handleSelectOnFocus} - onKeyDown={handleSelectOnKeyDown} - ref={selectRef} - tabIndex={disabled ? -1 : tabIndex} - role="combobox" - aria-controls={isOpen ? listboxId : undefined} - aria-disabled={disabled} - aria-expanded={isOpen} - aria-haspopup="listbox" - aria-labelledby={label ? selectLabelId : undefined} - aria-activedescendant={visualFocusIndex >= 0 ? `option-${visualFocusIndex}` : undefined} - aria-invalid={error ? true : false} - aria-errormessage={error ? errorId : undefined} - aria-required={!disabled && !optional} - > - {multiple && Array.isArray(selectedOption) && selectedOption.length > 0 && ( - <SelectionIndicator> - <SelectionNumber disabled={disabled}>{selectedOption.length}</SelectionNumber> - <DxcTooltip label={translatedLabels.select.actionClearSelectionTitle}> - <ClearOptionsAction - disabled={disabled} - onMouseDown={(event) => { - // Avoid input to lose focus when pressed - event.preventDefault(); - }} - onClick={handleClearOptionsActionOnClick} - tabIndex={-1} - aria-label={translatedLabels.select.actionClearSelectionTitle} - > - <DxcIcon icon="clear" /> - </ClearOptionsAction> - </DxcTooltip> - </SelectionIndicator> - )} + <Select + id={selectId} + disabled={disabled} + error={error} + onBlur={handleSelectOnBlur} + onClick={handleSelectOnClick} + onFocus={handleSelectOnFocus} + onKeyDown={handleSelectOnKeyDown} + ref={selectRef} + tabIndex={disabled ? -1 : tabIndex} + role="combobox" + aria-controls={isOpen ? listboxId : undefined} + aria-disabled={disabled} + aria-expanded={isOpen} + aria-haspopup="listbox" + aria-labelledby={label ? selectLabelId : undefined} + aria-activedescendant={visualFocusIndex >= 0 ? `option-${visualFocusIndex}` : undefined} + aria-invalid={error ? true : false} + aria-errormessage={error ? errorId : undefined} + aria-required={!disabled && !optional} + > + {multiple && Array.isArray(selectedOption) && selectedOption.length > 0 && ( + <SelectionIndicator> + <SelectionNumber disabled={disabled}>{selectedOption.length}</SelectionNumber> + <Tooltip label={translatedLabels.select.actionClearSelectionTitle}> + <ClearOptionsAction + disabled={disabled} + onMouseDown={(event) => { + // Avoid input to lose focus when pressed + event.preventDefault(); + }} + onClick={handleClearOptionsActionOnClick} + tabIndex={-1} + aria-label={translatedLabels.select.actionClearSelectionTitle} + > + <DxcIcon icon="clear" /> + </ClearOptionsAction> + </Tooltip> + </SelectionIndicator> + )} + <TooltipWrapper condition={hasTooltip} label={getSelectedOptionLabel(placeholder, selectedOption)}> <SearchableValueContainer> <input style={{ display: "none" }} @@ -334,31 +335,31 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>( </SelectedOption> )} </SearchableValueContainer> - {!disabled && error && ( - <ErrorIcon> - <DxcIcon icon="filled_error" /> - </ErrorIcon> - )} - {searchable && searchValue.length > 0 && ( - <DxcTooltip label={translatedLabels.select.actionClearSelectionTitle}> - <ClearSearchAction - onMouseDown={(event) => { - // Avoid input to lose focus - event.preventDefault(); - }} - onClick={handleClearSearchActionOnClick} - tabIndex={-1} - aria-label={translatedLabels.select.actionClearSearchTitle} - > - <DxcIcon icon="clear" /> - </ClearSearchAction> - </DxcTooltip> - )} - <CollapseIndicator disabled={disabled}> - <DxcIcon icon={isOpen ? "keyboard_arrow_up" : "keyboard_arrow_down"} /> - </CollapseIndicator> - </Select> - </TooltipWrapper> + </TooltipWrapper> + {!disabled && error && ( + <ErrorIcon> + <DxcIcon icon="filled_error" /> + </ErrorIcon> + )} + {searchable && searchValue.length > 0 && ( + <Tooltip label={translatedLabels.select.actionClearSelectionTitle}> + <ClearSearchAction + onMouseDown={(event) => { + // Avoid input to lose focus + event.preventDefault(); + }} + onClick={handleClearSearchActionOnClick} + tabIndex={-1} + aria-label={translatedLabels.select.actionClearSearchTitle} + > + <DxcIcon icon="clear" /> + </ClearSearchAction> + </Tooltip> + )} + <CollapseIndicator disabled={disabled}> + <DxcIcon icon={isOpen ? "keyboard_arrow_up" : "keyboard_arrow_down"} /> + </CollapseIndicator> + </Select> </Popover.Trigger> <Popover.Portal> <Popover.Content diff --git a/packages/lib/src/tabs/Tab.tsx b/packages/lib/src/tabs/Tab.tsx index 1cd4abd0d..a355e9565 100644 --- a/packages/lib/src/tabs/Tab.tsx +++ b/packages/lib/src/tabs/Tab.tsx @@ -2,7 +2,7 @@ import { forwardRef, Ref, useContext, useEffect, useRef } from "react"; import styled from "styled-components"; import DxcBadge from "../badge/Badge"; import DxcIcon from "../icon/Icon"; -import DxcTooltip from "../tooltip/Tooltip"; +import { Tooltip } from "../tooltip/Tooltip"; import useTheme from "../useTheme"; import BaseTypography from "../utils/BaseTypography"; import { TabsContext } from "./TabsContext"; @@ -64,7 +64,7 @@ const DxcTab = forwardRef( }; return ( - <DxcTooltip label={title}> + <Tooltip label={title}> <TabContainer role="tab" type="button" @@ -128,7 +128,7 @@ const DxcTab = forwardRef( </BadgeContainer> )} </TabContainer> - </DxcTooltip> + </Tooltip> ); } ); diff --git a/packages/lib/src/toggle-group/ToggleGroup.tsx b/packages/lib/src/toggle-group/ToggleGroup.tsx index 30853f117..91206df98 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.tsx @@ -3,7 +3,7 @@ import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; import DxcFlex from "../flex/Flex"; import DxcIcon from "../icon/Icon"; -import DxcTooltip from "../tooltip/Tooltip"; +import { Tooltip } from "../tooltip/Tooltip"; import useTheme from "../useTheme"; import ToggleGroupPropsType, { OptionLabel } from "./types"; @@ -67,7 +67,7 @@ const DxcToggleGroup = ({ <HelperText disabled={disabled}>{helperText}</HelperText> <OptionsContainer aria-labelledby={toggleGroupLabelId}> {options.map((option, i) => ( - <DxcTooltip label={option.title}> + <Tooltip label={option.title}> <ToggleButton key={`toggle-${i}-${option.label}`} aria-label={option.title} @@ -109,7 +109,7 @@ const DxcToggleGroup = ({ {option.label && <LabelContainer>{option.label}</LabelContainer>} </DxcFlex> </ToggleButton> - </DxcTooltip> + </Tooltip> ))} </OptionsContainer> </ToggleGroup> diff --git a/packages/lib/src/tooltip/Tooltip.tsx b/packages/lib/src/tooltip/Tooltip.tsx index 0c1e77cae..fac597ddf 100644 --- a/packages/lib/src/tooltip/Tooltip.tsx +++ b/packages/lib/src/tooltip/Tooltip.tsx @@ -84,6 +84,7 @@ const TooltipContainer = styled.div` font-family: ${CoreTokens.type_sans}; color: ${CoreTokens.color_white}; background-color: ${CoreTokens.color_grey_800}; + overflow-wrap: break-word; `; const triangleIcon = ( From 03080641bc1dacbcd40bd9a61ad6eb9f242faaea 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, 10 Oct 2024 16:53:47 +0200 Subject: [PATCH 03/11] Adding new title prop to the Dropdown & several fixes --- .../dropdown/code/DropdownCodePage.tsx | 11 ++ .../heading/usage/HeadingUsagePage.tsx | 2 +- packages/lib/src/action-icon/ActionIcon.tsx | 6 +- packages/lib/src/badge/Badge.tsx | 44 +++--- packages/lib/src/button/Button.tsx | 6 +- .../lib/src/contextual-menu/ItemAction.tsx | 3 +- .../lib/src/dropdown/Dropdown.stories.tsx | 60 +++++--- packages/lib/src/dropdown/Dropdown.tsx | 130 +++++++++--------- packages/lib/src/dropdown/types.ts | 5 + packages/lib/src/select/ListOption.tsx | 3 +- packages/lib/src/select/Select.tsx | 3 +- packages/lib/src/tabs/Tab.tsx | 6 +- packages/lib/src/toggle-group/ToggleGroup.tsx | 6 +- 13 files changed, 160 insertions(+), 125 deletions(-) diff --git a/apps/website/screens/components/dropdown/code/DropdownCodePage.tsx b/apps/website/screens/components/dropdown/code/DropdownCodePage.tsx index 919696722..db39ff7f6 100644 --- a/apps/website/screens/components/dropdown/code/DropdownCodePage.tsx +++ b/apps/website/screens/components/dropdown/code/DropdownCodePage.tsx @@ -184,6 +184,17 @@ const sections = [ <TableCode>0</TableCode> </td> </tr> + <tr> + <td>title</td> + <td> + <TableCode>string</TableCode> + </td> + <td> + Text representing advisory information related to the dropdown's trigger action. Under the hood, this + prop also serves as an accessible label for the component. + </td> + <td>-</td> + </tr> </tbody> </DxcTable> </> diff --git a/apps/website/screens/components/heading/usage/HeadingUsagePage.tsx b/apps/website/screens/components/heading/usage/HeadingUsagePage.tsx index 8fc8359fe..93e8fcd15 100644 --- a/apps/website/screens/components/heading/usage/HeadingUsagePage.tsx +++ b/apps/website/screens/components/heading/usage/HeadingUsagePage.tsx @@ -39,7 +39,7 @@ const sections = [ <> <DxcBulletedList> <DxcBulletedList.Item> - Use <Code>Heading</Code> componments from <Code>H1</Code> to <Code>H5</Code> when creating content + Use <Code>Heading</Code> components from <Code>H1</Code> to <Code>H5</Code> when creating content hierarchy in the page. </DxcBulletedList.Item> <DxcBulletedList.Item> diff --git a/packages/lib/src/action-icon/ActionIcon.tsx b/packages/lib/src/action-icon/ActionIcon.tsx index eda97467e..2fe04621a 100644 --- a/packages/lib/src/action-icon/ActionIcon.tsx +++ b/packages/lib/src/action-icon/ActionIcon.tsx @@ -3,12 +3,12 @@ import ActionIconPropsTypes, { RefType } from "./types"; import styled from "styled-components"; import CoreTokens from "../common/coreTokens"; import DxcIcon from "../icon/Icon"; -import { Tooltip } from "../tooltip/Tooltip"; +import { TooltipWrapper } from "../tooltip/Tooltip"; const DxcActionIcon = forwardRef<RefType, ActionIconPropsTypes>( ({ disabled = false, title, icon, onClick, tabIndex }, ref): JSX.Element => { return ( - <Tooltip label={title}> + <TooltipWrapper condition={Boolean(title)} label={title}> <ActionIcon aria-label={title} disabled={disabled} @@ -22,7 +22,7 @@ const DxcActionIcon = forwardRef<RefType, ActionIconPropsTypes>( > {typeof icon === "string" ? <DxcIcon icon={icon} /> : icon} </ActionIcon> - </Tooltip> + </TooltipWrapper> ); } ); diff --git a/packages/lib/src/badge/Badge.tsx b/packages/lib/src/badge/Badge.tsx index d7ae2e361..f1ede5f49 100644 --- a/packages/lib/src/badge/Badge.tsx +++ b/packages/lib/src/badge/Badge.tsx @@ -1,9 +1,8 @@ import styled from "styled-components"; import BadgePropsType from "./types"; -import DxcFlex from "../flex/Flex"; import CoreTokens from "../common/coreTokens"; import DxcIcon from "../icon/Icon"; -import { Tooltip } from "../tooltip/Tooltip"; +import { TooltipWrapper } from "../tooltip/Tooltip"; const contextualColorMap = { grey: { @@ -75,14 +74,6 @@ const sizeMap = { }, }; -const Label = ({ label, notificationLimit, size }) => { - return ( - <LabelContainer size={size}> - {typeof label === "number" ? (label > notificationLimit ? "+" + notificationLimit : label) : label} - </LabelContainer> - ); -}; - const DxcBadge = ({ label, title, @@ -92,7 +83,7 @@ const DxcBadge = ({ notificationLimit = 99, size = "medium", }: BadgePropsType): JSX.Element => ( - <Tooltip label={title}> + <TooltipWrapper condition={Boolean(title)} label={title}> <BadgeContainer label={label} mode={mode} @@ -100,16 +91,16 @@ const DxcBadge = ({ size={size} aria-label={title} > - {(mode === "contextual" && ( - <DxcFlex gap="0.125rem" alignItems="center"> - {icon && ( - <IconContainer size={size}>{typeof icon === "string" ? <DxcIcon icon={icon} /> : icon}</IconContainer> - )} - <Label label={label} notificationLimit={notificationLimit} size={size} /> - </DxcFlex> - )) || <Label label={label} notificationLimit={notificationLimit} size={size} />} + {mode === "contextual" && icon && ( + <IconContainer size={size}>{typeof icon === "string" ? <DxcIcon icon={icon} /> : icon}</IconContainer> + )} + {label && ( + <Label size={size}> + {typeof label === "number" ? (label > notificationLimit ? "+" + notificationLimit : label) : label} + </Label> + )} </BadgeContainer> - </Tooltip> + </TooltipWrapper> ); const getColor = (mode, color) => (mode === "contextual" ? contextualColorMap[color].text : CoreTokens.color_white); @@ -126,17 +117,18 @@ const BadgeContainer = styled.div<{ color: BadgePropsType["color"]; size: BadgePropsType["size"]; }>` - display: flex; - color: ${(props) => getColor(props.mode, props.color)}; - background-color: ${(props) => getBackgroundColor(props.mode, props.color)}; + box-sizing: border-box; border-radius: ${(props) => sizeMap[props.size].borderRadius}; + padding: ${(props) => (props.label ? getPadding(props.mode, props.size) : "")}; width: ${(props) => (!props.label && props.mode === "notification" ? sizeMap[props.size].width : "fit-content")}; min-width: ${(props) => props.mode === "notification" && sizeMap[props.size].minWidth}; height: ${(props) => sizeMap[props.size].height}; - padding: ${(props) => (props.label ? getPadding(props.mode, props.size) : "")}; + display: flex; align-items: center; + gap: ${CoreTokens.spacing_2}; justify-content: center; - box-sizing: border-box; + background-color: ${(props) => getBackgroundColor(props.mode, props.color)}; + color: ${(props) => getColor(props.mode, props.color)}; `; const IconContainer = styled.div<{ size: BadgePropsType["size"] }>` @@ -149,7 +141,7 @@ const IconContainer = styled.div<{ size: BadgePropsType["size"] }>` } `; -const LabelContainer = styled.span<{ size: BadgePropsType["size"] }>` +const Label = styled.span<{ size: BadgePropsType["size"] }>` font-family: ${CoreTokens.type_sans}; font-size: ${(props) => sizeMap[props.size].fontSize}; font-weight: ${CoreTokens.type_semibold}; diff --git a/packages/lib/src/button/Button.tsx b/packages/lib/src/button/Button.tsx index 54a51db86..edaa42c7d 100644 --- a/packages/lib/src/button/Button.tsx +++ b/packages/lib/src/button/Button.tsx @@ -4,7 +4,7 @@ import { getMargin } from "../common/utils"; import useTheme from "../useTheme"; import ButtonPropsType from "./types"; import DxcIcon from "../icon/Icon"; -import { Tooltip } from "../tooltip/Tooltip"; +import { TooltipWrapper } from "../tooltip/Tooltip"; const DxcButton = ({ label = "", @@ -24,7 +24,7 @@ const DxcButton = ({ return ( <ThemeProvider theme={colorsTheme.button}> - <Tooltip label={title}> + <TooltipWrapper condition={Boolean(title)} label={title}> <Button aria-label={title} disabled={disabled} @@ -46,7 +46,7 @@ const DxcButton = ({ <IconContainer size={size}>{typeof icon === "string" ? <DxcIcon icon={icon} /> : icon}</IconContainer> )} </Button> - </Tooltip> + </TooltipWrapper> </ThemeProvider> ); }; diff --git a/packages/lib/src/contextual-menu/ItemAction.tsx b/packages/lib/src/contextual-menu/ItemAction.tsx index e3a8de3e4..d16921a3a 100644 --- a/packages/lib/src/contextual-menu/ItemAction.tsx +++ b/packages/lib/src/contextual-menu/ItemAction.tsx @@ -19,8 +19,7 @@ const ItemAction = ({ badge, collapseIcon, icon, label, depthLevel, ...props }: selected={props.selected} onMouseEnter={(event: React.MouseEvent<HTMLSpanElement>) => { const text = event.currentTarget; - if (text.title === "" && text.scrollWidth > text.clientWidth) setHasTooltip(true); - else setHasTooltip(false); + setHasTooltip(text.scrollWidth > text.clientWidth); }} > {label} diff --git a/packages/lib/src/dropdown/Dropdown.stories.tsx b/packages/lib/src/dropdown/Dropdown.stories.tsx index 995a557db..8c84583fc 100644 --- a/packages/lib/src/dropdown/Dropdown.stories.tsx +++ b/packages/lib/src/dropdown/Dropdown.stories.tsx @@ -87,9 +87,9 @@ const optionsIcon: any = options.map((op, i) => ({ ...op, icon: icons[i] })); const opinionatedTheme = { dropdown: { - baseColor: "#ffffff", - fontColor: "#000000", - optionFontColor: "#000000", + baseColor: "#fabada", + fontColor: "#fff", + optionFontColor: "#0095ff", }, }; @@ -364,14 +364,7 @@ const DropdownCenterAlignment = () => ( </ExampleContainer> ); -export const Chromatic = Dropdown.bind({}); -Chromatic.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const buttonList = canvas.getAllByRole("button"); - await userEvent.click(buttonList[buttonList.length - 1]); -}; - -export const OpinionatedTheme = () => ( +const OpinionatedTheme = () => ( <> <Title title="Opinionated theme" theme="light" level={2} /> <ExampleContainer> @@ -404,23 +397,56 @@ export const OpinionatedTheme = () => ( <DxcDropdown label="Disabled" options={options} onSelectOption={(value) => {}} icon={iconSVG} disabled /> </HalstackProvider> </ExampleContainer> + <ExampleContainer expanded> + <Title title="List opened" theme="light" level={4} /> + <HalstackProvider theme={opinionatedTheme}> + <DxcDropdown label="Default" options={options} onSelectOption={(value) => {}} icon={iconSVG} /> + </HalstackProvider> + </ExampleContainer> </> ); -export const DropdownMenuStates = DropdownListStates.bind({}); -DropdownMenuStates.play = async ({ canvasElement }) => { +const TooltipTitle = () => ( + <ExampleContainer> + <Title title="Tooltip" theme="light" level={3} /> + <DxcDropdown title="Show options" options={options} onSelectOption={(value) => {}} icon="menu" caretHidden /> + </ExampleContainer> +); + +export const Chromatic = Dropdown.bind({}); +Chromatic.play = async ({ canvasElement }) => { + const canvas = within(canvasElement); + const buttonList = canvas.getAllByRole("button"); + await userEvent.click(buttonList[buttonList.length - 1]); +}; + +export const Themed = OpinionatedTheme.bind({}); +Themed.play = async ({ canvasElement }) => { + const canvas = within(canvasElement); + const buttonList = canvas.getAllByRole("button"); + await userEvent.click(buttonList[buttonList.length - 1]); +}; + +export const MenuStates = DropdownListStates.bind({}); +MenuStates.play = async ({ canvasElement }) => { const canvas = within(canvasElement); await userEvent.click(canvas.getAllByRole("button")[0]); }; -export const DropdownMenuAlignedRight = DropdownRightAlignment.bind({}); -DropdownMenuAlignedRight.play = async ({ canvasElement }) => { +export const MenuAlignedRight = DropdownRightAlignment.bind({}); +MenuAlignedRight.play = async ({ canvasElement }) => { const canvas = within(canvasElement); await userEvent.click(canvas.getByRole("button")); }; -export const DropdownMenuAlignedCenter = DropdownCenterAlignment.bind({}); -DropdownMenuAlignedCenter.play = async ({ canvasElement }) => { +export const MenuAlignedCenter = DropdownCenterAlignment.bind({}); +MenuAlignedCenter.play = async ({ canvasElement }) => { const canvas = within(canvasElement); await userEvent.click(canvas.getByRole("button")); }; + +export const MenuTooltip = TooltipTitle.bind({}); +MenuTooltip.play = async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.hover(canvas.getByRole("button")); +}; diff --git a/packages/lib/src/dropdown/Dropdown.tsx b/packages/lib/src/dropdown/Dropdown.tsx index e40104dc2..1531ec79f 100644 --- a/packages/lib/src/dropdown/Dropdown.tsx +++ b/packages/lib/src/dropdown/Dropdown.tsx @@ -8,6 +8,7 @@ import useTheme from "../useTheme"; import useWidth from "../utils/useWidth"; import DropdownMenu from "./DropdownMenu"; import DropdownPropsType from "./types"; +import { TooltipWrapper } from "../tooltip/Tooltip"; const DxcDropdown = ({ options, @@ -22,6 +23,7 @@ const DxcDropdown = ({ margin, size = "fitContent", tabIndex = 0, + title, }: DropdownPropsType): JSX.Element => { const id = useId(); const triggerId = `trigger-${id}`; @@ -137,70 +139,72 @@ const DxcDropdown = ({ return ( <ThemeProvider theme={colorsTheme.dropdown}> - <DropdownContainer - onMouseEnter={!disabled && expandOnHover ? handleOnOpenMenu : undefined} - onMouseLeave={!disabled && expandOnHover ? handleOnCloseMenu : undefined} - onBlur={!disabled ? handleOnBlur : undefined} - margin={margin} - size={size} - > - <Popover.Root open={isOpen}> - <Popover.Trigger asChild type={undefined}> - <DropdownTrigger - onClick={handleTriggerOnClick} - onKeyDown={handleTriggerOnKeyDown} - onBlur={(event) => { - event.stopPropagation(); - }} - disabled={disabled} - label={label} - margin={margin} - size={size} - id={triggerId} - aria-haspopup="true" - aria-controls={isOpen ? menuId : undefined} - aria-expanded={isOpen ? true : undefined} - aria-label="Show options" - tabIndex={tabIndex} - ref={triggerRef} - > - <DropdownTriggerContent> - {label && iconPosition === "after" && <DropdownTriggerLabel>{label}</DropdownTriggerLabel>} - {icon && ( - <DropdownTriggerIcon - disabled={disabled} - role={typeof icon === "string" ? undefined : "img"} - aria-hidden - > - {typeof icon === "string" ? <DxcIcon icon={icon} /> : icon} - </DropdownTriggerIcon> + <TooltipWrapper condition={Boolean(title)} label={title}> + <DropdownContainer + onMouseEnter={!disabled && expandOnHover ? handleOnOpenMenu : undefined} + onMouseLeave={!disabled && expandOnHover ? handleOnCloseMenu : undefined} + onBlur={!disabled ? handleOnBlur : undefined} + margin={margin} + size={size} + > + <Popover.Root open={isOpen}> + <Popover.Trigger asChild type={undefined}> + <DropdownTrigger + onClick={handleTriggerOnClick} + onKeyDown={handleTriggerOnKeyDown} + onBlur={(event) => { + event.stopPropagation(); + }} + disabled={disabled} + label={label} + margin={margin} + size={size} + id={triggerId} + aria-haspopup="true" + aria-controls={isOpen ? menuId : undefined} + aria-expanded={isOpen ? true : undefined} + aria-label="Show options" + tabIndex={tabIndex} + ref={triggerRef} + > + <DropdownTriggerContent> + {label && iconPosition === "after" && <DropdownTriggerLabel>{label}</DropdownTriggerLabel>} + {icon && ( + <DropdownTriggerIcon + disabled={disabled} + role={typeof icon === "string" ? undefined : "img"} + aria-hidden + > + {typeof icon === "string" ? <DxcIcon icon={icon} /> : icon} + </DropdownTriggerIcon> + )} + {label && iconPosition === "before" && <DropdownTriggerLabel>{label}</DropdownTriggerLabel>} + </DropdownTriggerContent> + {!caretHidden && ( + <CaretIcon disabled={disabled}> + <DxcIcon icon={isOpen ? "arrow_drop_up" : "arrow_drop_down"} />{" "} + </CaretIcon> )} - {label && iconPosition === "before" && <DropdownTriggerLabel>{label}</DropdownTriggerLabel>} - </DropdownTriggerContent> - {!caretHidden && ( - <CaretIcon disabled={disabled}> - <DxcIcon icon={isOpen ? "arrow_drop_up" : "arrow_drop_down"} />{" "} - </CaretIcon> - )} - </DropdownTrigger> - </Popover.Trigger> - <Popover.Portal> - <Popover.Content asChild sideOffset={1}> - <DropdownMenu - id={menuId} - dropdownTriggerId={triggerId} - options={options} - iconsPosition={optionsIconPosition} - visualFocusIndex={visualFocusIndex} - menuItemOnClick={handleMenuItemOnClick} - onKeyDown={handleMenuOnKeyDown} - styles={{ width, zIndex: "2147483647" }} - ref={menuRef} - /> - </Popover.Content> - </Popover.Portal> - </Popover.Root> - </DropdownContainer> + </DropdownTrigger> + </Popover.Trigger> + <Popover.Portal> + <Popover.Content asChild sideOffset={1}> + <DropdownMenu + id={menuId} + dropdownTriggerId={triggerId} + options={options} + iconsPosition={optionsIconPosition} + visualFocusIndex={visualFocusIndex} + menuItemOnClick={handleMenuItemOnClick} + onKeyDown={handleMenuOnKeyDown} + styles={{ width, zIndex: "2147483647" }} + ref={menuRef} + /> + </Popover.Content> + </Popover.Portal> + </Popover.Root> + </DropdownContainer> + </TooltipWrapper> </ThemeProvider> ); }; diff --git a/packages/lib/src/dropdown/types.ts b/packages/lib/src/dropdown/types.ts index 84f60fcf8..0fff032dc 100644 --- a/packages/lib/src/dropdown/types.ts +++ b/packages/lib/src/dropdown/types.ts @@ -76,6 +76,11 @@ type Props = { * Value of the tabindex attribute. */ tabIndex?: number; + /** + * Text representing advisory information related to the dropdown's trigger action. + * Under the hood, this prop also serves as an accessible label for the component. + */ + title?: string; }; export type DropdownMenuProps = { diff --git a/packages/lib/src/select/ListOption.tsx b/packages/lib/src/select/ListOption.tsx index 3faeea7d7..97847ad90 100644 --- a/packages/lib/src/select/ListOption.tsx +++ b/packages/lib/src/select/ListOption.tsx @@ -20,8 +20,7 @@ const ListOption = ({ const handleOnMouseEnter = (event: React.MouseEvent<HTMLSpanElement>) => { const text = event.currentTarget; - if (text.title === "" && text.scrollWidth > text.clientWidth) setHasTooltip(true); - else setHasTooltip(false); + setHasTooltip(text.scrollWidth > text.clientWidth); }; return ( diff --git a/packages/lib/src/select/Select.tsx b/packages/lib/src/select/Select.tsx index 2569def64..a211d9d2e 100644 --- a/packages/lib/src/select/Select.tsx +++ b/packages/lib/src/select/Select.tsx @@ -230,8 +230,7 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>( const handleOnMouseEnter = (event: React.MouseEvent<HTMLSpanElement>) => { const text = event.currentTarget; - if (text.title === "" && text.scrollWidth > text.clientWidth) setHasTooltip(true); - else setHasTooltip(false); + setHasTooltip(text.scrollWidth > text.clientWidth); }; return ( diff --git a/packages/lib/src/tabs/Tab.tsx b/packages/lib/src/tabs/Tab.tsx index a355e9565..903871113 100644 --- a/packages/lib/src/tabs/Tab.tsx +++ b/packages/lib/src/tabs/Tab.tsx @@ -2,7 +2,7 @@ import { forwardRef, Ref, useContext, useEffect, useRef } from "react"; import styled from "styled-components"; import DxcBadge from "../badge/Badge"; import DxcIcon from "../icon/Icon"; -import { Tooltip } from "../tooltip/Tooltip"; +import { TooltipWrapper } from "../tooltip/Tooltip"; import useTheme from "../useTheme"; import BaseTypography from "../utils/BaseTypography"; import { TabsContext } from "./TabsContext"; @@ -64,7 +64,7 @@ const DxcTab = forwardRef( }; return ( - <Tooltip label={title}> + <TooltipWrapper condition={Boolean(title)} label={title}> <TabContainer role="tab" type="button" @@ -128,7 +128,7 @@ const DxcTab = forwardRef( </BadgeContainer> )} </TabContainer> - </Tooltip> + </TooltipWrapper> ); } ); diff --git a/packages/lib/src/toggle-group/ToggleGroup.tsx b/packages/lib/src/toggle-group/ToggleGroup.tsx index 91206df98..5610b51b8 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.tsx @@ -3,7 +3,7 @@ import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; import DxcFlex from "../flex/Flex"; import DxcIcon from "../icon/Icon"; -import { Tooltip } from "../tooltip/Tooltip"; +import { Tooltip, TooltipWrapper } from "../tooltip/Tooltip"; import useTheme from "../useTheme"; import ToggleGroupPropsType, { OptionLabel } from "./types"; @@ -67,7 +67,7 @@ const DxcToggleGroup = ({ <HelperText disabled={disabled}>{helperText}</HelperText> <OptionsContainer aria-labelledby={toggleGroupLabelId}> {options.map((option, i) => ( - <Tooltip label={option.title}> + <TooltipWrapper condition={Boolean(option.title)} label={option.title}> <ToggleButton key={`toggle-${i}-${option.label}`} aria-label={option.title} @@ -109,7 +109,7 @@ const DxcToggleGroup = ({ {option.label && <LabelContainer>{option.label}</LabelContainer>} </DxcFlex> </ToggleButton> - </Tooltip> + </TooltipWrapper> ))} </OptionsContainer> </ToggleGroup> From fc0d89965f3304f9f17072d826f151d9dcf88dae 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, 10 Oct 2024 17:07:10 +0200 Subject: [PATCH 04/11] Restoring correct role --- packages/lib/src/select/ListOption.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib/src/select/ListOption.tsx b/packages/lib/src/select/ListOption.tsx index 97847ad90..836ad2b37 100644 --- a/packages/lib/src/select/ListOption.tsx +++ b/packages/lib/src/select/ListOption.tsx @@ -33,7 +33,7 @@ const ListOption = ({ visualFocused={visualFocused} selected={isSelected} role="option" - aria-pressed={!multiple ? isSelected : undefined} + aria-selected={!multiple ? isSelected : undefined} > <StyledOption visualFocused={visualFocused} From 986637fa624e2972c4148d37def8ffa4a528377d 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, 11 Oct 2024 09:45:37 +0200 Subject: [PATCH 05/11] Stories update --- packages/lib/src/contextual-menu/ContextualMenu.stories.tsx | 4 ++-- packages/lib/src/dropdown/Dropdown.stories.tsx | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx b/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx index 5ee709d48..dc8b6904e 100644 --- a/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx +++ b/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx @@ -218,8 +218,8 @@ export const SingleItemStates = () => { }; const ItemWithEllipsis = () => ( - <ExampleContainer> - <Title title="Tooltip" theme="light" level={3} /> + <ExampleContainer expanded> + <Title title="Tooltip in items with ellipsis" theme="light" level={3} /> <DxcContainer width="300px"> <DxcContextualMenu items={itemsWithTruncatedText} /> </DxcContainer> diff --git a/packages/lib/src/dropdown/Dropdown.stories.tsx b/packages/lib/src/dropdown/Dropdown.stories.tsx index 8c84583fc..766ecc81e 100644 --- a/packages/lib/src/dropdown/Dropdown.stories.tsx +++ b/packages/lib/src/dropdown/Dropdown.stories.tsx @@ -449,4 +449,5 @@ export const MenuTooltip = TooltipTitle.bind({}); MenuTooltip.play = async ({ canvasElement }) => { const canvas = within(canvasElement); await userEvent.hover(canvas.getByRole("button")); + await new Promise((resolve) => setTimeout(resolve, 1000)); }; From 3ebf4924f6ba7effd2a397ee5edbba9c8b20e13b 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, 11 Oct 2024 10:42:55 +0200 Subject: [PATCH 06/11] More storybook tests --- packages/lib/src/dropdown/Dropdown.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib/src/dropdown/Dropdown.stories.tsx b/packages/lib/src/dropdown/Dropdown.stories.tsx index 766ecc81e..b450437e3 100644 --- a/packages/lib/src/dropdown/Dropdown.stories.tsx +++ b/packages/lib/src/dropdown/Dropdown.stories.tsx @@ -449,5 +449,5 @@ export const MenuTooltip = TooltipTitle.bind({}); MenuTooltip.play = async ({ canvasElement }) => { const canvas = within(canvasElement); await userEvent.hover(canvas.getByRole("button")); - await new Promise((resolve) => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve, 4000)); }; From f43f161b64a09f3887968473f2e282a00b911b68 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, 11 Oct 2024 13:13:16 +0200 Subject: [PATCH 07/11] Fixed Dropdown tooltip --- .../lib/src/dropdown/Dropdown.stories.tsx | 10 +++- packages/lib/src/dropdown/Dropdown.tsx | 54 +++++++++---------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/packages/lib/src/dropdown/Dropdown.stories.tsx b/packages/lib/src/dropdown/Dropdown.stories.tsx index b450437e3..09d864c4f 100644 --- a/packages/lib/src/dropdown/Dropdown.stories.tsx +++ b/packages/lib/src/dropdown/Dropdown.stories.tsx @@ -409,7 +409,14 @@ const OpinionatedTheme = () => ( const TooltipTitle = () => ( <ExampleContainer> <Title title="Tooltip" theme="light" level={3} /> - <DxcDropdown title="Show options" options={options} onSelectOption={(value) => {}} icon="menu" caretHidden /> + <DxcDropdown + title="Show options" + options={options} + onSelectOption={(value) => {}} + icon="menu" + caretHidden + margin="large" + /> </ExampleContainer> ); @@ -449,5 +456,4 @@ export const MenuTooltip = TooltipTitle.bind({}); MenuTooltip.play = async ({ canvasElement }) => { const canvas = within(canvasElement); await userEvent.hover(canvas.getByRole("button")); - await new Promise((resolve) => setTimeout(resolve, 4000)); }; diff --git a/packages/lib/src/dropdown/Dropdown.tsx b/packages/lib/src/dropdown/Dropdown.tsx index 1531ec79f..4cf7d73c2 100644 --- a/packages/lib/src/dropdown/Dropdown.tsx +++ b/packages/lib/src/dropdown/Dropdown.tsx @@ -139,15 +139,15 @@ const DxcDropdown = ({ return ( <ThemeProvider theme={colorsTheme.dropdown}> - <TooltipWrapper condition={Boolean(title)} label={title}> - <DropdownContainer - onMouseEnter={!disabled && expandOnHover ? handleOnOpenMenu : undefined} - onMouseLeave={!disabled && expandOnHover ? handleOnCloseMenu : undefined} - onBlur={!disabled ? handleOnBlur : undefined} - margin={margin} - size={size} - > - <Popover.Root open={isOpen}> + <DropdownContainer + onMouseEnter={!disabled && expandOnHover ? handleOnOpenMenu : undefined} + onMouseLeave={!disabled && expandOnHover ? handleOnCloseMenu : undefined} + onBlur={!disabled ? handleOnBlur : undefined} + margin={margin} + size={size} + > + <Popover.Root open={isOpen}> + <TooltipWrapper condition={Boolean(title)} label={title}> <Popover.Trigger asChild type={undefined}> <DropdownTrigger onClick={handleTriggerOnClick} @@ -187,24 +187,24 @@ const DxcDropdown = ({ )} </DropdownTrigger> </Popover.Trigger> - <Popover.Portal> - <Popover.Content asChild sideOffset={1}> - <DropdownMenu - id={menuId} - dropdownTriggerId={triggerId} - options={options} - iconsPosition={optionsIconPosition} - visualFocusIndex={visualFocusIndex} - menuItemOnClick={handleMenuItemOnClick} - onKeyDown={handleMenuOnKeyDown} - styles={{ width, zIndex: "2147483647" }} - ref={menuRef} - /> - </Popover.Content> - </Popover.Portal> - </Popover.Root> - </DropdownContainer> - </TooltipWrapper> + </TooltipWrapper> + <Popover.Portal> + <Popover.Content asChild sideOffset={1}> + <DropdownMenu + id={menuId} + dropdownTriggerId={triggerId} + options={options} + iconsPosition={optionsIconPosition} + visualFocusIndex={visualFocusIndex} + menuItemOnClick={handleMenuItemOnClick} + onKeyDown={handleMenuOnKeyDown} + styles={{ width, zIndex: "2147483647" }} + ref={menuRef} + /> + </Popover.Content> + </Popover.Portal> + </Popover.Root> + </DropdownContainer> </ThemeProvider> ); }; From 9fa4804bffa2df0237ae4dcdcda5db642190286b 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, 14 Oct 2024 17:03:57 +0200 Subject: [PATCH 08/11] Updates based on feedback --- .../screens/principles/themes/ThemesPage.tsx | 3 + packages/lib/src/HalstackContext.tsx | 1 + packages/lib/src/action-icon/ActionIcon.tsx | 38 +- packages/lib/src/badge/Badge.tsx | 6 +- packages/lib/src/button/Button.tsx | 6 +- packages/lib/src/dropdown/Dropdown.tsx | 6 +- packages/lib/src/select/ListOption.tsx | 1 - packages/lib/src/select/Select.stories.tsx | 503 +++++++----------- packages/lib/src/tabs/Tab.tsx | 6 +- packages/lib/src/toggle-group/ToggleGroup.tsx | 6 +- packages/lib/src/tooltip/Tooltip.tsx | 2 +- packages/lib/src/tooltip/types.tsx | 4 +- 12 files changed, 228 insertions(+), 354 deletions(-) diff --git a/apps/website/screens/principles/themes/ThemesPage.tsx b/apps/website/screens/principles/themes/ThemesPage.tsx index d01a1c452..52ef41982 100644 --- a/apps/website/screens/principles/themes/ThemesPage.tsx +++ b/apps/website/screens/principles/themes/ThemesPage.tsx @@ -978,6 +978,9 @@ const sections = [ <td>Option font color</td> <td> <Code>listOptionFontColor</Code> + <br /> + <br /> + <Code>listOptionIconColor</Code> </td> </tr> <tr> diff --git a/packages/lib/src/HalstackContext.tsx b/packages/lib/src/HalstackContext.tsx index a326b9f80..d99ade814 100644 --- a/packages/lib/src/HalstackContext.tsx +++ b/packages/lib/src/HalstackContext.tsx @@ -255,6 +255,7 @@ const parseTheme = (theme: DeepPartial<OpinionatedTheme>): AdvancedTheme => { selectTokens.labelFontColor = theme?.select?.fontColor ?? selectTokens.labelFontColor; selectTokens.helperTextFontColor = theme?.select?.fontColor ?? selectTokens.helperTextFontColor; selectTokens.listOptionFontColor = theme?.select?.optionFontColor ?? selectTokens.listOptionFontColor; + selectTokens.listOptionIconColor = theme?.select?.optionFontColor ?? selectTokens.listOptionIconColor; selectTokens.placeholderFontColor = addLightness(30, theme?.select?.fontColor) ?? selectTokens.placeholderFontColor; selectTokens.collapseIndicatorColor = theme?.select?.fontColor ?? selectTokens.collapseIndicatorColor; selectTokens.hoverInputBorderColor = theme?.select?.hoverBorderColor ?? selectTokens.hoverInputBorderColor; diff --git a/packages/lib/src/action-icon/ActionIcon.tsx b/packages/lib/src/action-icon/ActionIcon.tsx index 2fe04621a..dcbbb7726 100644 --- a/packages/lib/src/action-icon/ActionIcon.tsx +++ b/packages/lib/src/action-icon/ActionIcon.tsx @@ -3,28 +3,26 @@ import ActionIconPropsTypes, { RefType } from "./types"; import styled from "styled-components"; import CoreTokens from "../common/coreTokens"; import DxcIcon from "../icon/Icon"; -import { TooltipWrapper } from "../tooltip/Tooltip"; +import { Tooltip } from "../tooltip/Tooltip"; const DxcActionIcon = forwardRef<RefType, ActionIconPropsTypes>( - ({ disabled = false, title, icon, onClick, tabIndex }, ref): JSX.Element => { - return ( - <TooltipWrapper condition={Boolean(title)} label={title}> - <ActionIcon - aria-label={title} - disabled={disabled} - onClick={onClick} - onMouseDown={(event) => { - event.stopPropagation(); - }} - tabIndex={tabIndex} - type="button" - ref={ref} - > - {typeof icon === "string" ? <DxcIcon icon={icon} /> : icon} - </ActionIcon> - </TooltipWrapper> - ); - } + ({ disabled = false, title, icon, onClick, tabIndex }, ref): JSX.Element => ( + <Tooltip label={title}> + <ActionIcon + aria-label={title} + disabled={disabled} + onClick={onClick} + onMouseDown={(event) => { + event.stopPropagation(); + }} + tabIndex={tabIndex} + type="button" + ref={ref} + > + {typeof icon === "string" ? <DxcIcon icon={icon} /> : icon} + </ActionIcon> + </Tooltip> + ) ); const ActionIcon = styled.button` diff --git a/packages/lib/src/badge/Badge.tsx b/packages/lib/src/badge/Badge.tsx index f1ede5f49..64895758f 100644 --- a/packages/lib/src/badge/Badge.tsx +++ b/packages/lib/src/badge/Badge.tsx @@ -2,7 +2,7 @@ import styled from "styled-components"; import BadgePropsType from "./types"; import CoreTokens from "../common/coreTokens"; import DxcIcon from "../icon/Icon"; -import { TooltipWrapper } from "../tooltip/Tooltip"; +import { Tooltip } from "../tooltip/Tooltip"; const contextualColorMap = { grey: { @@ -83,7 +83,7 @@ const DxcBadge = ({ notificationLimit = 99, size = "medium", }: BadgePropsType): JSX.Element => ( - <TooltipWrapper condition={Boolean(title)} label={title}> + <Tooltip label={title}> <BadgeContainer label={label} mode={mode} @@ -100,7 +100,7 @@ const DxcBadge = ({ </Label> )} </BadgeContainer> - </TooltipWrapper> + </Tooltip> ); const getColor = (mode, color) => (mode === "contextual" ? contextualColorMap[color].text : CoreTokens.color_white); diff --git a/packages/lib/src/button/Button.tsx b/packages/lib/src/button/Button.tsx index edaa42c7d..54a51db86 100644 --- a/packages/lib/src/button/Button.tsx +++ b/packages/lib/src/button/Button.tsx @@ -4,7 +4,7 @@ import { getMargin } from "../common/utils"; import useTheme from "../useTheme"; import ButtonPropsType from "./types"; import DxcIcon from "../icon/Icon"; -import { TooltipWrapper } from "../tooltip/Tooltip"; +import { Tooltip } from "../tooltip/Tooltip"; const DxcButton = ({ label = "", @@ -24,7 +24,7 @@ const DxcButton = ({ return ( <ThemeProvider theme={colorsTheme.button}> - <TooltipWrapper condition={Boolean(title)} label={title}> + <Tooltip label={title}> <Button aria-label={title} disabled={disabled} @@ -46,7 +46,7 @@ const DxcButton = ({ <IconContainer size={size}>{typeof icon === "string" ? <DxcIcon icon={icon} /> : icon}</IconContainer> )} </Button> - </TooltipWrapper> + </Tooltip> </ThemeProvider> ); }; diff --git a/packages/lib/src/dropdown/Dropdown.tsx b/packages/lib/src/dropdown/Dropdown.tsx index 4cf7d73c2..ad36a565e 100644 --- a/packages/lib/src/dropdown/Dropdown.tsx +++ b/packages/lib/src/dropdown/Dropdown.tsx @@ -8,7 +8,7 @@ import useTheme from "../useTheme"; import useWidth from "../utils/useWidth"; import DropdownMenu from "./DropdownMenu"; import DropdownPropsType from "./types"; -import { TooltipWrapper } from "../tooltip/Tooltip"; +import { Tooltip } from "../tooltip/Tooltip"; const DxcDropdown = ({ options, @@ -147,7 +147,7 @@ const DxcDropdown = ({ size={size} > <Popover.Root open={isOpen}> - <TooltipWrapper condition={Boolean(title)} label={title}> + <Tooltip label={title}> <Popover.Trigger asChild type={undefined}> <DropdownTrigger onClick={handleTriggerOnClick} @@ -187,7 +187,7 @@ const DxcDropdown = ({ )} </DropdownTrigger> </Popover.Trigger> - </TooltipWrapper> + </Tooltip> <Popover.Portal> <Popover.Content asChild sideOffset={1}> <DropdownMenu diff --git a/packages/lib/src/select/ListOption.tsx b/packages/lib/src/select/ListOption.tsx index 836ad2b37..3f2b82ce2 100644 --- a/packages/lib/src/select/ListOption.tsx +++ b/packages/lib/src/select/ListOption.tsx @@ -3,7 +3,6 @@ import { OptionProps } from "./types"; import DxcCheckbox from "../checkbox/Checkbox"; import DxcIcon from "../icon/Icon"; import { useState } from "react"; -import { Tooltip } from "@radix-ui/react-tooltip"; import { TooltipWrapper } from "../tooltip/Tooltip"; const ListOption = ({ diff --git a/packages/lib/src/select/Select.stories.tsx b/packages/lib/src/select/Select.stories.tsx index e841a9691..c1e1f8f84 100644 --- a/packages/lib/src/select/Select.stories.tsx +++ b/packages/lib/src/select/Select.stories.tsx @@ -220,10 +220,10 @@ const optionsWithEllipsis = [ const opinionatedTheme = { select: { - selectedOptionBackgroundColor: "#e6f4ff", - fontColor: "#000000", - optionFontColor: "#000000", - hoverBorderColor: "#a46ede", + selectedOptionBackgroundColor: "#fabada", + fontColor: "#333", + optionFontColor: "#a46ede", + hoverBorderColor: "#0095ff", }, }; @@ -357,53 +357,34 @@ const Select = () => ( margin={{ top: "xxlarge" }} /> </ExampleContainer> + </> +); + +const Opinionated = () => ( + <> <Title title="Opinionated theme" theme="light" level={2} /> <ExampleContainer pseudoState="pseudo-hover"> - <Title title="Hovered" theme="light" level={4} /> - <HalstackProvider theme={opinionatedTheme}> - <DxcSelect label="Hovered" options={single_options} /> - </HalstackProvider> - </ExampleContainer> - <ExampleContainer pseudoState="pseudo-focus-within"> - <Title title="Focused" theme="light" level={4} /> - <HalstackProvider theme={opinionatedTheme}> - <DxcSelect label="Focused" options={single_options} /> - </HalstackProvider> - </ExampleContainer> - <ExampleContainer> - <Title title="Disabled" theme="light" level={4} /> + <Title title="Default" theme="light" level={4} /> <HalstackProvider theme={opinionatedTheme}> - <DxcSelect label="Disabled" placeholder="Placeholder" disabled options={single_options} /> + <DxcSelect label="Hovered" helperText="Helper text" placeholder="Placeholder" options={single_options} /> </HalstackProvider> </ExampleContainer> - <ExampleContainer> - <Title title="Disabled with value" theme="light" level={4} /> - <HalstackProvider theme={opinionatedTheme}> - <DxcSelect label="Disabled with value" disabled options={single_options} defaultValue="1" /> - </HalstackProvider> - </ExampleContainer> - <ExampleContainer> - <Title title="Error" theme="light" level={4} /> + <ExampleContainer pseudoState="pseudo-hover"> + <Title title="Hovered" theme="light" level={4} /> <HalstackProvider theme={opinionatedTheme}> <DxcSelect - label="Label" - options={single_options} - error="Error message." + label="Hovered" helperText="Helper text" - placeholder="Placeholder" + options={single_options} + multiple + defaultValue={["1", "2"]} /> - <ExampleContainer> - <Title title="Multiple selection" theme="light" level={4} /> - <DxcSelect label="Multiple select" searchable options={single_options} multiple defaultValue={["1", "2"]} /> - </ExampleContainer> - <ExampleContainer pseudoState="pseudo-hover"> - <Title title="Multiple clear hovered" theme="light" level={4} /> - <DxcSelect label="Multiple select" options={single_options} multiple defaultValue={["1", "2"]} /> - </ExampleContainer> - <ExampleContainer pseudoState="pseudo-active"> - <Title title="Multiple clear actived" theme="light" level={4} /> - <DxcSelect label="Multiple select" options={single_options} multiple defaultValue={["1", "2"]} /> - </ExampleContainer> + </HalstackProvider> + </ExampleContainer> + <ExampleContainer pseudoState="pseudo-hover" expanded> + <Title title="List opened" theme="light" level={4} /> + <HalstackProvider theme={opinionatedTheme}> + <DxcSelect label="Hovered" helperText="Helper text" options={icon_options_grouped_material} defaultValue="1" /> </HalstackProvider> </ExampleContainer> </> @@ -413,279 +394,165 @@ const SelectListbox = () => { const colorsTheme = useTheme(); return ( - <> - <ThemeProvider theme={colorsTheme.select}> - <Title title="Listbox" theme="light" level={2} /> - <ExampleContainer> - <Title - title="List dialog uses a Radix Popover to appear over elements with a certain z-index" - theme="light" - level={3} - /> - <div - style={{ - position: "relative", - display: "flex", - flexDirection: "column", - gap: "20px", - height: "150px", - width: "min-content", - marginBottom: "100px", - padding: "20px", - border: "1px solid black", - borderRadius: "4px", - overflow: "auto", - zIndex: "1300", - }} - > - <DxcSelect label="Label" options={single_options} optional placeholder="Choose an option" /> - <button style={{ zIndex: "1", width: "100px" }}>Submit</button> - </div> - </ExampleContainer> - <Title title="Listbox option states" theme="light" level={3} /> - <ExampleContainer pseudoState="pseudo-hover"> - <Title title="Hovered option" theme="light" level={4} /> - <Listbox - id="x8" - currentValue="" - options={one_option} - visualFocusIndex={-1} - lastOptionIndex={0} - multiple={false} - optional={false} - optionalItem={{ label: "Empty", value: "" }} - searchable={false} - handleOptionOnClick={() => {}} - styles={{ width: 360 }} - /> - </ExampleContainer> - <ExampleContainer pseudoState="pseudo-active"> - <Title title="Active option" theme="light" level={4} /> - <Listbox - id="x9" - currentValue="" - options={one_option} - visualFocusIndex={-1} - lastOptionIndex={0} - multiple={false} - optional={false} - optionalItem={{ label: "Empty", value: "" }} - searchable={false} - handleOptionOnClick={() => {}} - styles={{ width: 360 }} - /> - </ExampleContainer> - <ExampleContainer> - <Title title="Focused option" theme="light" level={4} /> - <Listbox - id="x10" - currentValue="" - options={one_option} - visualFocusIndex={0} - lastOptionIndex={0} - multiple={false} - optional={false} - optionalItem={{ label: "Empty", value: "" }} - searchable={false} - handleOptionOnClick={() => {}} - styles={{ width: 360 }} - /> - </ExampleContainer> - <ExampleContainer pseudoState="pseudo-hover"> - <Title title="Hovered selected option" theme="light" level={4} /> - <Listbox - id="x11" - currentValue="1" - options={single_options} - visualFocusIndex={-1} - lastOptionIndex={3} - multiple={false} - optional={false} - optionalItem={{ label: "Empty", value: "" }} - searchable={false} - handleOptionOnClick={() => {}} - styles={{ width: 360 }} - /> - </ExampleContainer> - <ExampleContainer pseudoState="pseudo-active"> - <Title title="Active selected option" theme="light" level={4} /> - <Listbox - id="x12" - currentValue="2" - options={single_options} - visualFocusIndex={0} - lastOptionIndex={3} - multiple={false} - optional={false} - optionalItem={{ label: "Empty", value: "" }} - searchable={false} - handleOptionOnClick={() => {}} - styles={{ width: 360 }} - /> - </ExampleContainer> - <Title title="Listbox with icons" theme="light" level={3} /> - <ExampleContainer> - <Title title="Icons (SVGs)" theme="light" level={4} /> - <Listbox - id="x13" - currentValue="3" - options={icon_options} - visualFocusIndex={-1} - lastOptionIndex={3} - multiple={false} - optional={false} - optionalItem={{ label: "Empty", value: "" }} - searchable={false} - handleOptionOnClick={() => {}} - styles={{ width: 360 }} - /> - </ExampleContainer> - <ExampleContainer> - <Title title="Grouped icons (Material Symbols)" theme="light" level={4} /> - <Listbox - id="x14" - currentValue={"4"} - options={icon_options_grouped_material} - visualFocusIndex={-1} - lastOptionIndex={3} - multiple={false} - optional={false} - optionalItem={{ label: "Empty", value: "" }} - searchable={false} - handleOptionOnClick={() => {}} - styles={{ width: 360 }} - /> - </ExampleContainer> - <ExampleContainer> - <Title title="Grouped icons (Material)" theme="light" level={4} /> - <Listbox - id="x15" - currentValue={["car", "motorcycle", "train"]} - options={options_material} - visualFocusIndex={-1} - lastOptionIndex={6} - multiple={true} - optional={false} - optionalItem={{ label: "Empty", value: "" }} - searchable={false} - handleOptionOnClick={() => {}} - styles={{ width: 360 }} - /> - </ExampleContainer> - </ThemeProvider> - <ThemeProvider theme={colorsTheme.select}> - <Title title="Opinionated theme" theme="light" level={2} /> - <ExampleContainer pseudoState="pseudo-hover"> - <Title title="Hovered option" theme="light" level={4} /> - <HalstackProvider theme={opinionatedTheme}> - <Listbox - id="x16" - currentValue="" - options={one_option} - visualFocusIndex={-1} - lastOptionIndex={0} - multiple={false} - optional={false} - optionalItem={{ label: "Empty", value: "" }} - searchable={false} - handleOptionOnClick={() => {}} - styles={{ width: 360 }} - /> - </HalstackProvider> - </ExampleContainer> - <ExampleContainer pseudoState="pseudo-active"> - <Title title="Active option" theme="light" level={4} />{" "} - <HalstackProvider theme={opinionatedTheme}> - <Listbox - id="x17" - currentValue="" - options={one_option} - visualFocusIndex={-1} - lastOptionIndex={0} - multiple={false} - optional={false} - optionalItem={{ label: "Empty", value: "" }} - searchable={false} - handleOptionOnClick={() => {}} - styles={{ width: 360 }} - /> - </HalstackProvider> - </ExampleContainer> - <ExampleContainer> - <Title title="Focused option" theme="light" level={4} />{" "} - <HalstackProvider theme={opinionatedTheme}> - <Listbox - id="x18" - currentValue="" - options={one_option} - visualFocusIndex={0} - lastOptionIndex={0} - multiple={false} - optional={false} - optionalItem={{ label: "Empty", value: "" }} - searchable={false} - handleOptionOnClick={() => {}} - styles={{ width: 360 }} - /> - </HalstackProvider> - </ExampleContainer> - <ExampleContainer pseudoState="pseudo-hover"> - <Title title="Hovered selected option" theme="light" level={4} />{" "} - <HalstackProvider theme={opinionatedTheme}> - <Listbox - id="x19" - currentValue="1" - options={single_options} - visualFocusIndex={-1} - lastOptionIndex={3} - multiple={false} - optional={false} - optionalItem={{ label: "Empty", value: "" }} - searchable={false} - handleOptionOnClick={() => {}} - styles={{ width: 360 }} - /> - </HalstackProvider> - </ExampleContainer> - <ExampleContainer pseudoState="pseudo-active"> - <Title title="Active selected option" theme="light" level={4} />{" "} - <HalstackProvider theme={opinionatedTheme}> - <Listbox - id="x20" - currentValue="2" - options={single_options} - visualFocusIndex={0} - lastOptionIndex={3} - multiple={false} - optional={false} - optionalItem={{ label: "Empty", value: "" }} - searchable={false} - handleOptionOnClick={() => {}} - styles={{ width: 360 }} - /> - </HalstackProvider> - </ExampleContainer> - <Title title="Listbox with icons" theme="light" level={3} /> - <ExampleContainer> - <Title title="Icons (SVGs)" theme="light" level={4} />{" "} - <HalstackProvider theme={opinionatedTheme}> - <Listbox - id="x21" - currentValue="3" - options={icon_options} - visualFocusIndex={-1} - lastOptionIndex={3} - multiple={false} - optional={false} - optionalItem={{ label: "Empty", value: "" }} - searchable={false} - handleOptionOnClick={() => {}} - styles={{ width: 360 }} - /> - </HalstackProvider> - </ExampleContainer> - </ThemeProvider> - </> + <ThemeProvider theme={colorsTheme.select}> + <Title title="Listbox" theme="light" level={2} /> + <ExampleContainer> + <Title + title="List dialog uses a Radix Popover to appear over elements with a certain z-index" + theme="light" + level={3} + /> + <div + style={{ + position: "relative", + display: "flex", + flexDirection: "column", + gap: "20px", + height: "150px", + width: "min-content", + marginBottom: "100px", + padding: "20px", + border: "1px solid black", + borderRadius: "4px", + overflow: "auto", + zIndex: "1300", + }} + > + <DxcSelect label="Label" options={single_options} optional placeholder="Choose an option" /> + <button style={{ zIndex: "1", width: "100px" }}>Submit</button> + </div> + </ExampleContainer> + <Title title="Listbox option states" theme="light" level={3} /> + <ExampleContainer pseudoState="pseudo-hover"> + <Title title="Hovered option" theme="light" level={4} /> + <Listbox + id="x8" + currentValue="" + options={one_option} + visualFocusIndex={-1} + lastOptionIndex={0} + multiple={false} + optional={false} + optionalItem={{ label: "Empty", value: "" }} + searchable={false} + handleOptionOnClick={() => {}} + styles={{ width: 360 }} + /> + </ExampleContainer> + <ExampleContainer pseudoState="pseudo-active"> + <Title title="Active option" theme="light" level={4} /> + <Listbox + id="x9" + currentValue="" + options={one_option} + visualFocusIndex={-1} + lastOptionIndex={0} + multiple={false} + optional={false} + optionalItem={{ label: "Empty", value: "" }} + searchable={false} + handleOptionOnClick={() => {}} + styles={{ width: 360 }} + /> + </ExampleContainer> + <ExampleContainer> + <Title title="Focused option" theme="light" level={4} /> + <Listbox + id="x10" + currentValue="" + options={one_option} + visualFocusIndex={0} + lastOptionIndex={0} + multiple={false} + optional={false} + optionalItem={{ label: "Empty", value: "" }} + searchable={false} + handleOptionOnClick={() => {}} + styles={{ width: 360 }} + /> + </ExampleContainer> + <ExampleContainer pseudoState="pseudo-hover"> + <Title title="Hovered selected option" theme="light" level={4} /> + <Listbox + id="x11" + currentValue="1" + options={single_options} + visualFocusIndex={-1} + lastOptionIndex={3} + multiple={false} + optional={false} + optionalItem={{ label: "Empty", value: "" }} + searchable={false} + handleOptionOnClick={() => {}} + styles={{ width: 360 }} + /> + </ExampleContainer> + <ExampleContainer pseudoState="pseudo-active"> + <Title title="Active selected option" theme="light" level={4} /> + <Listbox + id="x12" + currentValue="2" + options={single_options} + visualFocusIndex={0} + lastOptionIndex={3} + multiple={false} + optional={false} + optionalItem={{ label: "Empty", value: "" }} + searchable={false} + handleOptionOnClick={() => {}} + styles={{ width: 360 }} + /> + </ExampleContainer> + <Title title="Listbox with icons" theme="light" level={3} /> + <ExampleContainer> + <Title title="Icons (SVGs)" theme="light" level={4} /> + <Listbox + id="x13" + currentValue="3" + options={icon_options} + visualFocusIndex={-1} + lastOptionIndex={3} + multiple={false} + optional={false} + optionalItem={{ label: "Empty", value: "" }} + searchable={false} + handleOptionOnClick={() => {}} + styles={{ width: 360 }} + /> + </ExampleContainer> + <ExampleContainer> + <Title title="Grouped icons (Material Symbols)" theme="light" level={4} /> + <Listbox + id="x14" + currentValue={"4"} + options={icon_options_grouped_material} + visualFocusIndex={-1} + lastOptionIndex={3} + multiple={false} + optional={false} + optionalItem={{ label: "Empty", value: "" }} + searchable={false} + handleOptionOnClick={() => {}} + styles={{ width: 360 }} + /> + </ExampleContainer> + <ExampleContainer> + <Title title="Grouped icons (Material)" theme="light" level={4} /> + <Listbox + id="x15" + currentValue={["car", "motorcycle", "train"]} + options={options_material} + visualFocusIndex={-1} + lastOptionIndex={6} + multiple={true} + optional={false} + optionalItem={{ label: "Empty", value: "" }} + searchable={false} + handleOptionOnClick={() => {}} + styles={{ width: 360 }} + /> + </ExampleContainer> + </ThemeProvider> ); }; @@ -812,6 +679,12 @@ Chromatic.play = async ({ canvasElement }) => { await userEvent.click(canvas.getAllByRole("combobox")[24]); }; +export const OpinionatedTheme = Opinionated.bind({}); +OpinionatedTheme.play = async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click(canvas.getAllByRole("combobox")[2]); +}; + export const ListboxStates = SelectListbox.bind({}); ListboxStates.play = async ({ canvasElement }) => { const canvas = within(canvasElement); diff --git a/packages/lib/src/tabs/Tab.tsx b/packages/lib/src/tabs/Tab.tsx index 903871113..a355e9565 100644 --- a/packages/lib/src/tabs/Tab.tsx +++ b/packages/lib/src/tabs/Tab.tsx @@ -2,7 +2,7 @@ import { forwardRef, Ref, useContext, useEffect, useRef } from "react"; import styled from "styled-components"; import DxcBadge from "../badge/Badge"; import DxcIcon from "../icon/Icon"; -import { TooltipWrapper } from "../tooltip/Tooltip"; +import { Tooltip } from "../tooltip/Tooltip"; import useTheme from "../useTheme"; import BaseTypography from "../utils/BaseTypography"; import { TabsContext } from "./TabsContext"; @@ -64,7 +64,7 @@ const DxcTab = forwardRef( }; return ( - <TooltipWrapper condition={Boolean(title)} label={title}> + <Tooltip label={title}> <TabContainer role="tab" type="button" @@ -128,7 +128,7 @@ const DxcTab = forwardRef( </BadgeContainer> )} </TabContainer> - </TooltipWrapper> + </Tooltip> ); } ); diff --git a/packages/lib/src/toggle-group/ToggleGroup.tsx b/packages/lib/src/toggle-group/ToggleGroup.tsx index 5610b51b8..91206df98 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.tsx @@ -3,7 +3,7 @@ import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; import DxcFlex from "../flex/Flex"; import DxcIcon from "../icon/Icon"; -import { Tooltip, TooltipWrapper } from "../tooltip/Tooltip"; +import { Tooltip } from "../tooltip/Tooltip"; import useTheme from "../useTheme"; import ToggleGroupPropsType, { OptionLabel } from "./types"; @@ -67,7 +67,7 @@ const DxcToggleGroup = ({ <HelperText disabled={disabled}>{helperText}</HelperText> <OptionsContainer aria-labelledby={toggleGroupLabelId}> {options.map((option, i) => ( - <TooltipWrapper condition={Boolean(option.title)} label={option.title}> + <Tooltip label={option.title}> <ToggleButton key={`toggle-${i}-${option.label}`} aria-label={option.title} @@ -109,7 +109,7 @@ const DxcToggleGroup = ({ {option.label && <LabelContainer>{option.label}</LabelContainer>} </DxcFlex> </ToggleButton> - </TooltipWrapper> + </Tooltip> ))} </OptionsContainer> </ToggleGroup> diff --git a/packages/lib/src/tooltip/Tooltip.tsx b/packages/lib/src/tooltip/Tooltip.tsx index fac597ddf..ae46bfdc0 100644 --- a/packages/lib/src/tooltip/Tooltip.tsx +++ b/packages/lib/src/tooltip/Tooltip.tsx @@ -106,9 +106,9 @@ const triangleIcon = ( const TooltipContext = createContext(false); export const Tooltip = ({ + label, hasAdditionalContainer = false, position = "bottom", - label, children, }: { hasAdditionalContainer?: boolean } & TooltipPropsType): JSX.Element => { const hasTooltip = useContext(TooltipContext); diff --git a/packages/lib/src/tooltip/types.tsx b/packages/lib/src/tooltip/types.tsx index ae2ad8bd9..a61ae9491 100644 --- a/packages/lib/src/tooltip/types.tsx +++ b/packages/lib/src/tooltip/types.tsx @@ -14,9 +14,9 @@ type Props = { }; export type TooltipWrapperProps = { - condition: boolean; + condition?: boolean; children: React.ReactNode; - label: string; + label?: string; }; export default Props; From c0bddf3ceccde3000b5dbe678b3644ff52e922f4 Mon Sep 17 00:00:00 2001 From: Mil4n0r <morenocarmonaenrique@gmail.com> Date: Tue, 15 Oct 2024 08:48:14 +0200 Subject: [PATCH 09/11] Added Inset to certain stories with cropped tooltips --- .../lib/src/dropdown/Dropdown.stories.tsx | 19 +++++++++++-------- packages/lib/src/select/Select.stories.tsx | 5 ++++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/lib/src/dropdown/Dropdown.stories.tsx b/packages/lib/src/dropdown/Dropdown.stories.tsx index 09d864c4f..0ed37da47 100644 --- a/packages/lib/src/dropdown/Dropdown.stories.tsx +++ b/packages/lib/src/dropdown/Dropdown.stories.tsx @@ -8,6 +8,7 @@ import useTheme from "../useTheme"; import DxcDropdown from "./Dropdown"; import DropdownMenu from "./DropdownMenu"; import { Option } from "./types"; +import { DxcInset } from ".."; export default { title: "Dropdown", @@ -409,14 +410,16 @@ const OpinionatedTheme = () => ( const TooltipTitle = () => ( <ExampleContainer> <Title title="Tooltip" theme="light" level={3} /> - <DxcDropdown - title="Show options" - options={options} - onSelectOption={(value) => {}} - icon="menu" - caretHidden - margin="large" - /> + <DxcInset bottom="3rem"> + <DxcDropdown + title="Show options" + options={options} + onSelectOption={(value) => {}} + icon="menu" + caretHidden + margin="large" + /> + </DxcInset> </ExampleContainer> ); diff --git a/packages/lib/src/select/Select.stories.tsx b/packages/lib/src/select/Select.stories.tsx index e841a9691..4addfe38a 100644 --- a/packages/lib/src/select/Select.stories.tsx +++ b/packages/lib/src/select/Select.stories.tsx @@ -9,6 +9,7 @@ import { HalstackProvider } from "../HalstackContext"; import useTheme from "../useTheme"; import Listbox from "./Listbox"; import DxcSelect from "./Select"; +import { DxcInset } from ".."; export default { title: "Select", @@ -802,7 +803,9 @@ const TooltipOption = () => { const TooltipClear = () => ( <ExampleContainer> <Title title="Clear action tooltip" theme="light" level={4} /> - <DxcSelect label="Label" options={single_options} multiple defaultValue={["1", "2", "3", "4"]} /> + <DxcInset bottom="3rem"> + <DxcSelect label="Label" options={single_options} multiple defaultValue={["1", "2", "3", "4"]} /> + </DxcInset> </ExampleContainer> ); From 5057d611573d4e33e8f5be3e035fd08ae5065ad2 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, 15 Oct 2024 09:09:59 +0200 Subject: [PATCH 10/11] Adding expanded prop to cropped stories --- .../lib/src/dropdown/Dropdown.stories.tsx | 21 ++++++++----------- packages/lib/src/select/Select.stories.tsx | 7 ++----- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/packages/lib/src/dropdown/Dropdown.stories.tsx b/packages/lib/src/dropdown/Dropdown.stories.tsx index 0ed37da47..32d6117e5 100644 --- a/packages/lib/src/dropdown/Dropdown.stories.tsx +++ b/packages/lib/src/dropdown/Dropdown.stories.tsx @@ -8,7 +8,6 @@ import useTheme from "../useTheme"; import DxcDropdown from "./Dropdown"; import DropdownMenu from "./DropdownMenu"; import { Option } from "./types"; -import { DxcInset } from ".."; export default { title: "Dropdown", @@ -408,18 +407,16 @@ const OpinionatedTheme = () => ( ); const TooltipTitle = () => ( - <ExampleContainer> + <ExampleContainer expanded> <Title title="Tooltip" theme="light" level={3} /> - <DxcInset bottom="3rem"> - <DxcDropdown - title="Show options" - options={options} - onSelectOption={(value) => {}} - icon="menu" - caretHidden - margin="large" - /> - </DxcInset> + <DxcDropdown + title="Show options" + options={options} + onSelectOption={(value) => {}} + icon="menu" + caretHidden + margin="large" + /> </ExampleContainer> ); diff --git a/packages/lib/src/select/Select.stories.tsx b/packages/lib/src/select/Select.stories.tsx index d8a050a05..8c098379c 100644 --- a/packages/lib/src/select/Select.stories.tsx +++ b/packages/lib/src/select/Select.stories.tsx @@ -9,7 +9,6 @@ import { HalstackProvider } from "../HalstackContext"; import useTheme from "../useTheme"; import Listbox from "./Listbox"; import DxcSelect from "./Select"; -import { DxcInset } from ".."; export default { title: "Select", @@ -668,11 +667,9 @@ const TooltipOption = () => { }; const TooltipClear = () => ( - <ExampleContainer> + <ExampleContainer expanded> <Title title="Clear action tooltip" theme="light" level={4} /> - <DxcInset bottom="3rem"> - <DxcSelect label="Label" options={single_options} multiple defaultValue={["1", "2", "3", "4"]} /> - </DxcInset> + <DxcSelect label="Label" options={single_options} multiple defaultValue={["1", "2", "3", "4"]} /> </ExampleContainer> ); From 1bc0f4f490f7aebaa971cae89be5eb113aec6586 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, 15 Oct 2024 10:30:02 +0200 Subject: [PATCH 11/11] Dropdown stories updated --- .../lib/src/dropdown/Dropdown.stories.tsx | 38 ++----------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/packages/lib/src/dropdown/Dropdown.stories.tsx b/packages/lib/src/dropdown/Dropdown.stories.tsx index 32d6117e5..1898f06d8 100644 --- a/packages/lib/src/dropdown/Dropdown.stories.tsx +++ b/packages/lib/src/dropdown/Dropdown.stories.tsx @@ -346,24 +346,6 @@ const DropdownListStates = () => { ); }; -const DropdownRightAlignment = () => ( - <ExampleContainer expanded> - <Title title="Dropdown collisions on the right boundary (right)" theme="light" level={4} /> - <DxcFlex justifyContent="flex-end"> - <DxcDropdown label="Label" options={options} onSelectOption={(value) => {}} /> - </DxcFlex> - </ExampleContainer> -); - -const DropdownCenterAlignment = () => ( - <ExampleContainer expanded> - <Title title="Dropdown collisions on the right boundary (centered)" theme="light" level={4} /> - <DxcFlex justifyContent="flex-end"> - <DxcDropdown label="Label" options={defaultOptions} onSelectOption={(value) => {}} margin="small" /> - </DxcFlex> - </ExampleContainer> -); - const OpinionatedTheme = () => ( <> <Title title="Opinionated theme" theme="light" level={2} /> @@ -380,9 +362,9 @@ const OpinionatedTheme = () => ( </HalstackProvider> </ExampleContainer> <ExampleContainer pseudoState="pseudo-active"> - <Title title="Actived" theme="light" level={4} /> + <Title title="Active" theme="light" level={4} /> <HalstackProvider theme={opinionatedTheme}> - <DxcDropdown label="Actived" options={options} onSelectOption={(value) => {}} icon={iconSVG} /> + <DxcDropdown label="Active" options={options} onSelectOption={(value) => {}} icon={iconSVG} /> </HalstackProvider> </ExampleContainer> <ExampleContainer pseudoState="pseudo-focus"> @@ -427,8 +409,8 @@ Chromatic.play = async ({ canvasElement }) => { await userEvent.click(buttonList[buttonList.length - 1]); }; -export const Themed = OpinionatedTheme.bind({}); -Themed.play = async ({ canvasElement }) => { +export const OpinionatedThemed = OpinionatedTheme.bind({}); +OpinionatedThemed.play = async ({ canvasElement }) => { const canvas = within(canvasElement); const buttonList = canvas.getAllByRole("button"); await userEvent.click(buttonList[buttonList.length - 1]); @@ -440,18 +422,6 @@ MenuStates.play = async ({ canvasElement }) => { await userEvent.click(canvas.getAllByRole("button")[0]); }; -export const MenuAlignedRight = DropdownRightAlignment.bind({}); -MenuAlignedRight.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.click(canvas.getByRole("button")); -}; - -export const MenuAlignedCenter = DropdownCenterAlignment.bind({}); -MenuAlignedCenter.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.click(canvas.getByRole("button")); -}; - export const MenuTooltip = TooltipTitle.bind({}); MenuTooltip.play = async ({ canvasElement }) => { const canvas = within(canvasElement);