@@ -18,7 +18,7 @@ import React, {
1818} from 'react' ;
1919import PropTypes from 'prop-types' ;
2020import { ThemeContext } from 'styled-components' ;
21- import { IOption , IUseComboboxReturnValue , useCombobox } from '@zendeskgarden/container-combobox' ;
21+ import { IUseComboboxReturnValue , useCombobox } from '@zendeskgarden/container-combobox' ;
2222import { DEFAULT_THEME , useText , useWindow } from '@zendeskgarden/react-theming' ;
2323import { VALIDATION } from '@zendeskgarden/react-forms' ;
2424import ChevronIcon from '@zendeskgarden/svg-icons/src/16/chevron-down-stroke.svg' ;
@@ -31,13 +31,13 @@ import {
3131 StyledInputIcon ,
3232 StyledInput ,
3333 StyledInputGroup ,
34+ StyledTagsButton ,
3435 StyledTrigger ,
3536 StyledValue
3637} from '../../views' ;
37- import { StyledTagsButton } from '../../views/combobox/StyledTagsButton' ;
3838import { Listbox } from './Listbox' ;
39- import { Tag } from './Tag ' ;
40- import { toOptions , toString } from './utils' ;
39+ import { TagGroup } from './TagGroup ' ;
40+ import { toOptions } from './utils' ;
4141
4242const MAX_TAGS = 4 ;
4343
@@ -82,6 +82,7 @@ export const Combobox = forwardRef<HTMLDivElement, IComboboxProps>(
8282 ) => {
8383 const { hasHint, hasMessage, labelProps, setLabelProps } = useFieldContext ( ) ;
8484 const [ isLabelHovered , setIsLabelHovered ] = useState ( false ) ;
85+ const [ isTagGroupExpanded , setIsTagGroupExpanded ] = useState ( false ) ;
8586 const [ optionTagProps , setOptionTagProps ] = useState < Record < string , IOptionProps [ 'tagProps' ] > > (
8687 { }
8788 ) ;
@@ -99,7 +100,6 @@ export const Combobox = forwardRef<HTMLDivElement, IComboboxProps>(
99100 const triggerRef = useRef < HTMLDivElement > ( null ) ;
100101 const inputRef = useRef < HTMLInputElement > ( null ) ;
101102 const listboxRef = useRef < HTMLUListElement > ( null ) ;
102- const tagsButtonRef = useRef < HTMLButtonElement > ( null ) ;
103103 /* istanbul ignore next */
104104 const theme = useContext ( ThemeContext ) || DEFAULT_THEME ;
105105 const environment = useWindow ( theme ) ;
@@ -178,10 +178,18 @@ export const Combobox = forwardRef<HTMLDivElement, IComboboxProps>(
178178 ...( getTriggerProps ( {
179179 onFocus : ( ) => {
180180 hasFocus . current = true ;
181+
182+ if ( isMultiselectable ) {
183+ setIsTagGroupExpanded ( true ) ;
184+ }
181185 } ,
182186 onBlur : event => {
183187 if ( event . relatedTarget === null || ! triggerRef . current ?. contains ( event . relatedTarget ) ) {
184188 hasFocus . current = false ;
189+
190+ if ( isMultiselectable ) {
191+ setIsTagGroupExpanded ( false ) ;
192+ }
185193 }
186194 }
187195 } ) as HTMLAttributes < HTMLDivElement > )
@@ -191,6 +199,7 @@ export const Combobox = forwardRef<HTMLDivElement, IComboboxProps>(
191199 hidden : ! ( isEditable && hasFocus . current ) ,
192200 isBare,
193201 isCompact,
202+ isEditable,
194203 isMultiselectable,
195204 placeholder,
196205 ...( getInputProps ( {
@@ -215,47 +224,14 @@ export const Combobox = forwardRef<HTMLDivElement, IComboboxProps>(
215224 return ( ) => labelProps && setLabelProps ( undefined ) ;
216225 } , [ getLabelProps , labelProps , setLabelProps ] ) ;
217226
218- const Tags = ( { selectedOptions } : { selectedOptions : IOption [ ] } ) => {
219- const value = selectedOptions . length - maxTags ;
220-
221- return (
222- < >
223- { selectedOptions . map ( ( option , index ) => {
224- const key = toString ( option ) ;
225- const disabled = isDisabled || option . disabled ;
226- const hidden = ! hasFocus . current && index >= maxTags ;
227-
228- return (
229- < Tag
230- key = { key }
231- hidden = { hidden }
232- option = { { ...option , disabled } }
233- tooltipZIndex = { listboxZIndex ? listboxZIndex + 1 : undefined }
234- { ...optionTagProps [ key ] }
235- />
236- ) ;
237- } ) }
238- { ! hasFocus . current && selectedOptions . length > maxTags && (
239- < StyledTagsButton
240- disabled = { isDisabled }
241- isCompact = { isCompact }
242- onClick = { ( ) => isEditable && inputRef . current ?. focus ( ) }
243- tabIndex = { - 1 }
244- type = "button"
245- ref = { tagsButtonRef }
246- >
247- { renderExpandTags
248- ? renderExpandTags ( value )
249- : expandTags ?. replace ( '{{value}}' , value . toString ( ) ) }
250- </ StyledTagsButton >
251- ) }
252- </ >
253- ) ;
254- } ;
255-
256227 return (
257228 < ComboboxContext . Provider value = { contextValue } >
258- < StyledCombobox isCompact = { isCompact } { ...props } ref = { ref } >
229+ < StyledCombobox
230+ isCompact = { isCompact }
231+ tabIndex = { - 1 } // HACK: otherwise screenreaders can't read the label
232+ { ...props }
233+ ref = { ref }
234+ >
259235 < StyledTrigger { ...triggerProps } >
260236 < StyledContainer >
261237 { startIcon && (
@@ -265,7 +241,37 @@ export const Combobox = forwardRef<HTMLDivElement, IComboboxProps>(
265241 ) }
266242 < StyledInputGroup >
267243 { isMultiselectable && Array . isArray ( selection ) && (
268- < Tags selectedOptions = { selection } />
244+ < TagGroup
245+ isDisabled = { isDisabled }
246+ isExpanded = { isTagGroupExpanded }
247+ maxTags = { maxTags }
248+ optionTagProps = { optionTagProps }
249+ selection = { selection }
250+ >
251+ { selection . length > maxTags && (
252+ < StyledTagsButton
253+ disabled = { isDisabled }
254+ hidden = { isTagGroupExpanded }
255+ isCompact = { isCompact }
256+ onClick = { event => {
257+ if ( isEditable ) {
258+ event . stopPropagation ( ) ;
259+ inputRef . current ?. focus ( ) ;
260+ }
261+ } }
262+ tabIndex = { - 1 }
263+ type = "button"
264+ >
265+ { ( ( ) => {
266+ const value = selection . length - maxTags ;
267+
268+ return renderExpandTags
269+ ? renderExpandTags ( value )
270+ : expandTags ?. replace ( '{{value}}' , value . toString ( ) ) ;
271+ } ) ( ) }
272+ </ StyledTagsButton >
273+ ) }
274+ </ TagGroup >
269275 ) }
270276 { ! ( isEditable && hasFocus . current ) && (
271277 < StyledValue
0 commit comments