@@ -12,93 +12,132 @@ import AddIcon from '@zendeskgarden/svg-icons/src/16/plus-stroke.svg';
1212import NextIcon from '@zendeskgarden/svg-icons/src/16/chevron-right-stroke.svg' ;
1313import PreviousIcon from '@zendeskgarden/svg-icons/src/16/chevron-left-stroke.svg' ;
1414import CheckedIcon from '@zendeskgarden/svg-icons/src/16/check-lg-stroke.svg' ;
15- import { IItemProps , OptionType as ItemType , OPTION_TYPE } from '../../types' ;
16- import { StyledItem , StyledItemContent , StyledItemIcon , StyledItemTypeIcon } from '../../views' ;
15+
16+ import { IItemProps , OPTION_TYPE , OptionType } from '../../types' ;
17+ import {
18+ StyledItem ,
19+ StyledItemAnchor ,
20+ StyledItemContent ,
21+ StyledItemIcon ,
22+ StyledItemTypeIcon
23+ } from '../../views' ;
1724import { ItemMeta } from './ItemMeta' ;
1825import useMenuContext from '../../context/useMenuContext' ;
1926import useItemGroupContext from '../../context/useItemGroupContext' ;
2027import { ItemContext } from '../../context/useItemContext' ;
2128import { toItem } from './utils' ;
2229
30+ const renderActionIcon = ( itemType ?: OptionType ) => {
31+ switch ( itemType ) {
32+ case 'add' :
33+ return < AddIcon /> ;
34+ case 'next' :
35+ return < NextIcon /> ;
36+ case 'previous' :
37+ return < PreviousIcon /> ;
38+ default :
39+ return < CheckedIcon /> ;
40+ }
41+ } ;
42+
43+ /**
44+ * 1. role='img' on `svg` is valid WAI-ARIA usage in this context.
45+ * https://dequeuniversity.com/rules/axe/4.2/svg-img-alt
46+ */
47+
2348const ItemComponent = forwardRef < HTMLLIElement , IItemProps > (
2449 (
2550 {
2651 children,
2752 value,
2853 label = value ,
54+ href,
2955 isSelected,
3056 icon,
3157 isDisabled,
58+ isExternal,
3259 type,
3360 name,
3461 onClick,
3562 onKeyDown,
3663 onMouseEnter,
37- ...props
64+ ...other
3865 } ,
3966 ref
4067 ) => {
4168 const { type : selectionType } = useItemGroupContext ( ) ;
42- const { focusedValue, getItemProps, isCompact } = useMenuContext ( ) ;
69+ const { focusedValue, getAnchorProps , getItemProps, isCompact } = useMenuContext ( ) ;
4370 const item = {
4471 ...toItem ( {
4572 value,
4673 label,
4774 name,
4875 type,
4976 isSelected,
50- isDisabled
77+ isDisabled,
78+ href,
79+ isExternal
5180 } ) ,
5281 type : selectionType
5382 } ;
5483
84+ const anchorProps = getAnchorProps ( { item } ) ;
85+
86+ if ( anchorProps && ( type === 'add' || type === 'danger' ) ) {
87+ throw new Error ( `Menu link item '${ value } ' can't use type '${ type } '` ) ;
88+ }
89+
5590 const { ref : _itemRef , ...itemProps } = getItemProps ( {
5691 item,
5792 onClick,
5893 onKeyDown,
5994 onMouseEnter
6095 } ) as LiHTMLAttributes < HTMLLIElement > & { ref : MutableRefObject < HTMLLIElement > } ;
6196
62- const isActive = value === focusedValue ;
63-
64- const renderActionIcon = ( iconType ?: ItemType ) => {
65- switch ( iconType ) {
66- case 'add' :
67- return < AddIcon /> ;
68-
69- case 'next' :
70- return < NextIcon /> ;
97+ const contextValue = useMemo ( ( ) => ( { isDisabled, type } ) , [ isDisabled , type ] ) ;
7198
72- case 'previous' :
73- return < PreviousIcon /> ;
99+ const itemChildren = (
100+ < >
101+ < StyledItemTypeIcon $isCompact = { isCompact } $type = { type } >
102+ { renderActionIcon ( type ) }
103+ </ StyledItemTypeIcon >
104+ { ! ! icon && (
105+ < StyledItemIcon $isDisabled = { isDisabled } $type = { type } >
106+ { icon }
107+ </ StyledItemIcon >
108+ ) }
109+ < StyledItemContent > { children || label } </ StyledItemContent >
110+ </ >
111+ ) ;
74112
75- default :
76- return < CheckedIcon /> ;
77- }
113+ const menuItemProps = {
114+ ...other ,
115+ ...itemProps ,
116+ ref : mergeRefs ( [ _itemRef , ref ] )
78117 } ;
79118
80- const contextValue = useMemo ( ( ) => ( { isDisabled, type } ) , [ isDisabled , type ] ) ;
81-
82119 return (
83120 < ItemContext . Provider value = { contextValue } >
84- < StyledItem
85- $type = { type }
86- $isCompact = { isCompact }
87- $isActive = { isActive }
88- { ...props }
89- { ...itemProps }
90- ref = { mergeRefs ( [ _itemRef , ref ] ) }
91- >
92- < StyledItemTypeIcon $isCompact = { isCompact } $type = { type } >
93- { renderActionIcon ( type ) }
94- </ StyledItemTypeIcon >
95- { ! ! icon && (
96- < StyledItemIcon $isDisabled = { isDisabled } $type = { type } >
97- { icon }
98- </ StyledItemIcon >
99- ) }
100- < StyledItemContent > { children || label } </ StyledItemContent >
101- </ StyledItem >
121+ { anchorProps ? (
122+ < li { ...menuItemProps } >
123+ < StyledItemAnchor
124+ $isCompact = { isCompact }
125+ $isActive = { value === focusedValue }
126+ { ...anchorProps }
127+ >
128+ { itemChildren }
129+ </ StyledItemAnchor >
130+ </ li >
131+ ) : (
132+ < StyledItem
133+ $isCompact = { isCompact }
134+ $isActive = { value === focusedValue }
135+ $type = { type }
136+ { ...menuItemProps }
137+ >
138+ { itemChildren }
139+ </ StyledItem >
140+ ) }
102141 </ ItemContext . Provider >
103142 ) ;
104143 }
@@ -107,9 +146,11 @@ const ItemComponent = forwardRef<HTMLLIElement, IItemProps>(
107146ItemComponent . displayName = 'Item' ;
108147
109148ItemComponent . propTypes = {
149+ href : PropTypes . string ,
110150 icon : PropTypes . any ,
111151 isDisabled : PropTypes . bool ,
112152 isSelected : PropTypes . bool ,
153+ isExternal : PropTypes . bool ,
113154 label : PropTypes . string ,
114155 name : PropTypes . string ,
115156 type : PropTypes . oneOf ( OPTION_TYPE ) ,
0 commit comments