From da0bebe68ba0b4f7f3cbf5e2f6fd9a054f1fd4a7 Mon Sep 17 00:00:00 2001 From: george treviranus Date: Wed, 26 Jun 2024 09:55:13 -0500 Subject: [PATCH 1/8] feat(dropdowns): enables menu item as link --- packages/dropdowns/demo/stories/data.ts | 7 ++ packages/dropdowns/src/elements/menu/Item.tsx | 114 +++++++++++++++--- packages/dropdowns/src/elements/menu/utils.ts | 2 + packages/dropdowns/src/types/index.ts | 8 +- .../src/views/combobox/StyledOption.ts | 5 +- packages/dropdowns/src/views/index.ts | 2 + .../src/views/menu/StyledHiddenLabel.ts | 25 ++++ .../dropdowns/src/views/menu/StyledItem.ts | 10 +- .../src/views/menu/StyledItemAnchor.ts | 31 +++++ .../src/views/menu/StyledItemContent.ts | 8 +- .../src/views/menu/StyledItemIcon.ts | 35 +++++- 11 files changed, 219 insertions(+), 28 deletions(-) create mode 100644 packages/dropdowns/src/views/menu/StyledHiddenLabel.ts create mode 100644 packages/dropdowns/src/views/menu/StyledItemAnchor.ts diff --git a/packages/dropdowns/demo/stories/data.ts b/packages/dropdowns/demo/stories/data.ts index 1f1f1f00ea9..f01e29e9a75 100644 --- a/packages/dropdowns/demo/stories/data.ts +++ b/packages/dropdowns/demo/stories/data.ts @@ -21,6 +21,13 @@ export const ITEMS: Items = [ value: 'separator', isSeparator: true }, + { + value: 'item-anchor', + label: 'Item link', + href: 'https://garden.zendesk.com', + externalAnchorLabel: '(opens in new window)', + isExternal: false + }, { value: 'item-meta', label: 'Item', diff --git a/packages/dropdowns/src/elements/menu/Item.tsx b/packages/dropdowns/src/elements/menu/Item.tsx index b0872637e6c..df880bdf085 100644 --- a/packages/dropdowns/src/elements/menu/Item.tsx +++ b/packages/dropdowns/src/elements/menu/Item.tsx @@ -5,30 +5,53 @@ * found at http://www.apache.org/licenses/LICENSE-2.0. */ -import React, { LiHTMLAttributes, MutableRefObject, forwardRef, useMemo } from 'react'; +import React, { + AnchorHTMLAttributes, + LiHTMLAttributes, + MutableRefObject, + forwardRef, + useMemo +} from 'react'; import PropTypes from 'prop-types'; import { mergeRefs } from 'react-merge-refs'; +import { useText } from '@zendeskgarden/react-theming'; import AddIcon from '@zendeskgarden/svg-icons/src/16/plus-stroke.svg'; import NextIcon from '@zendeskgarden/svg-icons/src/16/chevron-right-stroke.svg'; import PreviousIcon from '@zendeskgarden/svg-icons/src/16/chevron-left-stroke.svg'; import CheckedIcon from '@zendeskgarden/svg-icons/src/16/check-lg-stroke.svg'; -import { IItemProps, OptionType as ItemType, OPTION_TYPE } from '../../types'; -import { StyledItem, StyledItemContent, StyledItemIcon, StyledItemTypeIcon } from '../../views'; +import NewWindowIcon from '@zendeskgarden/svg-icons/src/12/new-window-stroke.svg'; +import { IItemProps, OPTION_TYPE, OptionType } from '../../types'; +import { + StyledHiddenLabel, + StyledItem, + StyledItemAnchor, + StyledItemContent, + StyledItemIcon, + StyledItemTypeIcon +} from '../../views'; import { ItemMeta } from './ItemMeta'; import useMenuContext from '../../context/useMenuContext'; import useItemGroupContext from '../../context/useItemGroupContext'; import { ItemContext } from '../../context/useItemContext'; import { toItem } from './utils'; +/** + * 1. role='img' on `svg` is valid WAI-ARIA usage in this context. + * https://dequeuniversity.com/rules/axe/4.2/svg-img-alt + */ + const ItemComponent = forwardRef( ( { children, value, label = value, + href, isSelected, icon, isDisabled, + isExternal, + externalAnchorLabel, type, name, onClick, @@ -47,7 +70,9 @@ const ItemComponent = forwardRef( name, type, isSelected, - isDisabled + isDisabled, + href, + isExternal }), type: selectionType }; @@ -59,10 +84,29 @@ const ItemComponent = forwardRef( onMouseEnter }) as LiHTMLAttributes & { ref: MutableRefObject }; + const hasAnchor = !!href; + const hasExternalLink = hasAnchor && isExternal; + + if (hasAnchor) { + if (type && OPTION_TYPE.includes(type)) { + throw new Error(`Menu item '${value}' can't use type '${type}'`); + } else if (selectionType) { + throw new Error(`Menu item '${value}' can't use selection type '${selectionType}'`); + } + } + + const _externalAnchorLabel = useText( + ItemComponent, + { externalAnchorLabel }, + 'externalAnchorLabel', + '(opens in a new tab)', + hasExternalLink + ); + const isActive = value === focusedValue; - const renderActionIcon = (iconType?: ItemType) => { - switch (iconType) { + const renderActionIcon = (itemType?: OptionType) => { + switch (itemType) { case 'add': return ; @@ -79,25 +123,55 @@ const ItemComponent = forwardRef( const contextValue = useMemo(() => ({ isDisabled, type }), [isDisabled, type]); + const itemChildren = ( + <> + + {renderActionIcon(type)} + + {icon && ( + + {icon} + + )} + + {children || label} + {hasExternalLink && ( + + + + )} + {hasExternalLink && {_externalAnchorLabel}} + + + ); + + const menuItemProps = { + isCompact, + isActive, + $type: type, + ...props, + ...itemProps, + ref: mergeRefs([_itemRef, ref]) + }; + return ( - - {renderActionIcon(type)} - - {icon && ( - - {icon} - + {hasAnchor ? ( + ))} + href={href} + $hasAnchor + > + {itemChildren} + + ) : ( + itemChildren )} - {children || label} ); diff --git a/packages/dropdowns/src/elements/menu/utils.ts b/packages/dropdowns/src/elements/menu/utils.ts index 1f7c664a078..544bb2ecc5a 100644 --- a/packages/dropdowns/src/elements/menu/utils.ts +++ b/packages/dropdowns/src/elements/menu/utils.ts @@ -22,7 +22,9 @@ export const toItem = ( value: props.value, label: props.label, ...(props.name && { name: props.name }), + ...(props.href && { href: props.href }), ...(props.isDisabled && { disabled: props.isDisabled }), + ...(props.isExternal && { isExternal: props.isExternal }), ...(props.isSelected && { selected: props.isSelected }), ...(props.selectionType && { type: props.selectionType }), ...(props.type === 'next' && { isNext: true }), diff --git a/packages/dropdowns/src/types/index.ts b/packages/dropdowns/src/types/index.ts index b13aed95b8e..4a66bf04399 100644 --- a/packages/dropdowns/src/types/index.ts +++ b/packages/dropdowns/src/types/index.ts @@ -278,14 +278,20 @@ export interface IMenuProps extends HTMLAttributes { } export interface IItemProps extends Omit, 'value'> { + /** Provides localized label for external anchor items */ + externalAnchorLabel?: string; /** Accepts an icon to display */ icon?: ReactElement; /** Indicates that the item is not interactive */ isDisabled?: boolean; + /** If the item is an anchor, opens the link externally */ + isExternal?: boolean; /** Determines the initial selection state for the item */ isSelected?: boolean; - /** Sets the text label of the item (defaults to `value`) */ + /** Provides the text label of the item (defaults to `value`) */ label?: string; + /** Sets the item as an anchor */ + href?: string; /** Associates the item in a radio item group */ name?: string; /** Determines the item type */ diff --git a/packages/dropdowns/src/views/combobox/StyledOption.ts b/packages/dropdowns/src/views/combobox/StyledOption.ts index fa4b1838051..6c72194c1ac 100644 --- a/packages/dropdowns/src/views/combobox/StyledOption.ts +++ b/packages/dropdowns/src/views/combobox/StyledOption.ts @@ -15,10 +15,11 @@ const COMPONENT_ID = 'dropdowns.combobox.option'; export interface IStyledOptionProps extends ThemeProps { isActive?: boolean; isCompact?: boolean; + $hasAnchor?: boolean; $type?: OptionType | 'header' | 'group'; } -const colorStyles = ({ theme, isActive, $type }: IStyledOptionProps) => { +const colorStyles = ({ theme, isActive, $type, $hasAnchor }: IStyledOptionProps) => { let backgroundColor; let boxShadow; @@ -33,7 +34,7 @@ const colorStyles = ({ theme, isActive, $type }: IStyledOptionProps) => { let foregroundVariable; - if ($type === 'add') { + if ($hasAnchor || $type === 'add') { foregroundVariable = 'foreground.primary'; } else if ($type === 'danger') { foregroundVariable = 'foreground.danger'; diff --git a/packages/dropdowns/src/views/index.ts b/packages/dropdowns/src/views/index.ts index e459f2e27a3..1f32613efde 100644 --- a/packages/dropdowns/src/views/index.ts +++ b/packages/dropdowns/src/views/index.ts @@ -31,9 +31,11 @@ export * from './combobox/StyledValue'; export * from './menu/StyledMenu'; export * from './menu/StyledFloatingMenu'; export * from './menu/StyledItem'; +export * from './menu/StyledItemAnchor'; export * from './menu/StyledItemContent'; export * from './menu/StyledItemGroup'; export * from './menu/StyledItemIcon'; export * from './menu/StyledItemMeta'; export * from './menu/StyledItemTypeIcon'; +export * from './menu/StyledHiddenLabel'; export * from './menu/StyledSeparator'; diff --git a/packages/dropdowns/src/views/menu/StyledHiddenLabel.ts b/packages/dropdowns/src/views/menu/StyledHiddenLabel.ts new file mode 100644 index 00000000000..a23abbf0329 --- /dev/null +++ b/packages/dropdowns/src/views/menu/StyledHiddenLabel.ts @@ -0,0 +1,25 @@ +/** + * Copyright Zendesk, Inc. + * + * Use of this source code is governed under the Apache License, Version 2.0 + * found at http://www.apache.org/licenses/LICENSE-2.0. + */ + +import styled from 'styled-components'; +import { retrieveComponentStyles, DEFAULT_THEME } from '@zendeskgarden/react-theming'; +import { hideVisually } from 'polished'; + +const COMPONENT_ID = 'dropdowns.menu.item_hidden_label'; + +export const StyledHiddenLabel = styled.span.attrs({ + 'data-garden-id': COMPONENT_ID, + 'data-garden-version': PACKAGE_VERSION +})` + ${hideVisually()} + + ${props => retrieveComponentStyles(COMPONENT_ID, props)}; +`; + +StyledHiddenLabel.defaultProps = { + theme: DEFAULT_THEME +}; diff --git a/packages/dropdowns/src/views/menu/StyledItem.ts b/packages/dropdowns/src/views/menu/StyledItem.ts index 73a8239c516..181d93d19d7 100644 --- a/packages/dropdowns/src/views/menu/StyledItem.ts +++ b/packages/dropdowns/src/views/menu/StyledItem.ts @@ -7,14 +7,20 @@ import styled from 'styled-components'; import { retrieveComponentStyles, DEFAULT_THEME } from '@zendeskgarden/react-theming'; -import { StyledOption } from '../combobox/StyledOption'; +import { IStyledOptionProps, StyledOption } from '../combobox/StyledOption'; const COMPONENT_ID = 'dropdowns.menu.item'; +interface IStyledItemProps extends IStyledOptionProps { + $hasAnchor?: boolean; +} + export const StyledItem = styled(StyledOption).attrs({ 'data-garden-id': COMPONENT_ID, 'data-garden-version': PACKAGE_VERSION -})` +})` + padding: ${p => p.$hasAnchor && 0}; + ${props => retrieveComponentStyles(COMPONENT_ID, props)}; `; diff --git a/packages/dropdowns/src/views/menu/StyledItemAnchor.ts b/packages/dropdowns/src/views/menu/StyledItemAnchor.ts new file mode 100644 index 00000000000..23b9e3118b5 --- /dev/null +++ b/packages/dropdowns/src/views/menu/StyledItemAnchor.ts @@ -0,0 +1,31 @@ +/** + * Copyright Zendesk, Inc. + * + * Use of this source code is governed under the Apache License, Version 2.0 + * found at http://www.apache.org/licenses/LICENSE-2.0. + */ + +import styled from 'styled-components'; +import { retrieveComponentStyles, DEFAULT_THEME } from '@zendeskgarden/react-theming'; +import { StyledOption } from '../combobox/StyledOption'; + +const COMPONENT_ID = 'dropdowns.menu.item_anchor'; + +interface IStyledItemAnchor { + $hasAnchor: boolean; +} + +export const StyledItemAnchor = styled(StyledOption as 'a').attrs({ + 'data-garden-id': COMPONENT_ID, + 'data-garden-version': PACKAGE_VERSION, + as: 'a' +})` + flex: 1; + direction: ${props => props.theme.rtl && 'rtl'}; + + ${props => retrieveComponentStyles(COMPONENT_ID, props)}; +`; + +StyledItemAnchor.defaultProps = { + theme: DEFAULT_THEME +}; diff --git a/packages/dropdowns/src/views/menu/StyledItemContent.ts b/packages/dropdowns/src/views/menu/StyledItemContent.ts index 4db0f90401b..1028193e8f7 100644 --- a/packages/dropdowns/src/views/menu/StyledItemContent.ts +++ b/packages/dropdowns/src/views/menu/StyledItemContent.ts @@ -11,10 +11,16 @@ import { StyledOptionContent } from '../combobox/StyledOptionContent'; const COMPONENT_ID = 'dropdowns.menu.item.content'; +interface IStyledItemContentProps { + $hasExternalLink?: boolean; +} + export const StyledItemContent = styled(StyledOptionContent).attrs({ 'data-garden-id': COMPONENT_ID, 'data-garden-version': PACKAGE_VERSION -})` +})` + flex-direction: ${p => p.$hasExternalLink && 'row'}; + ${props => retrieveComponentStyles(COMPONENT_ID, props)}; `; diff --git a/packages/dropdowns/src/views/menu/StyledItemIcon.ts b/packages/dropdowns/src/views/menu/StyledItemIcon.ts index b84ae305462..82ce77de43c 100644 --- a/packages/dropdowns/src/views/menu/StyledItemIcon.ts +++ b/packages/dropdowns/src/views/menu/StyledItemIcon.ts @@ -5,16 +5,47 @@ * found at http://www.apache.org/licenses/LICENSE-2.0. */ -import styled from 'styled-components'; +import styled, { css, DefaultTheme, ThemeProps } from 'styled-components'; import { retrieveComponentStyles, DEFAULT_THEME } from '@zendeskgarden/react-theming'; import { StyledOptionIcon } from '../combobox/StyledOptionIcon'; const COMPONENT_ID = 'dropdowns.menu.item.icon'; +interface IStyledItemIconProps { + $isExternalLinkIcon?: boolean; +} + +const colorStyles = ({ $isExternalLinkIcon }: IStyledItemIconProps) => { + return css` + color: ${$isExternalLinkIcon && 'inherit'}; + `; +}; + +const sizeStyles = ({ + $isExternalLinkIcon, + theme +}: IStyledItemIconProps & ThemeProps) => { + const size = theme.iconSizes.sm; + const marginHorizontal = `${theme.space.base * 2}px`; + + return css` + align-self: ${$isExternalLinkIcon && 'center'}; + margin-${theme.rtl ? 'right' : 'left'}: ${$isExternalLinkIcon && marginHorizontal}; + width: ${$isExternalLinkIcon && size}; + height: ${$isExternalLinkIcon && size}; + `; +}; + export const StyledItemIcon = styled(StyledOptionIcon).attrs({ 'data-garden-id': COMPONENT_ID, 'data-garden-version': PACKAGE_VERSION -})` +})` + transform: ${p => p.$isExternalLinkIcon && p.theme.rtl && 'scaleX(-1)'}; + + ${sizeStyles} + + ${colorStyles} + ${props => retrieveComponentStyles(COMPONENT_ID, props)}; `; From a2fb9cdbd79f385c8edad38ed4047bb86fe54213 Mon Sep 17 00:00:00 2001 From: Florent Mathieu Date: Mon, 21 Apr 2025 11:35:37 -1000 Subject: [PATCH 2/8] refactor(menu): remove external link icon, fix keyboard behavior on link, clean up --- packages/dropdowns/src/elements/menu/Item.tsx | 89 +++++++------------ packages/dropdowns/src/types/index.ts | 2 - .../src/views/combobox/StyledOption.ts | 5 +- packages/dropdowns/src/views/index.ts | 1 - .../src/views/menu/StyledHiddenLabel.ts | 25 ------ .../dropdowns/src/views/menu/StyledItem.ts | 10 +-- .../src/views/menu/StyledItemAnchor.ts | 16 +--- .../src/views/menu/StyledItemContent.ts | 10 +-- .../src/views/menu/StyledItemIcon.ts | 37 +------- 9 files changed, 46 insertions(+), 149 deletions(-) delete mode 100644 packages/dropdowns/src/views/menu/StyledHiddenLabel.ts diff --git a/packages/dropdowns/src/elements/menu/Item.tsx b/packages/dropdowns/src/elements/menu/Item.tsx index e9dea97206e..48734793e76 100644 --- a/packages/dropdowns/src/elements/menu/Item.tsx +++ b/packages/dropdowns/src/elements/menu/Item.tsx @@ -14,15 +14,13 @@ import React, { } from 'react'; import PropTypes from 'prop-types'; import { mergeRefs } from 'react-merge-refs'; -import { useText } from '@zendeskgarden/react-theming'; import AddIcon from '@zendeskgarden/svg-icons/src/16/plus-stroke.svg'; import NextIcon from '@zendeskgarden/svg-icons/src/16/chevron-right-stroke.svg'; import PreviousIcon from '@zendeskgarden/svg-icons/src/16/chevron-left-stroke.svg'; import CheckedIcon from '@zendeskgarden/svg-icons/src/16/check-lg-stroke.svg'; -import NewWindowIcon from '@zendeskgarden/svg-icons/src/12/new-window-stroke.svg'; + import { IItemProps, OPTION_TYPE, OptionType } from '../../types'; import { - StyledHiddenLabel, StyledItem, StyledItemAnchor, StyledItemContent, @@ -35,6 +33,21 @@ import useItemGroupContext from '../../context/useItemGroupContext'; import { ItemContext } from '../../context/useItemContext'; import { toItem } from './utils'; +const optionType = new Set(OPTION_TYPE); + +const renderActionIcon = (itemType?: OptionType) => { + switch (itemType) { + case 'add': + return ; + case 'next': + return ; + case 'previous': + return ; + default: + return ; + } +}; + /** * 1. role='img' on `svg` is valid WAI-ARIA usage in this context. * https://dequeuniversity.com/rules/axe/4.2/svg-img-alt @@ -50,8 +63,7 @@ const ItemComponent = forwardRef( isSelected, icon, isDisabled, - isExternal, - externalAnchorLabel, + isExternal = true, type, name, onClick, @@ -85,42 +97,15 @@ const ItemComponent = forwardRef( }) as LiHTMLAttributes & { ref: MutableRefObject }; const hasAnchor = !!href; - const hasExternalLink = hasAnchor && isExternal; if (hasAnchor) { - if (type && OPTION_TYPE.includes(type)) { + if (type && optionType.has(type)) { throw new Error(`Menu item '${value}' can't use type '${type}'`); } else if (selectionType) { throw new Error(`Menu item '${value}' can't use selection type '${selectionType}'`); } } - const _externalAnchorLabel = useText( - ItemComponent, - { externalAnchorLabel }, - 'externalAnchorLabel', - '(opens in a new tab)', - hasExternalLink - ); - - const isActive = value === focusedValue; - - const renderActionIcon = (itemType?: OptionType) => { - switch (itemType) { - case 'add': - return ; - - case 'next': - return ; - - case 'previous': - return ; - - default: - return ; - } - }; - const contextValue = useMemo(() => ({ isDisabled, type }), [isDisabled, type]); const itemChildren = ( @@ -133,21 +118,13 @@ const ItemComponent = forwardRef( {icon} )} - - {children || label} - {!!hasExternalLink && ( - - - - )} - {!!hasExternalLink && {_externalAnchorLabel}} - + {children || label} ); const menuItemProps = { - isCompact, - isActive, + $isCompact: isCompact, + $isActive: value === focusedValue, $type: type, ...props, ...itemProps, @@ -156,23 +133,21 @@ const ItemComponent = forwardRef( return ( - - {hasAnchor ? ( + {hasAnchor ? ( +
  • ))} + {...(menuItemProps as AnchorHTMLAttributes)} href={href} - $hasAnchor + target={isExternal ? '_blank' : undefined} + // legacy browsers safeguards + rel={isExternal ? 'noopener noreferrer' : undefined} > {itemChildren} - ) : ( - itemChildren - )} - +
  • + ) : ( + {itemChildren} + )}
    ); } @@ -181,9 +156,11 @@ const ItemComponent = forwardRef( ItemComponent.displayName = 'Item'; ItemComponent.propTypes = { + href: PropTypes.string, icon: PropTypes.any, isDisabled: PropTypes.bool, isSelected: PropTypes.bool, + isExternal: PropTypes.bool, label: PropTypes.string, name: PropTypes.string, type: PropTypes.oneOf(OPTION_TYPE), diff --git a/packages/dropdowns/src/types/index.ts b/packages/dropdowns/src/types/index.ts index 4f6feb52e47..3c77a92a292 100644 --- a/packages/dropdowns/src/types/index.ts +++ b/packages/dropdowns/src/types/index.ts @@ -282,8 +282,6 @@ export interface IMenuProps extends HTMLAttributes { } export interface IItemProps extends Omit, 'value'> { - /** Provides localized label for external anchor items */ - externalAnchorLabel?: string; /** Accepts an icon to display */ icon?: ReactElement; /** Indicates that the item is not interactive */ diff --git a/packages/dropdowns/src/views/combobox/StyledOption.ts b/packages/dropdowns/src/views/combobox/StyledOption.ts index 6aa04f97fbf..dad9ceb2333 100644 --- a/packages/dropdowns/src/views/combobox/StyledOption.ts +++ b/packages/dropdowns/src/views/combobox/StyledOption.ts @@ -13,13 +13,12 @@ import { OptionType } from '../../types'; const COMPONENT_ID = 'dropdowns.combobox.option'; export interface IStyledOptionProps extends ThemeProps { - $hasAnchor?: boolean; $isActive?: boolean; $isCompact?: boolean; $type?: OptionType | 'header' | 'group'; } -const colorStyles = ({ theme, $hasAnchor, $isActive, $type }: IStyledOptionProps) => { +const colorStyles = ({ theme, $isActive, $type }: IStyledOptionProps) => { let backgroundColor; let boxShadow; @@ -34,7 +33,7 @@ const colorStyles = ({ theme, $hasAnchor, $isActive, $type }: IStyledOptionProps let foregroundVariable; - if ($hasAnchor || $type === 'add') { + if ($type === 'add') { foregroundVariable = 'foreground.primary'; } else if ($type === 'danger') { foregroundVariable = 'foreground.danger'; diff --git a/packages/dropdowns/src/views/index.ts b/packages/dropdowns/src/views/index.ts index 90776485a91..5b4e1cfc944 100644 --- a/packages/dropdowns/src/views/index.ts +++ b/packages/dropdowns/src/views/index.ts @@ -38,5 +38,4 @@ export * from './menu/StyledItemGroup'; export * from './menu/StyledItemIcon'; export * from './menu/StyledItemMeta'; export * from './menu/StyledItemTypeIcon'; -export * from './menu/StyledHiddenLabel'; export * from './menu/StyledSeparator'; diff --git a/packages/dropdowns/src/views/menu/StyledHiddenLabel.ts b/packages/dropdowns/src/views/menu/StyledHiddenLabel.ts deleted file mode 100644 index a23abbf0329..00000000000 --- a/packages/dropdowns/src/views/menu/StyledHiddenLabel.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright Zendesk, Inc. - * - * Use of this source code is governed under the Apache License, Version 2.0 - * found at http://www.apache.org/licenses/LICENSE-2.0. - */ - -import styled from 'styled-components'; -import { retrieveComponentStyles, DEFAULT_THEME } from '@zendeskgarden/react-theming'; -import { hideVisually } from 'polished'; - -const COMPONENT_ID = 'dropdowns.menu.item_hidden_label'; - -export const StyledHiddenLabel = styled.span.attrs({ - 'data-garden-id': COMPONENT_ID, - 'data-garden-version': PACKAGE_VERSION -})` - ${hideVisually()} - - ${props => retrieveComponentStyles(COMPONENT_ID, props)}; -`; - -StyledHiddenLabel.defaultProps = { - theme: DEFAULT_THEME -}; diff --git a/packages/dropdowns/src/views/menu/StyledItem.ts b/packages/dropdowns/src/views/menu/StyledItem.ts index 5e6363e74dc..ddac8a8c915 100644 --- a/packages/dropdowns/src/views/menu/StyledItem.ts +++ b/packages/dropdowns/src/views/menu/StyledItem.ts @@ -7,19 +7,13 @@ import styled from 'styled-components'; import { componentStyles } from '@zendeskgarden/react-theming'; -import { IStyledOptionProps, StyledOption } from '../combobox/StyledOption'; +import { StyledOption } from '../combobox/StyledOption'; const COMPONENT_ID = 'dropdowns.menu.item'; -interface IStyledItemProps extends IStyledOptionProps { - $hasAnchor?: boolean; -} - export const StyledItem = styled(StyledOption).attrs({ 'data-garden-id': COMPONENT_ID, 'data-garden-version': PACKAGE_VERSION -})` - padding: ${p => p.$hasAnchor && 0}; - +})` ${componentStyles}; `; diff --git a/packages/dropdowns/src/views/menu/StyledItemAnchor.ts b/packages/dropdowns/src/views/menu/StyledItemAnchor.ts index 23b9e3118b5..9fedcfd3acc 100644 --- a/packages/dropdowns/src/views/menu/StyledItemAnchor.ts +++ b/packages/dropdowns/src/views/menu/StyledItemAnchor.ts @@ -6,26 +6,18 @@ */ import styled from 'styled-components'; -import { retrieveComponentStyles, DEFAULT_THEME } from '@zendeskgarden/react-theming'; +import { componentStyles, getColor } from '@zendeskgarden/react-theming'; import { StyledOption } from '../combobox/StyledOption'; const COMPONENT_ID = 'dropdowns.menu.item_anchor'; -interface IStyledItemAnchor { - $hasAnchor: boolean; -} - export const StyledItemAnchor = styled(StyledOption as 'a').attrs({ 'data-garden-id': COMPONENT_ID, 'data-garden-version': PACKAGE_VERSION, as: 'a' -})` - flex: 1; +})` direction: ${props => props.theme.rtl && 'rtl'}; + color: ${({ theme }) => getColor({ theme, variable: 'foreground.primary' })}; - ${props => retrieveComponentStyles(COMPONENT_ID, props)}; + ${componentStyles}; `; - -StyledItemAnchor.defaultProps = { - theme: DEFAULT_THEME -}; diff --git a/packages/dropdowns/src/views/menu/StyledItemContent.ts b/packages/dropdowns/src/views/menu/StyledItemContent.ts index affcffdf751..fc7f32cf709 100644 --- a/packages/dropdowns/src/views/menu/StyledItemContent.ts +++ b/packages/dropdowns/src/views/menu/StyledItemContent.ts @@ -11,15 +11,9 @@ import { StyledOptionContent } from '../combobox/StyledOptionContent'; const COMPONENT_ID = 'dropdowns.menu.item.content'; -interface IStyledItemContentProps { - $hasExternalLink?: boolean; -} - export const StyledItemContent = styled(StyledOptionContent).attrs({ 'data-garden-id': COMPONENT_ID, 'data-garden-version': PACKAGE_VERSION -})` - flex-direction: ${p => p.$hasExternalLink && 'row'}; - +})` ${componentStyles}; -})`; +`; diff --git a/packages/dropdowns/src/views/menu/StyledItemIcon.ts b/packages/dropdowns/src/views/menu/StyledItemIcon.ts index fc11eb96803..e4968e3fec6 100644 --- a/packages/dropdowns/src/views/menu/StyledItemIcon.ts +++ b/packages/dropdowns/src/views/menu/StyledItemIcon.ts @@ -5,46 +5,15 @@ * found at http://www.apache.org/licenses/LICENSE-2.0. */ -import styled, { css, DefaultTheme, ThemeProps } from 'styled-components'; +import styled from 'styled-components'; import { componentStyles } from '@zendeskgarden/react-theming'; import { StyledOptionIcon } from '../combobox/StyledOptionIcon'; const COMPONENT_ID = 'dropdowns.menu.item.icon'; -interface IStyledItemIconProps { - $isExternalLinkIcon?: boolean; -} - -const colorStyles = ({ $isExternalLinkIcon }: IStyledItemIconProps) => { - return css` - color: ${$isExternalLinkIcon && 'inherit'}; - `; -}; - -const sizeStyles = ({ - $isExternalLinkIcon, - theme -}: IStyledItemIconProps & ThemeProps) => { - const size = theme.iconSizes.sm; - const marginHorizontal = `${theme.space.base * 2}px`; - - return css` - align-self: ${$isExternalLinkIcon && 'center'}; - margin-${theme.rtl ? 'right' : 'left'}: ${$isExternalLinkIcon && marginHorizontal}; - width: ${$isExternalLinkIcon && size}; - height: ${$isExternalLinkIcon && size}; - `; -}; - export const StyledItemIcon = styled(StyledOptionIcon).attrs({ 'data-garden-id': COMPONENT_ID, 'data-garden-version': PACKAGE_VERSION -})` - transform: ${p => p.$isExternalLinkIcon && p.theme.rtl && 'scaleX(-1)'}; - - ${sizeStyles} - - ${colorStyles} - +})` ${componentStyles}; -})`; +`; From cf77156c0098ea4080631364e56c038cd24cc8f9 Mon Sep 17 00:00:00 2001 From: Florent Mathieu Date: Mon, 21 Apr 2025 12:06:53 -1000 Subject: [PATCH 3/8] test(Menu): add Item with link specs --- packages/dropdowns/src/elements/menu/Item.tsx | 14 ++-- .../dropdowns/src/elements/menu/Menu.spec.tsx | 64 +++++++++++++++++++ 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/packages/dropdowns/src/elements/menu/Item.tsx b/packages/dropdowns/src/elements/menu/Item.tsx index 48734793e76..fb71f5be2f6 100644 --- a/packages/dropdowns/src/elements/menu/Item.tsx +++ b/packages/dropdowns/src/elements/menu/Item.tsx @@ -89,13 +89,6 @@ const ItemComponent = forwardRef( type: selectionType }; - const { ref: _itemRef, ...itemProps } = getItemProps({ - item, - onClick, - onKeyDown, - onMouseEnter - }) as LiHTMLAttributes & { ref: MutableRefObject }; - const hasAnchor = !!href; if (hasAnchor) { @@ -106,6 +99,13 @@ const ItemComponent = forwardRef( } } + const { ref: _itemRef, ...itemProps } = getItemProps({ + item, + onClick, + onKeyDown, + onMouseEnter + }) as LiHTMLAttributes & { ref: MutableRefObject }; + const contextValue = useMemo(() => ({ isDisabled, type }), [isDisabled, type]); const itemChildren = ( diff --git a/packages/dropdowns/src/elements/menu/Menu.spec.tsx b/packages/dropdowns/src/elements/menu/Menu.spec.tsx index 9166c4470ba..bf74269191f 100644 --- a/packages/dropdowns/src/elements/menu/Menu.spec.tsx +++ b/packages/dropdowns/src/elements/menu/Menu.spec.tsx @@ -686,4 +686,68 @@ describe('Menu', () => { expect(button).toHaveAttribute('data-garden-id', 'buttons.button'); }); }); + + describe('Item link behavior', () => { + it('renders with href as anchor tag', async () => { + const { getByTestId } = render( + + + Example Link + + + ); + await floating(); + const item = getByTestId('item'); + expect(item.tagName).toBe('A'); + expect(item).toHaveAttribute('href', 'https://example.com'); + expect(item).toHaveAttribute('target', '_blank'); + expect(item).toHaveAttribute('rel', 'noopener noreferrer'); + }); + + it('renders with isExternal=false correctly', async () => { + const { getByTestId } = render( + + + Internal Link + + + ); + await floating(); + const item = getByTestId('item'); + expect(item.tagName).toBe('A'); + expect(item).toHaveAttribute('href', 'https://example.com'); + expect(item).not.toHaveAttribute('target'); + expect(item).not.toHaveAttribute('rel'); + }); + + it('throws error when href is used with a selection type', () => { + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + + expect(() => { + render( + + + + + + ); + }).toThrow(/can't use selection type/u); + + consoleSpy.mockRestore(); + }); + + it('throws error when href is used with option type', () => { + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + + expect(() => { + render( + + + + ); + }).toThrow(/can't use type/u); + + consoleSpy.mockRestore(); + }); + }); }); From 15e0ba4f51cb1194f703f24c770ce471eb825449 Mon Sep 17 00:00:00 2001 From: Florent Mathieu Date: Mon, 21 Apr 2025 12:07:26 -1000 Subject: [PATCH 4/8] chore: clean up story data --- packages/dropdowns/demo/stories/data.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/dropdowns/demo/stories/data.ts b/packages/dropdowns/demo/stories/data.ts index 9b9b16ed892..7e4c0a0d3ae 100644 --- a/packages/dropdowns/demo/stories/data.ts +++ b/packages/dropdowns/demo/stories/data.ts @@ -26,9 +26,7 @@ export const ITEMS: Items = [ { value: 'item-anchor', label: 'Item link', - href: 'https://garden.zendesk.com', - externalAnchorLabel: '(opens in new window)', - isExternal: false + href: 'https://garden.zendesk.com' }, { value: 'item-meta', From c4b0891375ed374ab7d890e347c00bd21d651d79 Mon Sep 17 00:00:00 2001 From: Florent Mathieu Date: Tue, 22 Apr 2025 14:20:07 -1000 Subject: [PATCH 5/8] chore: PR feedback --- packages/dropdowns/demo/stories/data.ts | 3 ++- packages/dropdowns/src/elements/menu/Item.tsx | 2 +- packages/dropdowns/src/elements/menu/Menu.spec.tsx | 2 +- packages/dropdowns/src/types/index.ts | 2 +- packages/dropdowns/src/views/menu/StyledItemAnchor.ts | 5 +++-- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/dropdowns/demo/stories/data.ts b/packages/dropdowns/demo/stories/data.ts index 7e4c0a0d3ae..800bdfbb1e7 100644 --- a/packages/dropdowns/demo/stories/data.ts +++ b/packages/dropdowns/demo/stories/data.ts @@ -26,7 +26,8 @@ export const ITEMS: Items = [ { value: 'item-anchor', label: 'Item link', - href: 'https://garden.zendesk.com' + href: 'https://garden.zendesk.com', + isExternal: true }, { value: 'item-meta', diff --git a/packages/dropdowns/src/elements/menu/Item.tsx b/packages/dropdowns/src/elements/menu/Item.tsx index fb71f5be2f6..f85d07c8469 100644 --- a/packages/dropdowns/src/elements/menu/Item.tsx +++ b/packages/dropdowns/src/elements/menu/Item.tsx @@ -63,7 +63,7 @@ const ItemComponent = forwardRef( isSelected, icon, isDisabled, - isExternal = true, + isExternal, type, name, onClick, diff --git a/packages/dropdowns/src/elements/menu/Menu.spec.tsx b/packages/dropdowns/src/elements/menu/Menu.spec.tsx index bf74269191f..093c0576773 100644 --- a/packages/dropdowns/src/elements/menu/Menu.spec.tsx +++ b/packages/dropdowns/src/elements/menu/Menu.spec.tsx @@ -691,7 +691,7 @@ describe('Menu', () => { it('renders with href as anchor tag', async () => { const { getByTestId } = render( - + Example Link diff --git a/packages/dropdowns/src/types/index.ts b/packages/dropdowns/src/types/index.ts index 3c77a92a292..c6e21dd8917 100644 --- a/packages/dropdowns/src/types/index.ts +++ b/packages/dropdowns/src/types/index.ts @@ -286,7 +286,7 @@ export interface IItemProps extends Omit, 'value icon?: ReactElement; /** Indicates that the item is not interactive */ isDisabled?: boolean; - /** If the item is an anchor, opens the link externally */ + /** Opens the `href` externally */ isExternal?: boolean; /** Determines the initial selection state for the item */ isSelected?: boolean; diff --git a/packages/dropdowns/src/views/menu/StyledItemAnchor.ts b/packages/dropdowns/src/views/menu/StyledItemAnchor.ts index 9fedcfd3acc..e88625c8c7c 100644 --- a/packages/dropdowns/src/views/menu/StyledItemAnchor.ts +++ b/packages/dropdowns/src/views/menu/StyledItemAnchor.ts @@ -6,7 +6,7 @@ */ import styled from 'styled-components'; -import { componentStyles, getColor } from '@zendeskgarden/react-theming'; +import { componentStyles } from '@zendeskgarden/react-theming'; import { StyledOption } from '../combobox/StyledOption'; const COMPONENT_ID = 'dropdowns.menu.item_anchor'; @@ -17,7 +17,8 @@ export const StyledItemAnchor = styled(StyledOption as 'a').attrs({ as: 'a' })` direction: ${props => props.theme.rtl && 'rtl'}; - color: ${({ theme }) => getColor({ theme, variable: 'foreground.primary' })}; + text-decoration: none; + color: unset; ${componentStyles}; `; From dd36fc700c5b11ca6ae4bf13d8c010322d6aefeb Mon Sep 17 00:00:00 2001 From: Florent Mathieu Date: Thu, 8 May 2025 07:56:11 -1000 Subject: [PATCH 6/8] refactor(dropdowns menu): use container-menu getAnchorProps getter --- .../dropdowns/src/context/useMenuContext.ts | 1 + packages/dropdowns/src/elements/menu/Item.tsx | 50 +++++++--------- .../dropdowns/src/elements/menu/Menu.spec.tsx | 57 ++++++++++++++++--- packages/dropdowns/src/elements/menu/Menu.tsx | 4 +- packages/dropdowns/src/elements/menu/utils.ts | 2 +- .../src/views/menu/StyledItemAnchor.ts | 6 +- 6 files changed, 78 insertions(+), 42 deletions(-) diff --git a/packages/dropdowns/src/context/useMenuContext.ts b/packages/dropdowns/src/context/useMenuContext.ts index f223c45f741..980734b33bc 100644 --- a/packages/dropdowns/src/context/useMenuContext.ts +++ b/packages/dropdowns/src/context/useMenuContext.ts @@ -12,6 +12,7 @@ export const MenuContext = createContext< | { isCompact?: boolean; focusedValue?: string | null; + getAnchorProps: IUseMenuReturnValue['getAnchorProps']; getItemGroupProps: IUseMenuReturnValue['getItemGroupProps']; getItemProps: IUseMenuReturnValue['getItemProps']; getSeparatorProps: IUseMenuReturnValue['getSeparatorProps']; diff --git a/packages/dropdowns/src/elements/menu/Item.tsx b/packages/dropdowns/src/elements/menu/Item.tsx index f85d07c8469..c0a6c1923bd 100644 --- a/packages/dropdowns/src/elements/menu/Item.tsx +++ b/packages/dropdowns/src/elements/menu/Item.tsx @@ -5,13 +5,7 @@ * found at http://www.apache.org/licenses/LICENSE-2.0. */ -import React, { - AnchorHTMLAttributes, - LiHTMLAttributes, - MutableRefObject, - forwardRef, - useMemo -} from 'react'; +import React, { LiHTMLAttributes, MutableRefObject, forwardRef, useMemo } from 'react'; import PropTypes from 'prop-types'; import { mergeRefs } from 'react-merge-refs'; import AddIcon from '@zendeskgarden/svg-icons/src/16/plus-stroke.svg'; @@ -33,8 +27,6 @@ import useItemGroupContext from '../../context/useItemGroupContext'; import { ItemContext } from '../../context/useItemContext'; import { toItem } from './utils'; -const optionType = new Set(OPTION_TYPE); - const renderActionIcon = (itemType?: OptionType) => { switch (itemType) { case 'add': @@ -69,12 +61,12 @@ const ItemComponent = forwardRef( onClick, onKeyDown, onMouseEnter, - ...props + ...other }, ref ) => { const { type: selectionType } = useItemGroupContext(); - const { focusedValue, getItemProps, isCompact } = useMenuContext(); + const { focusedValue, getAnchorProps, getItemProps, isCompact } = useMenuContext(); const item = { ...toItem({ value, @@ -89,14 +81,10 @@ const ItemComponent = forwardRef( type: selectionType }; - const hasAnchor = !!href; + const anchorProps = getAnchorProps({ item }); - if (hasAnchor) { - if (type && optionType.has(type)) { - throw new Error(`Menu item '${value}' can't use type '${type}'`); - } else if (selectionType) { - throw new Error(`Menu item '${value}' can't use selection type '${selectionType}'`); - } + if (anchorProps && (type === 'add' || type === 'danger')) { + throw new Error(`Menu link item '${value}' can't use type '${type}'`); } const { ref: _itemRef, ...itemProps } = getItemProps({ @@ -123,30 +111,32 @@ const ItemComponent = forwardRef( ); const menuItemProps = { - $isCompact: isCompact, - $isActive: value === focusedValue, - $type: type, - ...props, + ...other, ...itemProps, ref: mergeRefs([_itemRef, ref]) }; return ( - {hasAnchor ? ( -
  • + {anchorProps ? ( +
  • )} - href={href} - target={isExternal ? '_blank' : undefined} - // legacy browsers safeguards - rel={isExternal ? 'noopener noreferrer' : undefined} + $isCompact={isCompact} + $isActive={value === focusedValue} + {...anchorProps} > {itemChildren}
  • ) : ( - {itemChildren} + + {itemChildren} + )}
    ); diff --git a/packages/dropdowns/src/elements/menu/Menu.spec.tsx b/packages/dropdowns/src/elements/menu/Menu.spec.tsx index 093c0576773..d3200efe6fd 100644 --- a/packages/dropdowns/src/elements/menu/Menu.spec.tsx +++ b/packages/dropdowns/src/elements/menu/Menu.spec.tsx @@ -698,10 +698,13 @@ describe('Menu', () => { ); await floating(); const item = getByTestId('item'); - expect(item.tagName).toBe('A'); - expect(item).toHaveAttribute('href', 'https://example.com'); - expect(item).toHaveAttribute('target', '_blank'); - expect(item).toHaveAttribute('rel', 'noopener noreferrer'); + const link = item.firstChild!; + + expect(item.nodeName).toBe('LI'); + expect(link.nodeName).toBe('A'); + expect(link).toHaveAttribute('href', 'https://example.com'); + expect(link).toHaveAttribute('target', '_blank'); + expect(link).toHaveAttribute('rel', 'noopener noreferrer'); }); it('renders with isExternal=false correctly', async () => { @@ -714,10 +717,46 @@ describe('Menu', () => { ); await floating(); const item = getByTestId('item'); - expect(item.tagName).toBe('A'); - expect(item).toHaveAttribute('href', 'https://example.com'); - expect(item).not.toHaveAttribute('target'); - expect(item).not.toHaveAttribute('rel'); + const link = item.firstChild!; + + expect(item.nodeName).toBe('LI'); + expect(link.nodeName).toBe('A'); + expect(link).toHaveAttribute('href', 'https://example.com'); + expect(link).not.toHaveAttribute('target'); + expect(link).not.toHaveAttribute('rel'); + }); + + it('renders with isSelected correctly', async () => { + const { getByTestId } = render( + + + Example Link + + + ); + await floating(); + const link = getByTestId('item').firstChild!; + + expect(link.nodeName).toBe('A'); + expect(link).toHaveAttribute('href', 'https://example.com'); + expect(link).toHaveAttribute('aria-current', 'page'); + }); + + it('renders with controlled selection correctly', async () => { + const { getByTestId } = render( + + + Link 1 + + + Link 2 + + + ); + await floating(); + + expect(getByTestId('item-01').firstChild).toHaveAttribute('aria-current', 'page'); + expect(getByTestId('item-02').firstChild).not.toHaveAttribute('aria-current'); }); it('throws error when href is used with a selection type', () => { @@ -731,7 +770,7 @@ describe('Menu', () => { ); - }).toThrow(/can't use selection type/u); + }).toThrow("Error: expected useMenu anchor item 'Flower' to not use 'checkbox'"); consoleSpy.mockRestore(); }); diff --git a/packages/dropdowns/src/elements/menu/Menu.tsx b/packages/dropdowns/src/elements/menu/Menu.tsx index 5381ebb3f58..c0d6bdbc3d6 100644 --- a/packages/dropdowns/src/elements/menu/Menu.tsx +++ b/packages/dropdowns/src/elements/menu/Menu.tsx @@ -52,6 +52,7 @@ export const Menu = forwardRef( focusedValue, getTriggerProps, getMenuProps, + getAnchorProps, getItemProps, getItemGroupProps, getSeparatorProps @@ -106,11 +107,12 @@ export const Menu = forwardRef( () => ({ isCompact, focusedValue, + getAnchorProps, getItemProps, getItemGroupProps, getSeparatorProps }), - [isCompact, focusedValue, getItemProps, getItemGroupProps, getSeparatorProps] + [focusedValue, getAnchorProps, getItemGroupProps, getItemProps, getSeparatorProps, isCompact] ); return ( diff --git a/packages/dropdowns/src/elements/menu/utils.ts b/packages/dropdowns/src/elements/menu/utils.ts index 544bb2ecc5a..cb9d7990aaa 100644 --- a/packages/dropdowns/src/elements/menu/utils.ts +++ b/packages/dropdowns/src/elements/menu/utils.ts @@ -24,7 +24,7 @@ export const toItem = ( ...(props.name && { name: props.name }), ...(props.href && { href: props.href }), ...(props.isDisabled && { disabled: props.isDisabled }), - ...(props.isExternal && { isExternal: props.isExternal }), + ...(props.isExternal && { external: props.isExternal }), ...(props.isSelected && { selected: props.isSelected }), ...(props.selectionType && { type: props.selectionType }), ...(props.type === 'next' && { isNext: true }), diff --git a/packages/dropdowns/src/views/menu/StyledItemAnchor.ts b/packages/dropdowns/src/views/menu/StyledItemAnchor.ts index e88625c8c7c..e3fd30be0b7 100644 --- a/packages/dropdowns/src/views/menu/StyledItemAnchor.ts +++ b/packages/dropdowns/src/views/menu/StyledItemAnchor.ts @@ -7,11 +7,12 @@ import styled from 'styled-components'; import { componentStyles } from '@zendeskgarden/react-theming'; +import { StyledItemTypeIcon } from './StyledItemTypeIcon'; import { StyledOption } from '../combobox/StyledOption'; const COMPONENT_ID = 'dropdowns.menu.item_anchor'; -export const StyledItemAnchor = styled(StyledOption as 'a').attrs({ +export const StyledItemAnchor = styled(StyledOption).attrs({ 'data-garden-id': COMPONENT_ID, 'data-garden-version': PACKAGE_VERSION, as: 'a' @@ -19,6 +20,9 @@ export const StyledItemAnchor = styled(StyledOption as 'a').attrs({ direction: ${props => props.theme.rtl && 'rtl'}; text-decoration: none; color: unset; + &[aria-current='page'] > ${StyledItemTypeIcon} { + opacity: 1; + } ${componentStyles}; `; From 69c8c23306c85206402543622aeec3c9e636e1ae Mon Sep 17 00:00:00 2001 From: Florent Mathieu Date: Tue, 13 May 2025 20:46:52 -1000 Subject: [PATCH 7/8] deps(dropdowns): upgrade to @zendeskgarden/container-menu@1.0.0 --- package-lock.json | 8 ++++---- packages/dropdowns/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2bc2f7e0712..b53356f4b9e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12197,9 +12197,9 @@ } }, "node_modules/@zendeskgarden/container-menu": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@zendeskgarden/container-menu/-/container-menu-0.5.1.tgz", - "integrity": "sha512-ctbuQGHSjmsGqKmJ9uyk1TvUjA4Im6xF64VpLwJhYwsmQV4ddp3/tMxu3t2gaB+cG9GsjJLzGVOuTfjwhVQlJg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@zendeskgarden/container-menu/-/container-menu-1.0.0.tgz", + "integrity": "sha512-GnG3IqKBmuNkbnDp9bHnwHpGyy/4/MUe3RmaDllVQNyG+uP0BsGondCRWZQE1ULwjfbGBeadAawQhEJcdoSPSA==", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.8.4", @@ -51904,7 +51904,7 @@ "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@zendeskgarden/container-combobox": "^2.0.0", - "@zendeskgarden/container-menu": "^0.5.1", + "@zendeskgarden/container-menu": "1.0.0", "@zendeskgarden/container-utilities": "^2.0.0", "@zendeskgarden/react-buttons": "^9.5.4", "@zendeskgarden/react-forms": "^9.5.4", diff --git a/packages/dropdowns/package.json b/packages/dropdowns/package.json index 7f4d60dc9ee..ab1011a759e 100644 --- a/packages/dropdowns/package.json +++ b/packages/dropdowns/package.json @@ -23,7 +23,7 @@ "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@zendeskgarden/container-combobox": "^2.0.0", - "@zendeskgarden/container-menu": "^0.5.1", + "@zendeskgarden/container-menu": "1.0.0", "@zendeskgarden/container-utilities": "^2.0.0", "@zendeskgarden/react-buttons": "^9.5.4", "@zendeskgarden/react-forms": "^9.5.4", From b135533733476845790addd46a27d92335e361c5 Mon Sep 17 00:00:00 2001 From: Florent Mathieu Date: Wed, 14 May 2025 11:32:26 -1000 Subject: [PATCH 8/8] chore: PR feedback --- package-lock.json | 2 +- packages/dropdowns/package.json | 2 +- packages/dropdowns/src/views/menu/StyledItemAnchor.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b53356f4b9e..121d465776b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51904,7 +51904,7 @@ "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@zendeskgarden/container-combobox": "^2.0.0", - "@zendeskgarden/container-menu": "1.0.0", + "@zendeskgarden/container-menu": "^1.0.0", "@zendeskgarden/container-utilities": "^2.0.0", "@zendeskgarden/react-buttons": "^9.5.4", "@zendeskgarden/react-forms": "^9.5.4", diff --git a/packages/dropdowns/package.json b/packages/dropdowns/package.json index ab1011a759e..4f6dec8ac30 100644 --- a/packages/dropdowns/package.json +++ b/packages/dropdowns/package.json @@ -23,7 +23,7 @@ "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@zendeskgarden/container-combobox": "^2.0.0", - "@zendeskgarden/container-menu": "1.0.0", + "@zendeskgarden/container-menu": "^1.0.0", "@zendeskgarden/container-utilities": "^2.0.0", "@zendeskgarden/react-buttons": "^9.5.4", "@zendeskgarden/react-forms": "^9.5.4", diff --git a/packages/dropdowns/src/views/menu/StyledItemAnchor.ts b/packages/dropdowns/src/views/menu/StyledItemAnchor.ts index e3fd30be0b7..f9871566cb1 100644 --- a/packages/dropdowns/src/views/menu/StyledItemAnchor.ts +++ b/packages/dropdowns/src/views/menu/StyledItemAnchor.ts @@ -17,9 +17,9 @@ export const StyledItemAnchor = styled(StyledOption).attrs({ 'data-garden-version': PACKAGE_VERSION, as: 'a' })` - direction: ${props => props.theme.rtl && 'rtl'}; text-decoration: none; color: unset; + &[aria-current='page'] > ${StyledItemTypeIcon} { opacity: 1; }