11import * as React from 'react' ;
22import _ from 'lodash' ;
33import { axe } from 'jest-axe' ;
4- import { useUIDSeed } from '@twilio-paste/uid-library' ;
54import { render , screen , fireEvent } from '@testing-library/react' ;
6- import { Label } from '@twilio-paste/label' ;
7- import { HelpText } from '@twilio-paste/help-text' ;
8- import { InputElement } from '@twilio-paste/input' ;
95import { Button } from '@twilio-paste/button' ;
106import { CloseIcon } from '@twilio-paste/icons/esm/CloseIcon' ;
117import { Box } from '@twilio-paste/box' ;
12- import { useCombobox , Combobox , ComboboxInputWrapper , ComboboxListbox , ComboboxListboxOption } from '../src' ;
8+ import { useCombobox , Combobox } from '../src' ;
139import { ComboboxProps } from '../src/types' ;
1410
1511const items = [ 'Alert' , 'Anchor' , 'Button' , 'Card' , 'Heading' , 'List' , 'Modal' , 'Paragraph' ] ;
@@ -64,54 +60,6 @@ const ComboboxMock: React.FC<{}> = () => {
6460 ) ;
6561} ;
6662
67- const ComboboxPartsMock : React . FC < { } > = ( ) => {
68- const {
69- getComboboxProps,
70- getInputProps,
71- getItemProps,
72- getLabelProps,
73- getMenuProps,
74- getToggleButtonProps,
75- highlightedIndex,
76- isOpen,
77- selectedItem,
78- } = useCombobox ( { items} ) ;
79- const seed = useUIDSeed ( ) ;
80- return (
81- < >
82- < Label htmlFor = "example-textbox" { ...getLabelProps ( ) } id = "example-label" data-testid = "label" >
83- Choose a component:
84- </ Label >
85- < ComboboxInputWrapper { ...getComboboxProps ( { role : 'combobox' } ) } aria-owns = "example-combobox-menu" >
86- < InputElement
87- type = "text"
88- { ...getToggleButtonProps ( { tabIndex : 0 } ) }
89- { ...getInputProps ( ) }
90- value = { selectedItem || '' }
91- // id="example-textbox"
92- data-testid = "textbox"
93- aria-controls = "example-combobox-menu"
94- aria-describedby = "example-helptext"
95- aria-labelledby = "example-label"
96- />
97- </ ComboboxInputWrapper >
98- < ComboboxListbox { ...getMenuProps ( ) } aria-labelledby = "example-label" >
99- { isOpen &&
100- items . map ( ( item , index ) => (
101- < ComboboxListboxOption
102- { ...getItemProps ( { item, index} ) }
103- highlighted = { highlightedIndex === index }
104- key = { seed ( item ) }
105- >
106- { item }
107- </ ComboboxListboxOption >
108- ) ) }
109- </ ComboboxListbox >
110- < HelpText id = "example-helptext" > This is the help text</ HelpText >
111- </ >
112- ) ;
113- } ;
114-
11563const GroupedMockCombobox : React . FC < { groupLabelTemplate ?: ComboboxProps [ 'groupLabelTemplate' ] } > = ( {
11664 groupLabelTemplate,
11765} ) => {
@@ -124,7 +72,7 @@ const GroupedMockCombobox: React.FC<{groupLabelTemplate?: ComboboxProps['groupLa
12472 helpText = "This is group"
12573 groupLabelTemplate = { groupLabelTemplate }
12674 optionTemplate = { ( item : any ) => < div > { item . label } </ div > }
127- itemToString = { item => ( item ? item . label : null ) }
75+ itemToString = { item => ( item && typeof item !== 'string' ? item . label : null ) }
12876 />
12977 ) ;
13078} ;
@@ -191,31 +139,39 @@ describe('Combobox ', () => {
191139 } ) ;
192140
193141 it ( 'should render a combobox with aria attributes' , ( ) => {
194- render ( < ComboboxPartsMock /> ) ;
142+ render ( < ComboboxMock /> ) ;
195143 const renderedCombobox = screen . getByRole ( 'combobox' ) ;
196144 expect ( renderedCombobox . getAttribute ( 'aria-haspopup' ) ) . toEqual ( 'listbox' ) ;
197- expect ( renderedCombobox . getAttribute ( 'aria-owns' ) ) . toEqual ( 'example-combobox-menu' ) ;
198- expect ( renderedCombobox . getAttribute ( 'aria-expanded' ) ) . toEqual ( 'false ' ) ;
145+ expect ( renderedCombobox . getAttribute ( 'aria-owns' ) ) . toEqual ( screen . getByRole ( 'listbox' ) . id ) ;
146+ expect ( renderedCombobox . getAttribute ( 'aria-expanded' ) ) . toEqual ( 'true ' ) ;
199147 } ) ;
200148
201149 it ( 'should render a textbox with aria attributes' , ( ) => {
202- render ( < ComboboxPartsMock /> ) ;
150+ render ( < ComboboxMock /> ) ;
203151 const renderedCombobox = screen . getByRole ( 'textbox' ) ;
204- expect ( renderedCombobox . getAttribute ( 'aria-controls' ) ) . toEqual ( 'example-combobox-menu' ) ;
205- expect ( renderedCombobox . getAttribute ( 'aria-labelledby' ) ) . toEqual ( 'example- label') ;
206- expect ( renderedCombobox . getAttribute ( 'aria-describedby' ) ) . toEqual ( 'example-helptext ' ) ;
152+ expect ( renderedCombobox . getAttribute ( 'aria-controls' ) ) . toEqual ( screen . getByRole ( 'listbox' ) . id ) ;
153+ expect ( renderedCombobox . getAttribute ( 'aria-labelledby' ) ) . toEqual ( document . querySelector ( ' label') . id ) ;
154+ expect ( renderedCombobox . getAttribute ( 'aria-describedby' ) ) . not . toEqual ( '' ) ;
207155 } ) ;
208156
209157 it ( 'should render a list with aria attributes' , ( ) => {
210- render ( < ComboboxPartsMock /> ) ;
158+ render ( < ComboboxMock /> ) ;
211159 const renderedCombobox = screen . getByRole ( 'listbox' ) ;
212- expect ( renderedCombobox . getAttribute ( 'aria-labelledby' ) ) . toEqual ( 'example-label' ) ;
160+ expect ( renderedCombobox . getAttribute ( 'aria-labelledby' ) ) . toEqual ( document . querySelector ( 'label' ) . id ) ;
161+ } ) ;
162+
163+ it ( 'should render a list with unique option ids' , ( ) => {
164+ render ( < ComboboxMock /> ) ;
165+ const renderedOptions = screen . getAllByRole ( 'option' ) ;
166+ const optionIDs = renderedOptions . map ( option => option . id ) ;
167+ const uniqueIDs = _ . uniq ( optionIDs ) ;
168+ expect ( uniqueIDs . length ) . toEqual ( optionIDs . length ) ;
213169 } ) ;
214170
215171 it ( 'should render a label for that matches the input id' , ( ) => {
216- render ( < ComboboxPartsMock /> ) ;
217- const renderedLabel = screen . getByTestId ( 'label' ) ;
218- const renderedTextbox = screen . getByTestId ( 'textbox' ) ;
172+ render ( < ComboboxMock /> ) ;
173+ const renderedLabel = document . querySelector ( 'label' ) ;
174+ const renderedTextbox = screen . getByRole ( 'textbox' ) ;
219175 expect ( renderedLabel . getAttribute ( 'for' ) ) . toEqual ( renderedTextbox . getAttribute ( 'id' ) ) ;
220176 } ) ;
221177 } ) ;
@@ -243,6 +199,14 @@ describe('Combobox ', () => {
243199 expect ( renderedListbox . querySelectorAll ( '[role="listbox"] > [role="option"]' ) . length ) . toEqual ( 1 ) ;
244200 } ) ;
245201
202+ it ( 'should render a listbox with groups of options that contains no duplicate ids' , ( ) => {
203+ render ( < GroupedMockCombobox /> ) ;
204+ const renderedOptions = screen . getAllByRole ( 'option' ) ;
205+ const optionIDs = renderedOptions . map ( option => option . id ) ;
206+ const uniqueIDs = _ . uniq ( optionIDs ) ;
207+ expect ( uniqueIDs . length ) . toEqual ( optionIDs . length ) ;
208+ } ) ;
209+
246210 it ( 'should render a custom group label' , ( ) => {
247211 render ( < GroupedMockCombobox groupLabelTemplate = { groupName => < span > hi { groupName } </ span > } /> ) ;
248212 const renderedGroups = screen . getAllByRole ( 'group' ) ;
0 commit comments